summaryrefslogtreecommitdiff
path: root/LedgerSMB
diff options
context:
space:
mode:
authorchristopherm <christopherm@4979c152-3d1c-0410-bac9-87ea11338e46>2006-09-01 01:16:38 +0000
committerchristopherm <christopherm@4979c152-3d1c-0410-bac9-87ea11338e46>2006-09-01 01:16:38 +0000
commitac5b087ea2d9ba7428d367aaeb288534158fee9a (patch)
tree2dbe0bdea0b653a215ba9ddfdf627cb57855050d /LedgerSMB
Initial Import
git-svn-id: https://ledger-smb.svn.sourceforge.net/svnroot/ledger-smb/ledger-smb@1 4979c152-3d1c-0410-bac9-87ea11338e46
Diffstat (limited to 'LedgerSMB')
-rwxr-xr-xLedgerSMB/AA.pm937
-rwxr-xr-xLedgerSMB/AM.pm1832
-rwxr-xr-xLedgerSMB/BP.pm326
-rwxr-xr-xLedgerSMB/CA.pm378
-rwxr-xr-xLedgerSMB/CP.pm684
-rwxr-xr-xLedgerSMB/CT.pm1080
-rwxr-xr-xLedgerSMB/Form.pm2942
-rwxr-xr-xLedgerSMB/GL.pm526
-rwxr-xr-xLedgerSMB/HR.pm555
-rwxr-xr-xLedgerSMB/IC.pm1714
-rwxr-xr-xLedgerSMB/IR.pm1123
-rwxr-xr-xLedgerSMB/IS.pm1684
-rwxr-xr-xLedgerSMB/Inifile.pm74
-rwxr-xr-xLedgerSMB/JC.pm582
-rwxr-xr-xLedgerSMB/Mailer.pm149
-rwxr-xr-xLedgerSMB/Menu.pm91
-rwxr-xr-xLedgerSMB/Num2text.pm149
-rwxr-xr-xLedgerSMB/OE.pm2238
-rwxr-xr-xLedgerSMB/OP.pm101
-rwxr-xr-xLedgerSMB/PE.pm1499
-rwxr-xr-xLedgerSMB/RC.pm391
-rwxr-xr-xLedgerSMB/RP.pm2103
-rwxr-xr-xLedgerSMB/Session.pm143
-rwxr-xr-xLedgerSMB/User.pm927
24 files changed, 22228 insertions, 0 deletions
diff --git a/LedgerSMB/AA.pm b/LedgerSMB/AA.pm
new file mode 100755
index 00000000..92816650
--- /dev/null
+++ b/LedgerSMB/AA.pm
@@ -0,0 +1,937 @@
+#=====================================================================
+# LedgerSMB
+# Small Medium Business Accounting software
+#
+# See COPYRIGHT file for copyright information
+#======================================================================
+#
+# This file has undergone whitespace cleanup.
+#
+#======================================================================
+#
+# AR/AP backend routines
+# common routines
+#
+#======================================================================
+
+package AA;
+
+
+sub post_transaction {
+
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect_noauto($myconfig);
+
+ my $query;
+ my $sth;
+
+ my $null;
+ ($null, $form->{department_id}) = split(/--/, $form->{department});
+ $form->{department_id} *= 1;
+
+ my $ml = 1;
+ my $table = 'ar';
+ my $buysell = 'buy';
+ my $ARAP = 'AR';
+ my $invnumber = "sinumber";
+ my $keepcleared;
+
+ if ($form->{vc} eq 'vendor') {
+ $table = 'ap';
+ $buysell = 'sell';
+ $ARAP = 'AP';
+ $ml = -1;
+ $invnumber = "vinumber";
+ }
+
+ if ($form->{currency} eq $form->{defaultcurrency}) {
+ $form->{exchangerate} = 1;
+ } else {
+ $exchangerate = $form->check_exchangerate($myconfig, $form->{currency}, $form->{transdate}, $buysell);
+
+ $form->{exchangerate} = ($exchangerate) ? $exchangerate : $form->parse_amount($myconfig, $form->{exchangerate});
+ }
+
+ my @taxaccounts = split / /, $form->{taxaccounts};
+ my $tax = 0;
+ my $fxtax = 0;
+ my $amount;
+ my $diff;
+
+ my %tax = ();
+ my $accno;
+
+ # add taxes
+ foreach $accno (@taxaccounts) {
+ $fxtax += $tax{fxamount}{$accno} = $form->parse_amount($myconfig, $form->{"tax_$accno"});
+ $tax += $tax{fxamount}{$accno};
+
+ push @{ $form->{acc_trans}{taxes} }, {
+ accno => $accno,
+ amount => $tax{fxamount}{$accno},
+ project_id => 'NULL',
+ fx_transaction => 0 };
+
+ $amount = $tax{fxamount}{$accno} * $form->{exchangerate};
+ $tax{amount}{$accno} = $form->round_amount($amount - $diff, 2);
+ $diff = $tax{amount}{$accno} - ($amount - $diff);
+ $amount = $tax{amount}{$accno} - $tax{fxamount}{$accno};
+ $tax += $amount;
+
+ if ($form->{currency} ne $form->{defaultcurrency}) {
+ push @{ $form->{acc_trans}{taxes} }, {
+ accno => $accno,
+ amount => $amount,
+ project_id => 'NULL',
+ fx_transaction => 1 };
+ }
+
+ }
+
+ my %amount = ();
+ my $fxinvamount = 0;
+ for (1 .. $form->{rowcount}) {
+ $fxinvamount += $amount{fxamount}{$_} = $form->parse_amount($myconfig, $form->{"amount_$_"})
+ }
+
+ $form->{taxincluded} *= 1;
+
+ my $i;
+ my $project_id;
+ my $cleared = 0;
+
+ $diff = 0;
+ # deduct tax from amounts if tax included
+ for $i (1 .. $form->{rowcount}) {
+
+ if ($amount{fxamount}{$i}) {
+
+ if ($form->{taxincluded}) {
+ $amount = ($fxinvamount) ? $fxtax * $amount{fxamount}{$i} / $fxinvamount : 0;
+ $amount{fxamount}{$i} -= $amount;
+ }
+
+ # multiply by exchangerate
+ $amount = $amount{fxamount}{$i} * $form->{exchangerate};
+ $amount{amount}{$i} = $form->round_amount($amount - $diff, 2);
+ $diff = $amount{amount}{$i} - ($amount - $diff);
+
+ ($null, $project_id) = split /--/, $form->{"projectnumber_$i"};
+ $project_id ||= 'NULL';
+ ($accno) = split /--/, $form->{"${ARAP}_amount_$i"};
+
+ if ($keepcleared) {
+ $cleared = ($form->{"cleared_$i"}) ? 1 : 0;
+ }
+
+ push @{ $form->{acc_trans}{lineitems} }, {
+ accno => $accno,
+ amount => $amount{fxamount}{$i},
+ project_id => $project_id,
+ description => $form->{"description_$i"},
+ cleared => $cleared,
+ fx_transaction => 0 };
+
+ if ($form->{currency} ne $form->{defaultcurrency}) {
+ $amount = $amount{amount}{$i} - $amount{fxamount}{$i};
+ push @{ $form->{acc_trans}{lineitems} }, {
+ accno => $accno,
+ amount => $amount,
+ project_id => $project_id,
+ description => $form->{"description_$i"},
+ cleared => $cleared,
+ fx_transaction => 1 };
+ }
+ }
+ }
+
+
+ my $invnetamount = 0;
+ for (@{ $form->{acc_trans}{lineitems} }) { $invnetamount += $_->{amount} }
+ my $invamount = $invnetamount + $tax;
+
+ # adjust paidaccounts if there is no date in the last row
+ $form->{paidaccounts}-- unless ($form->{"datepaid_$form->{paidaccounts}"});
+
+ my $paid = 0;
+ my $fxamount;
+
+ $diff = 0;
+ # add payments
+ for $i (1 .. $form->{paidaccounts}) {
+ $fxamount = $form->parse_amount($myconfig, $form->{"paid_$i"});
+
+ if ($fxamount) {
+ $paid += $fxamount;
+
+ $paidamount = $fxamount * $form->{exchangerate};
+
+ $amount = $form->round_amount($paidamount - $diff, 2);
+ $diff = $amount - ($paidamount - $diff);
+
+ $form->{datepaid} = $form->{"datepaid_$i"};
+
+ $paid{fxamount}{$i} = $fxamount;
+ $paid{amount}{$i} = $amount;
+ }
+ }
+
+ $fxinvamount += $fxtax unless $form->{taxincluded};
+ $fxinvamount = $form->round_amount($fxinvamount, 2);
+ $invamount = $form->round_amount($invamount, 2);
+ $paid = $form->round_amount($paid, 2);
+
+ $paid = ($fxinvamount == $paid) ? $invamount : $form->round_amount($paid * $form->{exchangerate}, 2);
+
+ $query = q|SELECT fxgain_accno_id, fxloss_accno_id
+ FROM defaults|;
+
+ my ($fxgain_accno_id, $fxloss_accno_id) = $dbh->selectrow_array($query);
+
+ ($null, $form->{employee_id}) = split /--/, $form->{employee};
+ unless ($form->{employee_id}) {
+ ($form->{employee}, $form->{employee_id}) = $form->get_employee($dbh);
+ }
+
+ # check if id really exists
+ if ($form->{id}) {
+ $keepcleared = 1;
+ $query = qq|SELECT id FROM $table
+ WHERE id = $form->{id}|;
+
+ if ($dbh->selectrow_array($query)) {
+ # delete detail records
+ $query = qq|DELETE FROM acc_trans
+ WHERE trans_id = $form->{id}|;
+
+ $dbh->do($query) || $form->dberror($query);
+ }
+ } else {
+
+ my $uid = localtime;
+ $uid .= "$$";
+
+ $query = qq|INSERT INTO $table (invnumber)
+ VALUES ('$uid')|;
+
+ $dbh->do($query) || $form->dberror($query);
+
+ $query = qq|SELECT id FROM $table
+ WHERE invnumber = '$uid'|;
+
+ ($form->{id}) = $dbh->selectrow_array($query);
+ }
+
+
+ # record last payment date in ar/ap table
+ $form->{datepaid} = $form->{transdate} unless $form->{datepaid};
+ my $datepaid = ($paid) ? qq|'$form->{datepaid}'| : 'NULL';
+
+ $form->{invnumber} = $form->update_defaults($myconfig, $invnumber) unless $form->{invnumber};
+
+ $query = qq|UPDATE $table SET invnumber = |.$dbh->quote($form->{invnumber}).qq|,
+ ordnumber = |.$dbh->quote($form->{ordnumber}).qq|,
+ transdate = '$form->{transdate}',
+ $form->{vc}_id = $form->{"$form->{vc}_id"},
+ taxincluded = '$form->{taxincluded}',
+ amount = $invamount,
+ duedate = '$form->{duedate}',
+ paid = $paid,
+ datepaid = $datepaid,
+ netamount = $invnetamount,
+ curr = '$form->{currency}',
+ notes = |.$dbh->quote($form->{notes}).qq|,
+ department_id = $form->{department_id},
+ employee_id = $form->{employee_id},
+ ponumber = |.$dbh->quote($form->{ponumber}).qq|
+ WHERE id = $form->{id}|;
+
+ $dbh->do($query) || $form->dberror($query);
+
+ # update exchangerate
+ my $buy = $form->{exchangerate};
+ my $sell = 0;
+ if ($form->{vc} eq 'vendor') {
+ $buy = 0;
+ $sell = $form->{exchangerate};
+ }
+
+ if (($form->{currency} ne $form->{defaultcurrency}) && !$exchangerate) {
+ $form->update_exchangerate($dbh, $form->{currency}, $form->{transdate}, $buy, $sell);
+ }
+
+ my $ref;
+
+ # add individual transactions
+ foreach $ref (@{ $form->{acc_trans}{lineitems} }) {
+
+ # insert detail records in acc_trans
+ if ($ref->{amount}) {
+ $query = qq|INSERT INTO acc_trans (trans_id, chart_id, amount, transdate,
+ project_id, memo, fx_transaction, cleared)
+ VALUES ($form->{id}, (SELECT id FROM chart
+ WHERE accno = '$ref->{accno}'),
+ $ref->{amount} * $ml, '$form->{transdate}',
+ $ref->{project_id}, |.$dbh->quote($ref->{description}).qq|,
+ '$ref->{fx_transaction}', '$ref->{cleared}')|;
+
+ $dbh->do($query) || $form->dberror($query);
+ }
+ }
+
+ # save taxes
+ foreach $ref (@{ $form->{acc_trans}{taxes} }) {
+ if ($ref->{amount}) {
+ $query = qq|INSERT INTO acc_trans (trans_id, chart_id, amount,
+ transdate, fx_transaction)
+ VALUES ($form->{id},
+ (SELECT id FROM chart
+ WHERE accno = '$ref->{accno}'),
+ $ref->{amount} * $ml, '$form->{transdate}',
+ '$ref->{fx_transaction}')|;
+
+ $dbh->do($query) || $form->dberror($query);
+ }
+ }
+
+
+ my $arap;
+
+ # record ar/ap
+ if (($arap = $invamount)) {
+ ($accno) = split /--/, $form->{$ARAP};
+
+ $query = qq|INSERT INTO acc_trans (trans_id, chart_id, amount, transdate)
+ VALUES ($form->{id},
+ (SELECT id FROM chart
+ WHERE accno = '$accno'),
+ $invamount * -1 * $ml, '$form->{transdate}')|;
+
+ $dbh->do($query) || $form->dberror($query);
+ }
+
+ # if there is no amount force ar/ap
+ if ($fxinvamount == 0) {
+ $arap = 1;
+ }
+
+
+ my $exchangerate;
+
+ # add paid transactions
+ for $i (1 .. $form->{paidaccounts}) {
+
+ if ($paid{fxamount}{$i}) {
+
+ ($accno) = split(/--/, $form->{"${ARAP}_paid_$i"});
+ $form->{"datepaid_$i"} = $form->{transdate} unless ($form->{"datepaid_$i"});
+
+ $exchangerate = 0;
+
+ if ($form->{currency} eq $form->{defaultcurrency}) {
+ $form->{"exchangerate_$i"} = 1;
+ } else {
+ $exchangerate = $form->check_exchangerate($myconfig, $form->{currency}, $form->{"datepaid_$i"}, $buysell);
+
+ $form->{"exchangerate_$i"} = ($exchangerate) ? $exchangerate : $form->parse_amount($myconfig, $form->{"exchangerate_$i"});
+ }
+
+ # if there is no amount
+ if ($fxinvamount == 0) {
+ $form->{exchangerate} = $form->{"exchangerate_$i"};
+ }
+
+ # ar/ap amount
+ if ($arap) {
+ ($accno) = split /--/, $form->{$ARAP};
+
+ # add ar/ap
+ $query = qq|INSERT INTO acc_trans (trans_id, chart_id, amount,transdate)
+ VALUES ($form->{id}, (SELECT id FROM chart
+ WHERE accno = '$accno'),
+ $paid{amount}{$i} * $ml, '$form->{"datepaid_$i"}')|;
+
+ $dbh->do($query) || $form->dberror($query);
+ }
+
+ $arap = $paid{amount}{$i};
+
+
+ # add payment
+ if ($paid{fxamount}{$i}) {
+
+ ($accno) = split /--/, $form->{"${ARAP}_paid_$i"};
+
+ my $cleared = ($form->{"cleared_$i"}) ? 1 : 0;
+
+ $amount = $paid{fxamount}{$i};
+ $query = qq|INSERT INTO acc_trans (trans_id, chart_id, amount,
+ transdate, source, memo, cleared)
+ VALUES ($form->{id}, (SELECT id FROM chart
+ WHERE accno = '$accno'),
+ $amount * -1 * $ml, '$form->{"datepaid_$i"}', |
+ .$dbh->quote($form->{"source_$i"}).qq|, |
+ .$dbh->quote($form->{"memo_$i"}).qq|, '$cleared')|;
+
+ $dbh->do($query) || $form->dberror($query);
+
+ if ($form->{currency} ne $form->{defaultcurrency}) {
+
+ # exchangerate gain/loss
+ $amount = ($form->round_amount($paid{fxamount}{$i} * $form->{exchangerate},2) - $form->round_amount($paid{fxamount}{$i} * $form->{"exchangerate_$i"},2)) * -1;
+
+ if ($amount) {
+
+ my $accno_id = (($amount * $ml) > 0) ? $fxgain_accno_id : $fxloss_accno_id;
+
+ $query = qq|INSERT INTO acc_trans (trans_id, chart_id, amount,
+ transdate, fx_transaction, cleared)
+ VALUES ($form->{id}, $accno_id,
+ $amount * $ml, '$form->{"datepaid_$i"}', '1',
+ '$cleared')|;
+
+ $dbh->do($query) || $form->dberror($query);
+ }
+
+ # exchangerate difference
+ $amount = $paid{amount}{$i} - $paid{fxamount}{$i} + $amount;
+
+ $query = qq|INSERT INTO acc_trans (trans_id, chart_id, amount,
+ transdate, fx_transaction, cleared, source)
+ VALUES ($form->{id}, (SELECT id FROM chart
+ WHERE accno = '$accno'),
+ $amount * -1 * $ml, '$form->{"datepaid_$i"}', '1',
+ '$cleared', |
+ .$dbh->quote($form->{"source_$i"}).qq|)|;
+
+ $dbh->do($query) || $form->dberror($query);
+
+ }
+
+ # update exchangerate record
+ $buy = $form->{"exchangerate_$i"};
+ $sell = 0;
+
+ if ($form->{vc} eq 'vendor') {
+ $buy = 0;
+ $sell = $form->{"exchangerate_$i"};
+ }
+
+ if (($form->{currency} ne $form->{defaultcurrency}) && !$exchangerate) {
+ $form->update_exchangerate($dbh, $form->{currency}, $form->{"datepaid_$i"}, $buy, $sell);
+ }
+ }
+ }
+ }
+
+ # save printed and queued
+ $form->save_status($dbh);
+
+ my %audittrail = ( tablename => $table,
+ reference => $form->{invnumber},
+ formname => 'transaction',
+ action => 'posted',
+ id => $form->{id} );
+
+ $form->audittrail($dbh, "", \%audittrail);
+
+ $form->save_recurring($dbh, $myconfig);
+
+ my $rc = $dbh->commit;
+
+ $dbh->disconnect;
+
+ $rc;
+
+}
+
+
+sub delete_transaction {
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database, turn AutoCommit off
+ my $dbh = $form->dbconnect_noauto($myconfig);
+
+ my $table = ($form->{vc} eq 'customer') ? 'ar' : 'ap';
+
+ my %audittrail = ( tablename => $table,
+ reference => $form->{invnumber},
+ formname => 'transaction',
+ action => 'deleted',
+ id => $form->{id} );
+
+ $form->audittrail($dbh, "", \%audittrail);
+
+ my $query = qq|DELETE FROM $table WHERE id = $form->{id}|;
+ $dbh->do($query) || $form->dberror($query);
+
+ $query = qq|DELETE FROM acc_trans WHERE trans_id = $form->{id}|;
+ $dbh->do($query) || $form->dberror($query);
+
+ # get spool files
+ $query = qq|SELECT spoolfile
+ FROM status
+ WHERE trans_id = $form->{id}
+ AND spoolfile IS NOT NULL|;
+
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ my $spoolfile;
+ my @spoolfiles = ();
+
+ while (($spoolfile) = $sth->fetchrow_array) {
+ push @spoolfiles, $spoolfile;
+ }
+
+ $sth->finish;
+
+ $query = qq|DELETE FROM status WHERE trans_id = $form->{id}|;
+ $dbh->do($query) || $form->dberror($query);
+
+ # commit
+ my $rc = $dbh->commit;
+ $dbh->disconnect;
+
+ if ($rc) {
+ foreach $spoolfile (@spoolfiles) {
+ unlink "$spool/$spoolfile" if $spoolfile;
+ }
+ }
+
+ $rc;
+}
+
+
+
+sub transactions {
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+ my $null;
+ my $var;
+ my $paid = "a.paid";
+ my $ml = 1;
+ my $ARAP = 'AR';
+ my $table = 'ar';
+ my $buysell = 'buy';
+ my $acc_trans_join;
+ my $acc_trans_flds;
+
+ if ($form->{vc} eq 'vendor') {
+ $ml = -1;
+ $ARAP = 'AP';
+ $table = 'ap';
+ $buysell = 'sell';
+ }
+
+ ($form->{transdatefrom}, $form->{transdateto}) = $form->from_to($form->{year}, $form->{month}, $form->{interval}) if $form->{year} && $form->{month};
+
+ if ($form->{outstanding}) {
+ $paid = qq|SELECT SUM(ac.amount) * -1 * $ml
+ FROM acc_trans ac
+ JOIN chart c ON (c.id = ac.chart_id)
+ WHERE ac.trans_id = a.id
+ AND (c.link LIKE '%${ARAP}_paid%' OR c.link = '')|;
+ $paid .= qq|
+ AND ac.transdate <= '$form->{transdateto}'| if $form->{transdateto};
+ $form->{summary} = 1;
+ }
+
+
+ if (!$form->{summary}) {
+ $acc_trans_flds = qq|, c.accno, ac.source,
+ pr.projectnumber, ac.memo AS description,
+ ac.amount AS linetotal,
+ i.description AS linedescription|;
+
+ $acc_trans_join = qq| JOIN acc_trans ac ON (a.id = ac.trans_id)
+ JOIN chart c ON (c.id = ac.chart_id)
+ LEFT JOIN project pr ON (pr.id = ac.project_id)
+ LEFT JOIN invoice i ON (i.id = ac.invoice_id)|;
+ }
+
+ my $query = qq|SELECT a.id, a.invnumber, a.ordnumber, a.transdate,
+ a.duedate, a.netamount, a.amount, ($paid) AS paid,
+ a.invoice, a.datepaid, a.terms, a.notes,
+ a.shipvia, a.shippingpoint, e.name AS employee, vc.name,
+ a.$form->{vc}_id, a.till, m.name AS manager, a.curr,
+ ex.$buysell AS exchangerate, d.description AS department,
+ a.ponumber $acc_trans_flds
+ FROM $table a
+ JOIN $form->{vc} vc ON (a.$form->{vc}_id = vc.id)
+ LEFT JOIN employee e ON (a.employee_id = e.id)
+ LEFT JOIN employee m ON (e.managerid = m.id)
+ LEFT JOIN exchangerate ex ON (ex.curr = a.curr
+ AND ex.transdate = a.transdate)
+ LEFT JOIN department d ON (a.department_id = d.id)
+ $acc_trans_join|;
+
+ my %ordinal = ( id => 1,
+ invnumber => 2,
+ ordnumber => 3,
+ transdate => 4,
+ duedate => 5,
+ datepaid => 10,
+ shipvia => 13,
+ shippingpoint => 14,
+ employee => 15,
+ name => 16,
+ manager => 19,
+ curr => 20,
+ department => 22,
+ ponumber => 23,
+ accno => 24,
+ source => 25,
+ project => 26,
+ description => 27);
+
+
+ my @a = (transdate, invnumber, name);
+ push @a, "employee" if $form->{l_employee};
+ push @a, "manager" if $form->{l_manager};
+ my $sortorder = $form->sort_order(\@a, \%ordinal);
+
+ my $where = "1 = 1";
+ if ($form->{"$form->{vc}_id"}) {
+ $where .= qq| AND a.$form->{vc}_id = $form->{"$form->{vc}_id"}|;
+ } else {
+ if ($form->{$form->{vc}}) {
+ $var = $form->like(lc $form->{$form->{vc}});
+ $where .= " AND lower(vc.name) LIKE '$var'";
+ }
+ }
+
+ for (qw(department employee)) {
+ if ($form->{$_}) {
+ ($null, $var) = split /--/, $form->{$_};
+ $where .= " AND a.${_}_id = $var";
+ }
+ }
+
+ for (qw(invnumber ordnumber)) {
+ if ($form->{$_}) {
+ $var = $form->like(lc $form->{$_});
+ $where .= " AND lower(a.$_) LIKE '$var'";
+ $form->{open} = $form->{closed} = 0;
+ }
+ }
+
+ for (qw(ponumber shipvia notes)) {
+ if ($form->{$_}) {
+ $var = $form->like(lc $form->{$_});
+ $where .= " AND lower(a.$_) LIKE '$var'";
+ }
+ }
+
+ if ($form->{description}) {
+ if ($acc_trans_flds) {
+ $var = $form->like(lc $form->{description});
+ $where .= " AND lower(ac.memo) LIKE '$var'
+ OR lower(i.description) LIKE '$var'";
+ } else {
+ $where .= " AND a.id = 0";
+ }
+ }
+
+ if ($form->{source}) {
+ if ($acc_trans_flds) {
+ $var = $form->like(lc $form->{source});
+ $where .= " AND lower(ac.source) LIKE '$var'";
+ } else {
+ $where .= " AND a.id = 0";
+ }
+ }
+
+
+ $where .= " AND a.transdate >= '$form->{transdatefrom}'" if $form->{transdatefrom};
+ $where .= " AND a.transdate <= '$form->{transdateto}'" if $form->{transdateto};
+
+ if ($form->{open} || $form->{closed}) {
+ unless ($form->{open} && $form->{closed}) {
+ $where .= " AND a.amount != a.paid" if ($form->{open});
+ $where .= " AND a.amount = a.paid" if ($form->{closed});
+ }
+ }
+
+ if ($form->{till} ne "") {
+ $where .= " AND a.invoice = '1'
+ AND a.till IS NOT NULL";
+
+ if ($myconfig->{role} eq 'user') {
+ $where .= " AND e.login = '$form->{login}'";
+ }
+ }
+
+ if ($form->{$ARAP}) {
+ my ($accno) = split /--/, $form->{$ARAP};
+
+ $where .= qq|AND a.id IN (SELECT ac.trans_id
+ FROM acc_trans ac
+ JOIN chart c ON (c.id = ac.chart_id)
+ WHERE a.id = ac.trans_id
+ AND c.accno = '$accno')|;
+ }
+
+ if ($form->{description}) {
+ $var = $form->like(lc $form->{description});
+ $where .= qq| AND (a.id IN (SELECT DISTINCT trans_id
+ FROM acc_trans
+ WHERE lower(memo) LIKE '$var')
+ OR a.id IN (SELECT DISTINCT trans_id
+ FROM invoice
+ WHERE lower(description) LIKE '$var'))|;
+ }
+
+ $query .= "WHERE $where
+ ORDER BY $sortorder";
+
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
+ $ref->{exchangerate} = 1 unless $ref->{exchangerate};
+
+ if ($ref->{linetotal} <= 0) {
+ $ref->{debit} = $ref->{linetotal} * -1;
+ $ref->{credit} = 0;
+ } else {
+ $ref->{debit} = 0;
+ $ref->{credit} = $ref->{linetotal};
+ }
+
+ if ($ref->{invoice}) {
+ $ref->{description} ||= $ref->{linedescription};
+ }
+
+ if ($form->{outstanding}) {
+ next if $form->round_amount($ref->{amount}, 2) == $form->round_amount($ref->{paid}, 2);
+ }
+
+ push @{ $form->{transactions} }, $ref;
+ }
+
+ $sth->finish;
+ $dbh->disconnect;
+}
+
+
+# this is used in IS, IR to retrieve the name
+sub get_name {
+
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ my $dateformat = $myconfig->{dateformat};
+
+ if ($myconfig->{dateformat} !~ /^y/) {
+ my @a = split /\W/, $form->{transdate};
+ $dateformat .= "yy" if (length $a[2] > 2);
+ }
+
+ if ($form->{transdate} !~ /\W/) {
+ $dateformat = 'yyyymmdd';
+ }
+
+ my $duedate;
+
+ if ($myconfig->{dbdriver} eq 'DB2') {
+ $duedate = ($form->{transdate}) ? "date('$form->{transdate}') + c.terms DAYS" : "current_date + c.terms DAYS";
+ } else {
+ $duedate = ($form->{transdate}) ? "to_date('$form->{transdate}', '$dateformat') + c.terms" : "current_date + c.terms";
+ }
+
+ $form->{"$form->{vc}_id"} *= 1;
+ # get customer/vendor
+ my $query = qq|SELECT c.name AS $form->{vc}, c.discount, c.creditlimit, c.terms,
+ c.email, c.cc, c.bcc, c.taxincluded,
+ c.address1, c.address2, c.city, c.state,
+ c.zipcode, c.country, c.curr AS currency, c.language_code,
+ $duedate AS duedate, c.notes AS intnotes,
+ b.discount AS tradediscount, b.description AS business,
+ e.name AS employee, e.id AS employee_id
+ FROM $form->{vc} c
+ LEFT JOIN business b ON (b.id = c.business_id)
+ LEFT JOIN employee e ON (e.id = c.employee_id)
+ WHERE c.id = $form->{"$form->{vc}_id"}|;
+
+ my $sth = $dbh->prepare($query);
+
+ $sth->execute || $form->dberror($query);
+
+ $ref = $sth->fetchrow_hashref(NAME_lc);
+
+ if ($form->{id}) {
+ for (qw(currency employee employee_id intnotes)) { delete $ref->{$_} }
+ }
+
+ for (keys %$ref) { $form->{$_} = $ref->{$_} }
+ $sth->finish;
+
+ my $buysell = ($form->{vc} eq 'customer') ? "buy" : "sell";
+
+ # if no currency use defaultcurrency
+ $form->{currency} = ($form->{currency}) ? $form->{currency} : $form->{defaultcurrency};
+ $form->{exchangerate} = 0 if $form->{currency} eq $form->{defaultcurrency};
+
+ if ($form->{transdate} && ($form->{currency} ne $form->{defaultcurrency})) {
+ $form->{exchangerate} = $form->get_exchangerate($dbh, $form->{currency}, $form->{transdate}, $buysell);
+ }
+
+ $form->{forex} = $form->{exchangerate};
+
+ # if no employee, default to login
+ ($form->{employee}, $form->{employee_id}) = $form->get_employee($dbh) unless $form->{employee_id};
+
+ my $arap = ($form->{vc} eq 'customer') ? 'ar' : 'ap';
+ my $ARAP = uc $arap;
+
+ $form->{creditremaining} = $form->{creditlimit};
+ $query = qq|SELECT SUM(amount - paid)
+ FROM $arap
+ WHERE $form->{vc}_id = $form->{"$form->{vc}_id"}|;
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ ($form->{creditremaining}) -= $sth->fetchrow_array;
+
+ $sth->finish;
+
+ $query = qq|SELECT o.amount, (SELECT e.$buysell FROM exchangerate e
+ WHERE e.curr = o.curr
+ AND e.transdate = o.transdate)
+ FROM oe o
+ WHERE o.$form->{vc}_id = $form->{"$form->{vc}_id"}
+ AND o.quotation = '0'
+ AND o.closed = '0'|;
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while (my ($amount, $exch) = $sth->fetchrow_array) {
+ $exch = 1 unless $exch;
+ $form->{creditremaining} -= $amount * $exch;
+ }
+
+ $sth->finish;
+
+
+ # get shipto if we did not converted an order or invoice
+ if (!$form->{shipto}) {
+
+ for (qw(shiptoname shiptoaddress1 shiptoaddress2 shiptocity
+ shiptostate shiptozipcode shiptocountry shiptocontact
+ shiptophone shiptofax shiptoemail)) {
+ delete $form->{$_}
+ }
+
+ ## needs fixing (SELECT *)
+ $query = qq|SELECT *
+ FROM shipto
+ WHERE trans_id = $form->{"$form->{vc}_id"}|;
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ $ref = $sth->fetchrow_hashref(NAME_lc);
+ for (keys %$ref) { $form->{$_} = $ref->{$_} }
+ $sth->finish;
+ }
+
+ # get taxes
+ $query = qq|SELECT c.accno
+ FROM chart c
+ JOIN $form->{vc}tax ct ON (ct.chart_id = c.id)
+ WHERE ct.$form->{vc}_id = $form->{"$form->{vc}_id"}|;
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ my %tax;
+
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+ $tax{$ref->{accno}} = 1;
+ }
+
+ $sth->finish;
+
+ my $where = qq|AND (t.validto >= '$form->{transdate}' OR t.validto IS NULL)| if $form->{transdate};
+
+ # get tax rates and description
+ $query = qq|SELECT c.accno, c.description, t.rate, t.taxnumber
+ FROM chart c
+ JOIN tax t ON (c.id = t.chart_id)
+ WHERE c.link LIKE '%${ARAP}_tax%'
+ $where
+ ORDER BY accno, validto|;
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ $form->{taxaccounts} = "";
+ my %a = ();
+
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+
+ if ($tax{$ref->{accno}}) {
+ if (not exists $a{$ref->{accno}}) {
+ for (qw(rate description taxnumber)) { $form->{"$ref->{accno}_$_"} = $ref->{$_} }
+ $form->{taxaccounts} .= "$ref->{accno} ";
+ $a{$ref->{accno}} = 1;
+ }
+ }
+ }
+
+ $sth->finish;
+ chop $form->{taxaccounts};
+
+ # setup last accounts used for this customer/vendor
+ if (!$form->{id} && $form->{type} !~ /_(order|quotation)/) {
+
+ $query = qq|SELECT c.accno, c.description, c.link, c.category,
+ ac.project_id, p.projectnumber, a.department_id,
+ d.description AS department
+ FROM chart c
+ JOIN acc_trans ac ON (ac.chart_id = c.id)
+ JOIN $arap a ON (a.id = ac.trans_id)
+ LEFT JOIN project p ON (ac.project_id = p.id)
+ LEFT JOIN department d ON (d.id = a.department_id)
+ WHERE a.$form->{vc}_id = $form->{"$form->{vc}_id"}
+ AND a.id IN (SELECT max(id)
+ FROM $arap
+ WHERE $form->{vc}_id = $form->{"$form->{vc}_id"})|;
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ my $i = 0;
+
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+ $form->{department} = $ref->{department};
+ $form->{department_id} = $ref->{department_id};
+
+ if ($ref->{link} =~ /_amount/) {
+ $i++;
+ $form->{"$form->{ARAP}_amount_$i"} = "$ref->{accno}--$ref->{description}" if $ref->{accno};
+ $form->{"projectnumber_$i"} = "$ref->{projectnumber}--$ref->{project_id}" if $ref->{project_id};
+ }
+
+ if ($ref->{link} eq $form->{ARAP}) {
+ $form->{$form->{ARAP}} = $form->{"$form->{ARAP}_1"} = "$ref->{accno}--$ref->{description}" if $ref->{accno};
+ }
+ }
+
+ $sth->finish;
+ $form->{rowcount} = $i if ($i && !$form->{type});
+ }
+
+ $dbh->disconnect;
+}
+
+1;
diff --git a/LedgerSMB/AM.pm b/LedgerSMB/AM.pm
new file mode 100755
index 00000000..ed4477bf
--- /dev/null
+++ b/LedgerSMB/AM.pm
@@ -0,0 +1,1832 @@
+#=====================================================================
+# LedgerSMB
+# Small Medium Business Accounting software
+#
+# See COPYRIGHT file for copyright information
+#======================================================================
+#
+# This file has undergone whitespace cleanup.
+#
+#======================================================================
+#
+# Administration module
+# Chart of Accounts
+# template routines
+# preferences
+#
+#======================================================================
+
+package AM;
+
+
+sub get_account {
+
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ my $query = qq|SELECT accno, description, charttype, gifi_accno,
+ category, link, contra
+ FROM chart
+ WHERE id = $form->{id}|;
+
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ my $ref = $sth->fetchrow_hashref(NAME_lc);
+ for (keys %$ref) { $form->{$_} = $ref->{$_} }
+ $sth->finish;
+
+ # get default accounts
+ $query = qq|SELECT inventory_accno_id, income_accno_id, expense_accno_id,
+ fxgain_accno_id, fxloss_accno_id
+ FROM defaults|;
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ $ref = $sth->fetchrow_hashref(NAME_lc);
+ for (keys %$ref) { $form->{$_} = $ref->{$_} }
+ $sth->finish;
+
+ # check if we have any transactions
+ $query = qq|SELECT trans_id FROM acc_trans
+ WHERE chart_id = $form->{id}|;
+
+ ($form->{orphaned}) = $dbh->selectrow_array($query);
+ $form->{orphaned} = !$form->{orphaned};
+
+ $dbh->disconnect;
+
+}
+
+
+sub save_account {
+
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database, turn off AutoCommit
+ my $dbh = $form->dbconnect_noauto($myconfig);
+
+ $form->{link} = "";
+ foreach my $item ($form->{AR},
+ $form->{AR_amount},
+ $form->{AR_tax},
+ $form->{AR_paid},
+ $form->{AP},
+ $form->{AP_amount},
+ $form->{AP_tax},
+ $form->{AP_paid},
+ $form->{IC},
+ $form->{IC_income},
+ $form->{IC_sale},
+ $form->{IC_expense},
+ $form->{IC_cogs},
+ $form->{IC_taxpart},
+ $form->{IC_taxservice}) {
+ $form->{link} .= "${item}:" if ($item);
+ }
+
+ chop $form->{link};
+
+ # strip blanks from accno
+ for (qw(accno gifi_accno)) { $form->{$_} =~ s/( |')//g }
+
+ foreach my $item (qw(accno gifi_accno description)) {
+ $form->{$item} =~ s/-(-+)/-/g;
+ $form->{$item} =~ s/ ( )+/ /g;
+ }
+
+ my $query;
+ my $sth;
+
+ $form->{contra} *= 1;
+
+ # if we have an id then replace the old record
+ if ($form->{id}) {
+ $query = qq|UPDATE chart SET accno = '$form->{accno}',
+ description = |.$dbh->quote($form->{description}).qq|,
+ charttype = '$form->{charttype}',
+ gifi_accno = '$form->{gifi_accno}',
+ category = '$form->{category}',
+ link = '$form->{link}',
+ contra = '$form->{contra}'
+ WHERE id = $form->{id}|;
+ } else {
+ $query = qq|INSERT INTO chart (accno, description, charttype,
+ gifi_accno, category, link, contra)
+ VALUES ('$form->{accno}',|
+ .$dbh->quote($form->{description}).qq|,
+ '$form->{charttype}', '$form->{gifi_accno}',
+ '$form->{category}', '$form->{link}', '$form->{contra}')|;
+ }
+
+ $dbh->do($query) || $form->dberror($query);
+
+
+ $chart_id = $form->{id};
+
+ if (! $form->{id}) {
+ # get id from chart
+ $query = qq|SELECT id
+ FROM chart
+ WHERE accno = '$form->{accno}'|;
+
+ ($chart_id) = $dbh->selectrow_array($query);
+ }
+
+ if ($form->{IC_taxpart} || $form->{IC_taxservice} || $form->{AR_tax} || $form->{AP_tax}) {
+
+ # add account if it doesn't exist in tax
+ $query = qq|SELECT chart_id
+ FROM tax
+ WHERE chart_id = $chart_id|;
+
+ my ($tax_id) = $dbh->selectrow_array($query);
+
+ # add tax if it doesn't exist
+ unless ($tax_id) {
+ $query = qq|INSERT INTO tax (chart_id, rate)
+ VALUES ($chart_id, 0)|;
+
+ $dbh->do($query) || $form->dberror($query);
+ }
+
+ } else {
+
+ # remove tax
+ if ($form->{id}) {
+ $query = qq|DELETE FROM tax
+ WHERE chart_id = $form->{id}|;
+
+ $dbh->do($query) || $form->dberror($query);
+ }
+ }
+
+ # commit
+ my $rc = $dbh->commit;
+ $dbh->disconnect;
+
+ $rc;
+}
+
+
+
+sub delete_account {
+
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database, turn off AutoCommit
+ my $dbh = $form->dbconnect_noauto($myconfig);
+
+ ## needs fixing (SELECT *...)
+ my $query = qq|SELECT *
+ FROM acc_trans
+ WHERE chart_id = $form->{id}|;
+
+ if ($dbh->selectrow_array($query)) {
+ $dbh->disconnect;
+ return;
+ }
+
+
+ # delete chart of account record
+ $query = qq|DELETE FROM chart
+ WHERE id = $form->{id}|;
+
+ $dbh->do($query) || $form->dberror($query);
+
+ # set inventory_accno_id, income_accno_id, expense_accno_id to defaults
+ $query = qq|UPDATE parts
+ SET inventory_accno_id = (SELECT inventory_accno_id
+ FROM defaults)
+ WHERE inventory_accno_id = $form->{id}|;
+
+ $dbh->do($query) || $form->dberror($query);
+
+ $query = qq|UPDATE parts
+ SET income_accno_id = (SELECT income_accno_id
+ FROM defaults)
+ WHERE income_accno_id = $form->{id}|;
+
+ $dbh->do($query) || $form->dberror($query);
+
+ $query = qq|UPDATE parts
+ SET expense_accno_id = (SELECT expense_accno_id
+ FROM defaults)
+ WHERE expense_accno_id = $form->{id}|;
+
+ $dbh->do($query) || $form->dberror($query);
+
+ foreach my $table (qw(partstax customertax vendortax tax)) {
+ $query = qq|DELETE FROM $table
+ WHERE chart_id = $form->{id}|;
+
+ $dbh->do($query) || $form->dberror($query);
+ }
+
+ # commit and redirect
+ my $rc = $dbh->commit;
+ $dbh->disconnect;
+
+ $rc;
+}
+
+
+sub gifi_accounts {
+
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ my $query = qq|SELECT accno, description
+ FROM gifi
+ ORDER BY accno|;
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
+ push @{ $form->{ALL} }, $ref;
+ }
+
+ $sth->finish;
+
+ $dbh->disconnect;
+}
+
+
+
+sub get_gifi {
+
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ my $query = qq|SELECT accno, description
+ FROM gifi
+ WHERE accno = '$form->{accno}'|;
+
+ ($form->{accno}, $form->{description}) = $dbh->selectrow_array($query);
+
+ # check for transactions ## needs fixing (SELECT *...)
+ $query = qq|SELECT *
+ FROM acc_trans a
+ JOIN chart c ON (a.chart_id = c.id)
+ JOIN gifi g ON (c.gifi_accno = g.accno)
+ WHERE g.accno = '$form->{accno}'|;
+
+ ($form->{orphaned}) = $dbh->selectrow_array($query);
+ $form->{orphaned} = !$form->{orphaned};
+
+ $dbh->disconnect;
+
+}
+
+
+sub save_gifi {
+
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ $form->{accno} =~ s/( |')//g;
+
+ foreach my $item (qw(accno description)) {
+ $form->{$item} =~ s/-(-+)/-/g;
+ $form->{$item} =~ s/ ( )+/ /g;
+ }
+
+ # id is the old account number!
+ if ($form->{id}) {
+ $query = qq|UPDATE gifi
+ SET accno = '$form->{accno}',
+ description = |.$dbh->quote($form->{description}).qq|
+ WHERE accno = '$form->{id}'|;
+
+ } else {
+ $query = qq|INSERT INTO gifi (accno, description)
+ VALUES ('$form->{accno}',|
+ .$dbh->quote($form->{description}).qq|)|;
+ }
+
+ $dbh->do($query) || $form->dberror;
+ $dbh->disconnect;
+
+}
+
+
+sub delete_gifi {
+
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ # id is the old account number!
+ $query = qq|DELETE FROM gifi
+ WHERE accno = '$form->{id}'|;
+
+ $dbh->do($query) || $form->dberror($query);
+ $dbh->disconnect;
+
+}
+
+
+sub warehouses {
+
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ $form->sort_order();
+ my $query = qq|SELECT id, description
+ FROM warehouse
+ ORDER BY description $form->{direction}|;
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
+ push @{ $form->{ALL} }, $ref;
+ }
+
+ $sth->finish;
+ $dbh->disconnect;
+
+}
+
+
+sub get_warehouse {
+
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ my $query = qq|SELECT description
+ FROM warehouse
+ WHERE id = $form->{id}|;
+
+ ($form->{description}) = $dbh->selectrow_array($query);
+
+ # see if it is in use
+ $query = qq|SELECT * FROM inventory
+ WHERE warehouse_id = $form->{id}|;
+
+ ($form->{orphaned}) = $dbh->selectrow_array($query);
+ $form->{orphaned} = !$form->{orphaned};
+
+ $dbh->disconnect;
+}
+
+
+sub save_warehouse {
+
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ $form->{description} =~ s/-(-)+/-/g;
+ $form->{description} =~ s/ ( )+/ /g;
+
+ if ($form->{id}) {
+ $query = qq|UPDATE warehouse
+ SET description = |.$dbh->quote($form->{description}).qq|
+ WHERE id = $form->{id}|;
+ } else {
+ $query = qq|INSERT INTO warehouse (description)
+ VALUES (|.$dbh->quote($form->{description}).qq|)|;
+ }
+
+ $dbh->do($query) || $form->dberror($query);
+ $dbh->disconnect;
+
+}
+
+
+sub delete_warehouse {
+
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ $query = qq|DELETE FROM warehouse
+ WHERE id = $form->{id}|;
+
+ $dbh->do($query) || $form->dberror($query);
+ $dbh->disconnect;
+
+}
+
+
+
+sub departments {
+
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ $form->sort_order();
+ my $query = qq|SELECT id, description, role
+ FROM department
+ ORDER BY description $form->{direction}|;
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
+ push @{ $form->{ALL} }, $ref;
+ }
+
+ $sth->finish;
+ $dbh->disconnect;
+
+}
+
+
+
+sub get_department {
+
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ my $query = qq|SELECT description, role
+ FROM department
+ WHERE id = $form->{id}|;
+
+ ($form->{description}, $form->{role}) = $dbh->selectrow_array($query);
+
+ for (keys %$ref) { $form->{$_} = $ref->{$_} }
+
+ # see if it is in use ## needs fixing (SELECT * ...)
+ $query = qq|SELECT *
+ FROM dpt_trans
+ WHERE department_id = $form->{id}|;
+
+ ($form->{orphaned}) = $dbh->selectrow_array($query);
+ $form->{orphaned} = !$form->{orphaned};
+
+ $dbh->disconnect;
+}
+
+
+sub save_department {
+
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ $form->{description} =~ s/-(-)+/-/g;
+ $form->{description} =~ s/ ( )+/ /g;
+
+ if ($form->{id}) {
+ $query = qq|UPDATE department
+ SET description = |.$dbh->quote($form->{description}).qq|,
+ role = '$form->{role}'
+ WHERE id = $form->{id}|;
+
+ } else {
+ $query = qq|INSERT INTO department (description, role)
+ VALUES (| .$dbh->quote($form->{description}).qq|, '$form->{role}')|;
+ }
+
+ $dbh->do($query) || $form->dberror($query);
+ $dbh->disconnect;
+
+}
+
+
+sub delete_department {
+
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ $query = qq|DELETE FROM department
+ WHERE id = $form->{id}|;
+
+ $dbh->do($query);
+ $dbh->disconnect;
+
+}
+
+
+sub business {
+
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ $form->sort_order();
+ my $query = qq|SELECT id, description, discount
+ FROM business
+ ORDER BY description $form->{direction}|;
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
+ push @{ $form->{ALL} }, $ref;
+ }
+
+ $sth->finish;
+ $dbh->disconnect;
+
+}
+
+
+sub get_business {
+
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ my $query = qq|SELECT description, discount
+ FROM business
+ WHERE id = $form->{id}|;
+
+ ($form->{description}, $form->{discount}) = $dbh->selectrow_array($query);
+ $dbh->disconnect;
+
+}
+
+
+sub save_business {
+
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ $form->{description} =~ s/-(-)+/-/g;
+ $form->{description} =~ s/ ( )+/ /g;
+ $form->{discount} /= 100;
+
+ if ($form->{id}) {
+ $query = qq|UPDATE business
+ SET description = |.$dbh->quote($form->{description}).qq|,
+ discount = $form->{discount}
+ WHERE id = $form->{id}|;
+
+ } else {
+ $query = qq|INSERT INTO business (description, discount)
+ VALUES (| .$dbh->quote($form->{description}).qq|, $form->{discount})|;
+ }
+
+ $dbh->do($query) || $form->dberror($query);
+ $dbh->disconnect;
+
+}
+
+
+sub delete_business {
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ $query = qq|DELETE FROM business
+ WHERE id = $form->{id}|;
+
+ $dbh->do($query) || $form->dberror($query);
+ $dbh->disconnect;
+
+}
+
+
+sub sic {
+
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ $form->{sort} = "code" unless $form->{sort};
+ my @a = qw(code description);
+
+ my %ordinal = ( code => 1,
+ description => 3 );
+
+ my $sortorder = $form->sort_order(\@a, \%ordinal);
+
+ my $query = qq|SELECT code, sictype, description
+ FROM sic
+ ORDER BY $sortorder|;
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
+ push @{ $form->{ALL} }, $ref;
+ }
+
+ $sth->finish;
+ $dbh->disconnect;
+
+}
+
+
+sub get_sic {
+
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ my $query = qq|SELECT code, sictype, description
+ FROM sic
+ WHERE code = |.$dbh->quote($form->{code});
+
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ my $ref = $sth->fetchrow_hashref(NAME_lc);
+ for (keys %$ref) { $form->{$_} = $ref->{$_} }
+
+ $sth->finish;
+ $dbh->disconnect;
+
+}
+
+
+sub save_sic {
+
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ foreach my $item (qw(code description)) {
+ $form->{$item} =~ s/-(-)+/-/g;
+ }
+
+ # if there is an id
+ if ($form->{id}) {
+ $query = qq|UPDATE sic
+ SET code = |.$dbh->quote($form->{code}).qq|,
+ sictype = '$form->{sictype}',
+ description = |.$dbh->quote($form->{description}).qq|
+ WHERE code = |.$dbh->quote($form->{id});
+
+ } else {
+ $query = qq|INSERT INTO sic (code, sictype, description)
+ VALUES (|.$dbh->quote($form->{code}).qq|,
+ '$form->{sictype}',|
+ .$dbh->quote($form->{description}).qq|)|;
+
+ }
+
+ $dbh->do($query) || $form->dberror($query);
+ $dbh->disconnect;
+
+}
+
+
+sub delete_sic {
+
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ $query = qq|DELETE FROM sic
+ WHERE code = |.$dbh->quote($form->{code});
+
+ $dbh->do($query);
+ $dbh->disconnect;
+
+}
+
+
+sub language {
+
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ $form->{sort} = "code" unless $form->{sort};
+ my @a = qw(code description);
+
+ my %ordinal = ( code => 1,
+ description => 2 );
+
+ my $sortorder = $form->sort_order(\@a, \%ordinal);
+
+ my $query = qq|SELECT code, description
+ FROM language
+ ORDER BY $sortorder|;
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
+ push @{ $form->{ALL} }, $ref;
+ }
+
+ $sth->finish;
+ $dbh->disconnect;
+
+}
+
+
+sub get_language {
+
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ ## needs fixing (SELECT *...)
+ my $query = qq|SELECT *
+ FROM language
+ WHERE code = |.$dbh->quote($form->{code});
+
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ my $ref = $sth->fetchrow_hashref(NAME_lc);
+
+ for (keys %$ref) { $form->{$_} = $ref->{$_} }
+
+ $sth->finish;
+ $dbh->disconnect;
+
+}
+
+
+sub save_language {
+
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ $form->{code} =~ s/ //g;
+
+ foreach my $item (qw(code description)) {
+ $form->{$item} =~ s/-(-)+/-/g;
+ $form->{$item} =~ s/ ( )+/-/g;
+ }
+
+ # if there is an id
+ if ($form->{id}) {
+ $query = qq|UPDATE language
+ SET code = |.$dbh->quote($form->{code}).qq|,
+ description = |.$dbh->quote($form->{description}).qq|
+ WHERE code = |.$dbh->quote($form->{id});
+
+ } else {
+ $query = qq|INSERT INTO language (code, description)
+ VALUES (|.$dbh->quote($form->{code}).qq|,|
+ .$dbh->quote($form->{description}).qq|)|;
+ }
+
+ $dbh->do($query) || $form->dberror($query);
+ $dbh->disconnect;
+
+}
+
+
+sub delete_language {
+
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ $query = qq|DELETE FROM language
+ WHERE code = |.$dbh->quote($form->{code});
+
+ $dbh->do($query) || $form->dberror($query);
+ $dbh->disconnect;
+
+}
+
+
+sub recurring_transactions {
+
+ my ($self, $myconfig, $form) = @_;
+
+ my $dbh = $form->dbconnect($myconfig);
+
+ my $query = qq|SELECT curr FROM defaults|;
+
+ my ($defaultcurrency) = $dbh->selectrow_array($query);
+ $defaultcurrency =~ s/:.*//g;
+
+ $form->{sort} ||= "nextdate";
+ my @a = ($form->{sort});
+ my $sortorder = $form->sort_order(\@a);
+
+ $query = qq|SELECT 'ar' AS module, 'ar' AS transaction, a.invoice,
+ n.name AS description, a.amount,
+ s.*, se.formname AS recurringemail,
+ sp.formname AS recurringprint,
+ s.nextdate - current_date AS overdue, 'customer' AS vc,
+ ex.buy AS exchangerate, a.curr,
+ (s.nextdate IS NULL OR s.nextdate > s.enddate) AS expired
+ FROM recurring s
+ JOIN ar a ON (a.id = s.id)
+ JOIN customer n ON (n.id = a.customer_id)
+ LEFT JOIN recurringemail se ON (se.id = s.id)
+ LEFT JOIN recurringprint sp ON (sp.id = s.id)
+ LEFT JOIN exchangerate ex ON (ex.curr = a.curr AND a.transdate = ex.transdate)
+
+ UNION
+
+ SELECT 'ap' AS module, 'ap' AS transaction, a.invoice,
+ n.name AS description, a.amount,
+ s.*, se.formname AS recurringemail,
+ sp.formname AS recurringprint,
+ s.nextdate - current_date AS overdue, 'vendor' AS vc,
+ ex.sell AS exchangerate, a.curr,
+ (s.nextdate IS NULL OR s.nextdate > s.enddate) AS expired
+ FROM recurring s
+ JOIN ap a ON (a.id = s.id)
+ JOIN vendor n ON (n.id = a.vendor_id)
+ LEFT JOIN recurringemail se ON (se.id = s.id)
+ LEFT JOIN recurringprint sp ON (sp.id = s.id)
+ LEFT JOIN exchangerate ex ON (ex.curr = a.curr AND a.transdate = ex.transdate)
+
+ UNION
+
+ SELECT 'gl' AS module, 'gl' AS transaction, FALSE AS invoice,
+ a.description, (SELECT SUM(ac.amount)
+ FROM acc_trans ac
+ WHERE ac.trans_id = a.id
+ AND ac.amount > 0) AS amount,
+ s.*, se.formname AS recurringemail,
+ sp.formname AS recurringprint,
+ s.nextdate - current_date AS overdue, '' AS vc,
+ '1' AS exchangerate, '$defaultcurrency' AS curr,
+ (s.nextdate IS NULL OR s.nextdate > s.enddate) AS expired
+ FROM recurring s
+ JOIN gl a ON (a.id = s.id)
+ LEFT JOIN recurringemail se ON (se.id = s.id)
+ LEFT JOIN recurringprint sp ON (sp.id = s.id)
+
+ UNION
+
+ SELECT 'oe' AS module, 'so' AS transaction, FALSE AS invoice,
+ n.name AS description, a.amount,
+ s.*, se.formname AS recurringemail,
+ sp.formname AS recurringprint,
+ s.nextdate - current_date AS overdue, 'customer' AS vc,
+ ex.buy AS exchangerate, a.curr,
+ (s.nextdate IS NULL OR s.nextdate > s.enddate) AS expired
+ FROM recurring s
+ JOIN oe a ON (a.id = s.id)
+ JOIN customer n ON (n.id = a.customer_id)
+ LEFT JOIN recurringemail se ON (se.id = s.id)
+ LEFT JOIN recurringprint sp ON (sp.id = s.id)
+ LEFT JOIN exchangerate ex ON (ex.curr = a.curr AND a.transdate = ex.transdate)
+ WHERE a.quotation = '0'
+
+ UNION
+
+ SELECT 'oe' AS module, 'po' AS transaction, FALSE AS invoice,
+ n.name AS description, a.amount,
+ s.*, se.formname AS recurringemail,
+ sp.formname AS recurringprint,
+ s.nextdate - current_date AS overdue, 'vendor' AS vc,
+ ex.sell AS exchangerate, a.curr,
+ (s.nextdate IS NULL OR s.nextdate > s.enddate) AS expired
+ FROM recurring s
+ JOIN oe a ON (a.id = s.id)
+ JOIN vendor n ON (n.id = a.vendor_id)
+ LEFT JOIN recurringemail se ON (se.id = s.id)
+ LEFT JOIN recurringprint sp ON (sp.id = s.id)
+ LEFT JOIN exchangerate ex ON (ex.curr = a.curr AND a.transdate = ex.transdate)
+ WHERE a.quotation = '0'
+
+ ORDER BY $sortorder|;
+
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ my $id;
+ my $transaction;
+ my %e = ();
+ my %p = ();
+
+ while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
+
+ $ref->{exchangerate} ||= 1;
+
+ if ($ref->{id} != $id) {
+
+ if (%e) {
+ $form->{transactions}{$transaction}->[$i]->{recurringemail} = "";
+ for (keys %e) { $form->{transactions}{$transaction}->[$i]->{recurringemail} .= "${_}:" }
+ chop $form->{transactions}{$transaction}->[$i]->{recurringemail};
+ }
+
+ if (%p) {
+ $form->{transactions}{$transaction}->[$i]->{recurringprint} = "";
+ for (keys %p) { $form->{transactions}{$transaction}->[$i]->{recurringprint} .= "${_}:" }
+ chop $form->{transactions}{$transaction}->[$i]->{recurringprint};
+ }
+
+ %e = ();
+ %p = ();
+
+ push @{ $form->{transactions}{$ref->{transaction}} }, $ref;
+
+ $id = $ref->{id};
+ $i = $#{ $form->{transactions}{$ref->{transaction}} };
+
+ }
+
+ $transaction = $ref->{transaction};
+
+ $e{$ref->{recurringemail}} = 1 if $ref->{recurringemail};
+ $p{$ref->{recurringprint}} = 1 if $ref->{recurringprint};
+
+ }
+
+ $sth->finish;
+
+ # this is for the last row
+ if (%e) {
+ $form->{transactions}{$transaction}->[$i]->{recurringemail} = "";
+ for (keys %e) { $form->{transactions}{$transaction}->[$i]->{recurringemail} .= "${_}:" }
+ chop $form->{transactions}{$transaction}->[$i]->{recurringemail};
+ }
+
+ if (%p) {
+ $form->{transactions}{$transaction}->[$i]->{recurringprint} = "";
+ for (keys %p) { $form->{transactions}{$transaction}->[$i]->{recurringprint} .= "${_}:" }
+ chop $form->{transactions}{$transaction}->[$i]->{recurringprint};
+ }
+
+
+ $dbh->disconnect;
+
+}
+
+sub recurring_details {
+
+ my ($self, $myconfig, $form, $id) = @_;
+
+ my $dbh = $form->dbconnect($myconfig);
+
+ my $query = qq|SELECT s.*, ar.id AS arid, ar.invoice AS arinvoice,
+ ap.id AS apid, ap.invoice AS apinvoice,
+ ar.duedate - ar.transdate AS overdue,
+ ar.datepaid - ar.transdate AS paid,
+ oe.reqdate - oe.transdate AS req,
+ oe.id AS oeid, oe.customer_id, oe.vendor_id
+ FROM recurring s
+ LEFT JOIN ar ON (ar.id = s.id)
+ LEFT JOIN ap ON (ap.id = s.id)
+ LEFT JOIN oe ON (oe.id = s.id)
+ WHERE s.id = $id|;
+
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ my $ref = $sth->fetchrow_hashref(NAME_lc);
+ $form->{vc} = "customer" if $ref->{customer_id};
+ $form->{vc} = "vendor" if $ref->{vendor_id};
+ for (keys %$ref) { $form->{$_} = $ref->{$_} }
+ $sth->finish;
+
+ $form->{invoice} = ($form->{arid} && $form->{arinvoice});
+ $form->{invoice} = ($form->{apid} && $form->{apinvoice}) unless $form->{invoice};
+
+ $query = qq|SELECT *
+ FROM recurringemail
+ WHERE id = $id|;
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ $form->{recurringemail} = "";
+
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+ $form->{recurringemail} .= "$ref->{formname}:$ref->{format}:";
+ $form->{message} = $ref->{message};
+ }
+
+ $sth->finish;
+
+ $query = qq|SELECT *
+ FROM recurringprint
+ WHERE id = $id|;
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ $form->{recurringprint} = "";
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+ $form->{recurringprint} .= "$ref->{formname}:$ref->{format}:$ref->{printer}:";
+ }
+
+ $sth->finish;
+
+ chop $form->{recurringemail};
+ chop $form->{recurringprint};
+
+ for (qw(arinvoice apinvoice)) { delete $form->{$_} }
+
+ $dbh->disconnect;
+
+}
+
+
+sub update_recurring {
+
+ my ($self, $myconfig, $form, $id) = @_;
+
+ my $dbh = $form->dbconnect($myconfig);
+
+ my $query = qq|SELECT nextdate, repeat, unit
+ FROM recurring
+ WHERE id = $id|;
+
+ my ($nextdate, $repeat, $unit) = $dbh->selectrow_array($query);
+
+ my %advance = ( 'Pg' => "(date '$nextdate' + interval '$repeat $unit')",
+ 'DB2' => qq|(date ('$nextdate') + "$repeat $unit")|,);
+
+ $interval{Oracle} = $interval{PgPP} = $interval{Pg};
+
+ # check if it is the last date
+ $query = qq|SELECT $advance{$myconfig->{dbdriver}} > enddate
+ FROM recurring
+ WHERE id = $id|;
+
+ my ($last_repeat) = $dbh->selectrow_array($query);
+ if ($last_repeat) {
+ $advance{$myconfig->{dbdriver}} = "NULL";
+ }
+
+ $query = qq|UPDATE recurring
+ SET nextdate = $advance{$myconfig->{dbdriver}}
+ WHERE id = $id|;
+
+ $dbh->do($query) || $form->dberror($query);
+
+ $dbh->disconnect;
+
+}
+
+
+sub load_template {
+
+ my ($self, $form) = @_;
+
+ open(TEMPLATE, "$form->{file}") or $form->error("$form->{file} : $!");
+
+ while (<TEMPLATE>) {
+ $form->{body} .= $_;
+ }
+
+ close(TEMPLATE);
+
+}
+
+
+sub save_template {
+
+ my ($self, $form) = @_;
+
+ open(TEMPLATE, ">$form->{file}") or $form->error("$form->{file} : $!");
+
+ # strip
+ $form->{body} =~ s/\r//g;
+ print TEMPLATE $form->{body};
+
+ close(TEMPLATE);
+
+}
+
+
+sub save_preferences {
+
+ my ($self, $myconfig, $form, $memberfile, $userspath) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ # update name
+ my $query = qq|UPDATE employee
+ SET name = |.$dbh->quote($form->{name}).qq|,
+ role = '$form->{role}'
+ WHERE login = '$form->{login}'|;
+
+ $dbh->do($query) || $form->dberror($query);
+
+ # get default currency
+ $query = qq|SELECT curr, businessnumber
+ FROM defaults|;
+
+ ($form->{currency}, $form->{businessnumber}) = $dbh->selectrow_array($query);
+ $form->{currency} =~ s/:.*//;
+
+ $dbh->disconnect;
+
+ my $myconfig = new User "$memberfile", "$form->{login}";
+
+ foreach my $item (keys %$form) {
+ $myconfig->{$item} = $form->{$item};
+ }
+
+ $myconfig->{password} = $form->{new_password} if ($form->{old_password} ne $form->{new_password});
+
+ $myconfig->save_member($memberfile, $userspath);
+
+ 1;
+
+}
+
+
+sub save_defaults {
+
+ my ($self, $myconfig, $form) = @_;
+
+ for (qw(IC IC_income IC_expense FX_gain FX_loss)) { ($form->{$_}) = split /--/, $form->{$_} }
+
+ my @a;
+ $form->{curr} =~ s/ //g;
+ for (split /:/, $form->{curr}) { push(@a, uc pack "A3", $_) if $_ }
+ $form->{curr} = join ':', @a;
+
+ # connect to database
+ my $dbh = $form->dbconnect_noauto($myconfig);
+
+ # save defaults
+ my $query = qq|UPDATE defaults
+ SET inventory_accno_id = (SELECT id
+ FROM chart
+ WHERE accno = '$form->{IC}'),
+ income_accno_id = (SELECT id
+ FROM chart
+ WHERE accno = '$form->{IC_income}'),
+ expense_accno_id = (SELECT id
+ FROM chart
+ WHERE accno = '$form->{IC_expense}'),
+ fxgain_accno_id = (SELECT id
+ FROM chart
+ WHERE accno = '$form->{FX_gain}'),
+ fxloss_accno_id = (SELECT id
+ FROM chart
+ WHERE accno = '$form->{FX_loss}'),
+ glnumber = '$form->{glnumber}',
+ sinumber = '$form->{sinumber}',
+ vinumber = '$form->{vinumber}',
+ sonumber = '$form->{sonumber}',
+ ponumber = '$form->{ponumber}',
+ sqnumber = '$form->{sqnumber}',
+ rfqnumber = '$form->{rfqnumber}',
+ partnumber = '$form->{partnumber}',
+ employeenumber = '$form->{employeenumber}',
+ customernumber = '$form->{customernumber}',
+ vendornumber = '$form->{vendornumber}',
+ projectnumber = '$form->{projectnumber}',
+ yearend = '$form->{yearend}',
+ curr = '$form->{curr}',
+ weightunit = |.$dbh->quote($form->{weightunit}).qq|,
+ businessnumber = |.$dbh->quote($form->{businessnumber});
+
+ $dbh->do($query) || $form->dberror($query);
+
+ my $rc = $dbh->commit;
+ $dbh->disconnect;
+
+ $rc;
+
+}
+
+
+sub defaultaccounts {
+
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ # get defaults from defaults table
+ my $query = qq|SELECT * FROM defaults|;
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ my $ref = $sth->fetchrow_hashref(NAME_lc);
+ for (keys %$ref) { $form->{$_} = $ref->{$_} }
+
+ $form->{defaults}{IC} = $form->{inventory_accno_id};
+ $form->{defaults}{IC_income} = $form->{income_accno_id};
+ $form->{defaults}{IC_sale} = $form->{income_accno_id};
+ $form->{defaults}{IC_expense} = $form->{expense_accno_id};
+ $form->{defaults}{IC_cogs} = $form->{expense_accno_id};
+ $form->{defaults}{FX_gain} = $form->{fxgain_accno_id};
+ $form->{defaults}{FX_loss} = $form->{fxloss_accno_id};
+
+ $sth->finish;
+
+ $query = qq|SELECT id, accno, description, link
+ FROM chart
+ WHERE link LIKE '%IC%'
+ ORDER BY accno|;
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ my $nkey;
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+ foreach my $key (split(/:/, $ref->{link})) {
+ if ($key =~ /IC/) {
+ $nkey = $key;
+
+ if ($key =~ /cogs/) {
+ $nkey = "IC_expense";
+ }
+
+ if ($key =~ /sale/) {
+ $nkey = "IC_income";
+ }
+
+ %{ $form->{accno}{$nkey}{$ref->{accno}} } = ( id => $ref->{id},
+ description => $ref->{description} );
+ }
+ }
+ }
+
+ $sth->finish;
+
+
+ $query = qq|SELECT id, accno, description
+ FROM chart
+ WHERE (category = 'I' OR category = 'E')
+ AND charttype = 'A'
+ ORDER BY accno|;
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
+ %{ $form->{accno}{FX_gain}{$ref->{accno}} } = ( id => $ref->{id},
+ description => $ref->{description} );
+
+ %{ $form->{accno}{FX_loss}{$ref->{accno}} } = ( id => $ref->{id},
+ description => $ref->{description} );
+ }
+
+ $sth->finish;
+
+ $dbh->disconnect;
+
+}
+
+
+sub taxes {
+
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ my $query = qq|SELECT c.id, c.accno, c.description,
+ t.rate * 100 AS rate, t.taxnumber, t.validto
+ FROM chart c
+ JOIN tax t ON (c.id = t.chart_id)
+ ORDER BY 3, 6|;
+
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
+ push @{ $form->{taxrates} }, $ref;
+ }
+
+ $sth->finish;
+
+ $dbh->disconnect;
+
+}
+
+
+sub save_taxes {
+
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect_noauto($myconfig);
+
+ my $query = qq|DELETE FROM tax|;
+ $dbh->do($query) || $form->dberror($query);
+
+ foreach my $item (split / /, $form->{taxaccounts}) {
+ my ($chart_id, $i) = split /_/, $item;
+ my $rate = $form->parse_amount($myconfig, $form->{"taxrate_$i"}) / 100;
+
+ $query = qq|INSERT INTO tax (chart_id, rate, taxnumber, validto)
+ VALUES ($chart_id, $rate, |
+ .$dbh->quote($form->{"taxnumber_$i"}).qq|, |
+ .$form->dbquote($form->{"validto_$i"}, SQL_DATE)
+ .qq|)|;
+
+ $dbh->do($query) || $form->dberror($query);
+ }
+
+ my $rc = $dbh->commit;
+ $dbh->disconnect;
+
+ $rc;
+
+}
+
+
+sub backup {
+
+ my ($self, $myconfig, $form, $userspath, $gzip) = @_;
+
+ my $mail;
+ my $err;
+
+ my @t = localtime(time);
+ $t[4]++;
+ $t[5] += 1900;
+ $t[3] = substr("0$t[3]", -2);
+ $t[4] = substr("0$t[4]", -2);
+
+ my $boundary = time;
+ my $tmpfile = "$userspath/$boundary.$myconfig->{dbname}-$form->{dbversion}-$t[5]$t[4]$t[3].sql";
+ my $out = $form->{OUT};
+ $form->{OUT} = ">$tmpfile";
+
+ open(OUT, "$form->{OUT}") or $form->error("$form->{OUT} : $!");
+
+ # get sequences, functions and triggers
+ my @tables = ();
+ my @sequences = ();
+ my @functions = ();
+ my @triggers = ();
+ my @schema = ();
+
+ # get dbversion from -tables.sql
+ my $file = "$myconfig->{dbdriver}-tables.sql";
+
+ open(FH, "sql/$file") or $form->error("sql/$file : $!");
+
+ my @a = <FH>;
+ close(FH);
+
+ @dbversion = grep /defaults \(version\)/, @a;
+
+ $dbversion = "@dbversion";
+ $dbversion =~ /(\d+\.\d+\.\d+)/;
+ $dbversion = User::calc_version($1);
+
+ opendir SQLDIR, "sql/." or $form->error($!);
+ @a = grep /$myconfig->{dbdriver}-upgrade-.*?\.sql$/, readdir SQLDIR;
+ closedir SQLDIR;
+
+ my $mindb;
+ my $maxdb;
+
+ foreach my $line (@a) {
+
+ $upgradescript = $line;
+ $line =~ s/(^$myconfig->{dbdriver}-upgrade-|\.sql$)//g;
+
+ ($mindb, $maxdb) = split /-/, $line;
+ $mindb = User::calc_version($mindb);
+
+ next if $mindb < $dbversion;
+
+ $maxdb = User::calc_version($maxdb);
+
+ $upgradescripts{$maxdb} = $upgradescript;
+ }
+
+
+ $upgradescripts{$dbversion} = "$myconfig->{dbdriver}-tables.sql";
+ $upgradescripts{functions} = "$myconfig->{dbdriver}-functions.sql";
+
+ if (-f "sql/$myconfig->{dbdriver}-custom_tables.sql") {
+ $upgradescripts{customtables} = "$myconfig->{dbdriver}-custom_tables.sql";
+ }
+
+ if (-f "sql/$myconfig->{dbdriver}-custom_functions.sql") {
+ $upgradescripts{customfunctions} = "$myconfig->{dbdriver}-custom_functions.sql";
+ }
+
+ foreach my $key (sort keys %upgradescripts) {
+
+ $file = $upgradescripts{$key};
+
+ open(FH, "sql/$file") or $form->error("sql/$file : $!");
+
+ push @schema, qq|-- $file\n|;
+
+ while (<FH>) {
+
+ if (/create table (\w+)/i) {
+ push @tables, $1;
+ }
+
+ if (/create sequence (\w+)/i) {
+ push @sequences, $1;
+ next;
+ }
+
+ if (/end function/i) {
+ push @functions, $_;
+ $function = 0;
+ $temp = 0;
+ next;
+ }
+
+ if (/create function /i) {
+ $function = 1;
+ }
+
+ if ($function) {
+ push @functions, $_;
+ next;
+ }
+
+ if (/end trigger/i) {
+ push @triggers, $_;
+ $trigger = 0;
+ next;
+ }
+
+ if (/create trigger/i) {
+ $trigger = 1;
+ }
+
+ if ($trigger) {
+ push @triggers, $_;
+ next;
+ }
+
+ push @schema, $_ if $_ !~ /^(insert|--)/i;
+
+ }
+
+ close(FH);
+
+ }
+
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ my $today = scalar localtime;
+
+ $myconfig->{dbhost} = 'localhost' unless $myconfig->{dbhost};
+
+ print OUT qq|-- LedgerSMB Backup
+ -- Dataset: $myconfig->{dbname}
+ -- Version: $form->{dbversion}
+ -- Host: $myconfig->{dbhost}
+ -- Login: $form->{login}
+ -- User: $myconfig->{name}
+ -- Date: $today
+ --
+ |;
+
+
+ @tables = grep !/^temp/, @tables;
+ # drop tables and sequences
+ for (@tables) { print OUT qq|DROP TABLE $_;\n| }
+
+ print OUT "--\n";
+
+ # triggers and index files are dropped with the tables
+
+ # drop functions
+ foreach $item (@functions) {
+ if ($item =~ /create function (.*\))/i) {
+ print OUT qq|DROP FUNCTION $1;\n|;
+ }
+ }
+
+ # create sequences
+ foreach $item (@sequences) {
+
+ if ($myconfig->{dbdriver} eq 'DB2') {
+ $query = qq|SELECT NEXTVAL FOR $item FROM sysibm.sysdummy1|;
+ } else {
+ $query = qq|SELECT last_value FROM $item|;
+ }
+
+ my ($id) = $dbh->selectrow_array($query);
+
+ if ($myconfig->{dbdriver} eq 'DB2') {
+ print OUT qq|DROP SEQUENCE $item RESTRICT
+ CREATE SEQUENCE $item AS INTEGER START WITH $id INCREMENT BY 1 MAXVALUE 2147483647 MINVALUE 1 CACHE 5;\n|;
+ } else {
+ if ($myconfig->{dbdriver} eq 'Pg') {
+ print OUT qq|CREATE SEQUENCE $item;
+ SELECT SETVAL('$item', $id, FALSE);\n|;
+ } else {
+ print OUT qq|DROP SEQUENCE $item
+ CREATE SEQUENCE $item START $id;\n|;
+ }
+ }
+ }
+
+ print OUT "--\n";
+
+ # add schema
+ print OUT @schema;
+ print OUT "\n";
+
+ print OUT qq|-- set options
+ $myconfig->{dboptions};
+ --
+ |;
+
+ my $query;
+ my $sth;
+ my @arr;
+ my $fields;
+
+ foreach $table (@tables) {
+
+ $query = qq|SELECT * FROM $table|;
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ $query = qq|INSERT INTO $table (|;
+ $query .= join ',', (map { $sth->{NAME}->[$_] } (0 .. $sth->{NUM_OF_FIELDS} - 1));
+ $query .= qq|) VALUES|;
+
+ while (@arr = $sth->fetchrow_array) {
+
+ $fields = "(";
+
+ $fields .= join ',', map { $dbh->quote($_) } @arr;
+ $fields .= ")";
+
+ print OUT qq|$query $fields;\n|;
+ }
+
+ $sth->finish;
+ }
+
+ print OUT "--\n";
+
+ # functions
+ for (@functions) { print OUT $_ }
+
+ # triggers
+ for (@triggers) { print OUT $_ }
+
+ # add the index files
+ open(FH, "sql/$myconfig->{dbdriver}-indices.sql");
+ @a = <FH>;
+ close(FH);
+ print OUT @a;
+
+ close(OUT);
+
+ $dbh->disconnect;
+
+ # compress backup if gzip defined
+ my $suffix = "";
+
+ if ($gzip) {
+ my @args = split / /, $gzip;
+ my @s = @args;
+
+ push @args, "$tmpfile";
+ system(@args) == 0 or $form->error("$args[0] : $?");
+
+ shift @s;
+ my %s = @s;
+ $suffix = ${-S} || ".gz";
+ $tmpfile .= $suffix;
+ }
+
+ if ($form->{media} eq 'email') {
+
+ use LedgerSMB::Mailer;
+ $mail = new Mailer;
+
+ $mail->{to} = qq|"$myconfig->{name}" <$myconfig->{email}>|;
+ $mail->{from} = qq|"$myconfig->{name}" <$myconfig->{email}>|;
+ $mail->{subject} = "LedgerSMB Backup / $myconfig->{dbname}-$form->{dbversion}-$t[5]$t[4]$t[3].sql$suffix";
+ @{ $mail->{attachments} } = ($tmpfile);
+ $mail->{version} = $form->{version};
+ $mail->{fileid} = "$boundary.";
+
+ $myconfig->{signature} =~ s/\\n/\n/g;
+ $mail->{message} = "-- \n$myconfig->{signature}";
+
+ $err = $mail->send($out);
+ }
+
+ if ($form->{media} eq 'file') {
+
+ open(IN, "$tmpfile") or $form->error("$tmpfile : $!");
+ open(OUT, ">-") or $form->error("STDOUT : $!");
+
+ print OUT qq|Content-Type: application/file;
+ Content-Disposition: attachment; filename="$myconfig->{dbname}-$form->{dbversion}-$t[5]$t[4]$t[3].sql$suffix"
+
+ |;
+ binmode(IN);
+ binmode(OUT);
+
+ while (<IN>) {
+ print OUT $_;
+ }
+
+ close(IN);
+ close(OUT);
+
+ }
+
+ unlink "$tmpfile";
+
+}
+
+
+sub closedto {
+ my ($self, $myconfig, $form) = @_;
+
+ my $dbh = $form->dbconnect($myconfig);
+
+ my $query = qq|SELECT closedto, revtrans, audittrail
+ FROM defaults|;
+
+ ($form->{closedto}, $form->{revtrans}, $form->{audittrail}) = $dbh->selectrow_array($query);
+
+ $dbh->disconnect;
+
+}
+
+
+sub closebooks {
+
+ my ($self, $myconfig, $form) = @_;
+
+ my $dbh = $form->dbconnect_noauto($myconfig);
+ my $query = qq|UPDATE defaults SET|;
+
+ if ($form->{revtrans}) {
+ $query .= qq| revtrans = '1'|;
+ } else {
+ $query .= qq| revtrans = '0'|;
+ }
+
+ $query .= qq|, closedto = |.$form->dbquote($form->{closedto}, SQL_DATE);
+
+ if ($form->{audittrail}) {
+ $query .= qq|, audittrail = '1'|;
+ } else {
+ $query .= qq|, audittrail = '0'|;
+ }
+
+ # set close in defaults
+ $dbh->do($query) || $form->dberror($query);
+
+ if ($form->{removeaudittrail}) {
+ $query = qq|DELETE FROM audittrail
+ WHERE transdate < '$form->{removeaudittrail}'|;
+
+ $dbh->do($query) || $form->dberror($query);
+ }
+
+ $dbh->commit;
+ $dbh->disconnect;
+
+}
+
+
+sub earningsaccounts {
+
+ my ($self, $myconfig, $form) = @_;
+
+ my ($query, $sth, $ref);
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ # get chart of accounts
+ $query = qq|SELECT accno,description
+ FROM chart
+ WHERE charttype = 'A'
+ AND category = 'Q'
+ ORDER BY accno|;
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+ $form->{chart} = "";
+
+ while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
+ push @{ $form->{chart} }, $ref;
+ }
+
+ $sth->finish;
+ $dbh->disconnect;
+}
+
+
+sub post_yearend {
+
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database, turn off AutoCommit
+ my $dbh = $form->dbconnect_noauto($myconfig);
+
+ my $query;
+ my $uid = localtime;
+ $uid .= "$$";
+
+ $query = qq|INSERT INTO gl (reference, employee_id)
+ VALUES ('$uid', (SELECT id FROM employee
+ WHERE login = '$form->{login}'))|;
+
+ $dbh->do($query) || $form->dberror($query);
+
+ $query = qq|SELECT id
+ FROM gl
+ WHERE reference = '$uid'|;
+
+ ($form->{id}) = $dbh->selectrow_array($query);
+
+ $query = qq|UPDATE gl
+ SET reference = |.$dbh->quote($form->{reference}).qq|,
+ description = |.$dbh->quote($form->{description}).qq|,
+ notes = |.$dbh->quote($form->{notes}).qq|,
+ transdate = '$form->{transdate}',
+ department_id = 0
+ WHERE id = $form->{id}|;
+
+ $dbh->do($query) || $form->dberror($query);
+
+ my $amount;
+ my $accno;
+
+ # insert acc_trans transactions
+ for my $i (1 .. $form->{rowcount}) {
+ # extract accno
+ ($accno) = split(/--/, $form->{"accno_$i"});
+ $amount = 0;
+
+ if ($form->{"credit_$i"}) {
+ $amount = $form->{"credit_$i"};
+ }
+
+ if ($form->{"debit_$i"}) {
+ $amount = $form->{"debit_$i"} * -1;
+ }
+
+
+ # if there is an amount, add the record
+ if ($amount) {
+ $query = qq|INSERT INTO acc_trans (trans_id, chart_id, amount, transdate, source)
+ VALUES ($form->{id}, (SELECT id
+ FROM chart
+ WHERE accno = '$accno'),
+ $amount, '$form->{transdate}', |
+ .$dbh->quote($form->{reference}).qq|)|;
+
+ $dbh->do($query) || $form->dberror($query);
+ }
+ }
+
+ $query = qq|INSERT INTO yearend (trans_id, transdate)
+ VALUES ($form->{id}, '$form->{transdate}')|;
+
+ $dbh->do($query) || $form->dberror($query);
+
+ my %audittrail = ( tablename => 'gl',
+ reference => $form->{reference},
+ formname => 'yearend',
+ action => 'posted',
+ id => $form->{id} );
+
+ $form->audittrail($dbh, "", \%audittrail);
+
+ # commit and redirect
+ my $rc = $dbh->commit;
+ $dbh->disconnect;
+
+ $rc;
+
+}
+
+
+1;
diff --git a/LedgerSMB/BP.pm b/LedgerSMB/BP.pm
new file mode 100755
index 00000000..c1feadc2
--- /dev/null
+++ b/LedgerSMB/BP.pm
@@ -0,0 +1,326 @@
+#=====================================================================
+# LedgerSMB
+# Small Medium Business Accounting software
+#
+# See COPYRIGHT file for copyright information
+#======================================================================
+#
+# This file has undergone whitespace cleanup.
+#
+#======================================================================
+#
+# Batch printing module backend routines
+#
+#======================================================================
+
+package BP;
+
+
+sub get_vc {
+
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ my %arap = ( invoice => ['ar'],
+ packing_list => ['oe', 'ar'],
+ sales_order => ['oe'],
+ work_order => ['oe'],
+ pick_list => ['oe', 'ar'],
+ purchase_order => ['oe'],
+ bin_list => ['oe'],
+ sales_quotation => ['oe'],
+ request_quotation => ['oe'],
+ timecard => ['jcitems'],);
+
+ my $query = "";
+ my $sth;
+ my $n;
+ my $count;
+ my $item;
+
+ foreach $item (@{ $arap{$form->{type}} }) {
+ $query = qq|SELECT count(*)
+ FROM (SELECT DISTINCT vc.id
+ FROM $form->{vc} vc, $item a, status s
+ WHERE a.$form->{vc}_id = vc.id
+ AND s.trans_id = a.id
+ AND s.formname = '$form->{type}'
+ AND s.spoolfile IS NOT NULL) AS total|;
+
+ ($n) = $dbh->selectrow_array($query);
+ $count += $n;
+ }
+
+ # build selection list
+ my $union = "";
+ $query = "";
+
+ if ($count < $myconfig->{vclimit}) {
+
+ foreach $item (@{ $arap{$form->{type}} }) {
+ $query .= qq| $union
+ SELECT DISTINCT vc.id, vc.name
+ FROM $item a
+ JOIN $form->{vc} vc ON (a.$form->{vc}_id = vc.id)
+ JOIN status s ON (s.trans_id = a.id)
+ WHERE s.formname = '$form->{type}'
+ AND s.spoolfile IS NOT NULL|;
+ $union = "UNION";
+ }
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
+ push @{ $form->{"all_$form->{vc}"} }, $ref;
+ }
+
+ $sth->finish;
+ }
+
+ $form->all_years($myconfig, $dbh);
+ $dbh->disconnect;
+
+}
+
+
+sub get_spoolfiles {
+
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ my $query;
+ my $invnumber = "invnumber";
+ my $item;
+
+ my %arap = ( invoice => ['ar'],
+ packing_list => ['oe', 'ar'],
+ sales_order => ['oe'],
+ work_order => ['oe'],
+ pick_list => ['oe', 'ar'],
+ purchase_order => ['oe'],
+ bin_list => ['oe'],
+ sales_quotation => ['oe'],
+ request_quotation => ['oe'],
+ timecard => ['jc'],);
+
+ ($form->{transdatefrom}, $form->{transdateto}) = $form->from_to($form->{year}, $form->{month}, $form->{interval}) if $form->{year} && $form->{month};
+
+ if ($form->{type} eq 'timecard') {
+ my $dateformat = $myconfig->{dateformat};
+ $dateformat =~ s/yy/yyyy/;
+ $dateformat =~ s/yyyyyy/yyyy/;
+
+ $invnumber = 'id';
+
+ $query = qq|SELECT j.id, e.name, j.id AS invnumber,
+ to_char(j.checkedin, '$dateformat') AS transdate,
+ '' AS ordnumber, '' AS quonumber, '0' AS invoice,
+ '$arap{$form->{type}}[0]' AS module, s.spoolfile
+ FROM jcitems j
+ JOIN employee e ON (e.id = j.employee_id)
+ JOIN status s ON (s.trans_id = j.id)
+ WHERE s.formname = '$form->{type}'
+ AND s.spoolfile IS NOT NULL|;
+
+ if ($form->{"$form->{vc}_id"}) {
+ $query .= qq| AND j.$form->{vc}_id = $form->{"$form->{vc}_id"}|;
+ } else {
+
+ if ($form->{$form->{vc}}) {
+ $item = $form->like(lc $form->{$form->{vc}});
+ $query .= " AND lower(e.name) LIKE '$item'";
+ }
+ }
+
+ $query .= " AND j.checkedin >= '$form->{transdatefrom}'" if $form->{transdatefrom};
+ $query .= " AND j.checkedin <= '$form->{transdateto}'" if $form->{transdateto};
+
+ } else {
+
+ foreach $item (@{ $arap{$form->{type}} }) {
+
+ $invoice = "a.invoice";
+ $invnumber = "invnumber";
+
+ if ($item eq 'oe') {
+ $invnumber = "ordnumber";
+ $invoice = "'0'";
+ }
+
+ $query .= qq| $union
+ SELECT a.id, vc.name, a.$invnumber AS invnumber, a.transdate,
+ a.ordnumber, a.quonumber, $invoice AS invoice,
+ '$item' AS module, s.spoolfile
+ FROM $item a, $form->{vc} vc, status s
+ WHERE s.trans_id = a.id
+ AND s.spoolfile IS NOT NULL
+ AND s.formname = '$form->{type}'
+ AND a.$form->{vc}_id = vc.id|;
+
+ if ($form->{"$form->{vc}_id"}) {
+ $query .= qq| AND a.$form->{vc}_id = $form->{"$form->{vc}_id"}|;
+ } else {
+
+ if ($form->{$form->{vc}} ne "") {
+ $item = $form->like(lc $form->{$form->{vc}});
+ $query .= " AND lower(vc.name) LIKE '$item'";
+ }
+ }
+
+ if ($form->{invnumber} ne "") {
+ $item = $form->like(lc $form->{invnumber});
+ $query .= " AND lower(a.invnumber) LIKE '$item'";
+ }
+
+ if ($form->{ordnumber} ne "") {
+ $item = $form->like(lc $form->{ordnumber});
+ $query .= " AND lower(a.ordnumber) LIKE '$item'";
+ }
+
+ if ($form->{quonumber} ne "") {
+ $item = $form->like(lc $form->{quonumber});
+ $query .= " AND lower(a.quonumber) LIKE '$item'";
+ }
+
+ $query .= " AND a.transdate >= '$form->{transdatefrom}'" if $form->{transdatefrom};
+ $query .= " AND a.transdate <= '$form->{transdateto}'" if $form->{transdateto};
+
+ $union = "UNION";
+
+ }
+ }
+
+ my %ordinal = ( 'name' => 2,
+ 'invnumber' => 3,
+ 'transdate' => 4,
+ 'ordnumber' => 5,
+ 'quonumber' => 6,);
+
+ my @a = ();
+ push @a, ("transdate", "$invnumber", "name");
+ my $sortorder = $form->sort_order(\@a, \%ordinal);
+ $query .= " ORDER by $sortorder";
+
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
+ push @{ $form->{SPOOL} }, $ref;
+ }
+
+ $sth->finish;
+ $dbh->disconnect;
+
+}
+
+
+sub delete_spool {
+
+ my ($self, $myconfig, $form, $spool) = @_;
+
+ # connect to database, turn AutoCommit off
+ my $dbh = $form->dbconnect_noauto($myconfig);
+
+ my $query;
+ my %audittrail;
+
+ $query = qq|UPDATE status
+ SET spoolfile = NULL
+ WHERE spoolfile = ?|;
+
+ my $sth = $dbh->prepare($query) || $form->dberror($query);
+
+ foreach my $i (1 .. $form->{rowcount}) {
+
+ if ($form->{"checked_$i"}) {
+ $sth->execute($form->{"spoolfile_$i"}) || $form->dberror($query);
+ $sth->finish;
+
+ %audittrail = ( tablename => $form->{module},
+ reference => $form->{"reference_$i"},
+ formname => $form->{type},
+ action => 'dequeued',
+ id => $form->{"id_$i"} );
+
+ $form->audittrail($dbh, "", \%audittrail);
+ }
+ }
+
+ # commit
+ my $rc = $dbh->commit;
+ $dbh->disconnect;
+
+ if ($rc) {
+ foreach my $i (1 .. $form->{rowcount}) {
+ $_ = qq|$spool/$form->{"spoolfile_$i"}|;
+ if ($form->{"checked_$i"}) {
+ unlink;
+ }
+ }
+ }
+
+ $rc;
+}
+
+
+sub print_spool {
+
+ my ($self, $myconfig, $form, $spool) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect_noauto($myconfig);
+
+ my %audittrail;
+
+ my $query = qq|UPDATE status
+ SET printed = '1'
+ WHERE spoolfile = ?|;
+
+ my $sth = $dbh->prepare($query) || $form->dberror($query);
+
+ foreach my $i (1 .. $form->{rowcount}) {
+
+ if ($form->{"checked_$i"}) {
+ open(OUT, $form->{OUT}) or $form->error("$form->{OUT} : $!");
+ binmode(OUT);
+
+ $spoolfile = qq|$spool/$form->{"spoolfile_$i"}|;
+
+ # send file to printer
+ open(IN, $spoolfile) or $form->error("$spoolfile : $!");
+ binmode(IN);
+
+ while (<IN>) {
+ print OUT $_;
+ }
+
+ close(IN);
+ close(OUT);
+
+ $sth->execute($form->{"spoolfile_$i"}) || $form->dberror($query);
+ $sth->finish;
+
+ %audittrail = ( tablename => $form->{module},
+ reference => $form->{"reference_$i"},
+ formname => $form->{type},
+ action => 'printed',
+ id => $form->{"id_$i"} );
+
+ $form->audittrail($dbh, "", \%audittrail);
+
+ $dbh->commit;
+ }
+ }
+
+ $dbh->disconnect;
+
+}
+
+
+1;
+
diff --git a/LedgerSMB/CA.pm b/LedgerSMB/CA.pm
new file mode 100755
index 00000000..d6df616d
--- /dev/null
+++ b/LedgerSMB/CA.pm
@@ -0,0 +1,378 @@
+#=====================================================================
+# LedgerSMB
+# Small Medium Business Accounting software
+#
+# See COPYRIGHT file for copyright information
+#======================================================================
+#
+# This file has undergone whitespace cleanup.
+#
+#======================================================================
+#
+# chart of accounts
+#
+#======================================================================
+
+
+package CA;
+
+
+sub all_accounts {
+
+ my ($self, $myconfig, $form) = @_;
+
+ my $amount = ();
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ my $query = qq|SELECT accno, SUM(acc_trans.amount) AS amount
+ FROM chart, acc_trans
+ WHERE chart.id = acc_trans.chart_id
+ GROUP BY accno|;
+
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
+ $amount{$ref->{accno}} = $ref->{amount}
+ }
+
+ $sth->finish;
+
+ $query = qq|SELECT accno, description
+ FROM gifi|;
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ my $gifi = ();
+
+ while (my ($accno, $description) = $sth->fetchrow_array) {
+ $gifi{$accno} = $description;
+ }
+
+ $sth->finish;
+
+ $query = qq|SELECT c.id, c.accno, c.description, c.charttype,
+ c.gifi_accno, c.category, c.link
+ FROM chart c
+ ORDER BY accno|;
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while (my $ca = $sth->fetchrow_hashref(NAME_lc)) {
+ $ca->{amount} = $amount{$ca->{accno}};
+ $ca->{gifi_description} = $gifi{$ca->{gifi_accno}};
+
+ if ($ca->{amount} < 0) {
+ $ca->{debit} = $ca->{amount} * -1;
+ } else {
+ $ca->{credit} = $ca->{amount};
+ }
+
+ push @{ $form->{CA} }, $ca;
+ }
+
+ $sth->finish;
+ $dbh->disconnect;
+
+}
+
+
+sub all_transactions {
+
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ # get chart_id
+ my $query = qq|SELECT id
+ FROM chart
+ WHERE accno = '$form->{accno}'|;
+
+ if ($form->{accounttype} eq 'gifi') {
+ $query = qq|SELECT id
+ FROM chart
+ WHERE gifi_accno = '$form->{gifi_accno}'|;
+ }
+
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ my @id = ();
+
+ while (my ($id) = $sth->fetchrow_array) {
+ push @id, $id;
+ }
+
+ $sth->finish;
+
+ my $fromdate_where;
+ my $todate_where;
+
+ ($form->{fromdate}, $form->{todate}) = $form->from_to($form->{year}, $form->{month}, $form->{interval}) if $form->{year} && $form->{month};
+
+ if ($form->{fromdate}) {
+ $fromdate_where = qq| AND ac.transdate >= '$form->{fromdate}' |;
+ }
+
+ if ($form->{todate}) {
+ $todate_where .= qq| AND ac.transdate <= '$form->{todate}' |;
+ }
+
+
+ my $false = ($myconfig->{dbdriver} =~ /Pg/) ? FALSE : q|'0'|;
+
+ # Oracle workaround, use ordinal positions
+ my %ordinal = ( transdate => 4,
+ reference => 2,
+ description => 3 );
+
+ my @a = qw(transdate reference description);
+ my $sortorder = $form->sort_order(\@a, \%ordinal);
+
+ my $null;
+ my $department_id;
+ my $dpt_where;
+ my $dpt_join;
+ my $union;
+
+ ($null, $department_id) = split /--/, $form->{department};
+
+ if ($department_id) {
+ $dpt_join = qq| JOIN department t ON (t.id = a.department_id) |;
+ $dpt_where = qq| AND t.id = $department_id |;
+ }
+
+
+ my $project;
+ my $project_id;
+
+ if ($form->{projectnumber}) {
+ ($null, $project_id) = split /--/, $form->{projectnumber};
+ $project = qq| AND ac.project_id = $project_id |;
+ }
+
+ if ($form->{accno} || $form->{gifi_accno}) {
+ # get category for account
+ $query = qq|SELECT description, category, link, contra
+ FROM chart
+ WHERE accno = '$form->{accno}'|;
+
+ if ($form->{accounttype} eq 'gifi') {
+ $query = qq|SELECT description, category, link, contra
+ FROM chart
+ WHERE gifi_accno = '$form->{gifi_accno}'
+ AND charttype = 'A'|;
+ }
+
+ ($form->{description}, $form->{category}, $form->{link}, $form->{contra}) = $dbh->selectrow_array($query);
+
+ if ($form->{fromdate}) {
+
+ if ($department_id) {
+
+ # get beginning balance
+ $query = "";
+ $union = "";
+
+ for (qw(ar ap gl)) {
+
+ if ($form->{accounttype} eq 'gifi') {
+ $query = qq| $union
+ SELECT SUM(ac.amount)
+ FROM acc_trans ac
+ JOIN $_ a ON (a.id = ac.trans_id)
+ JOIN chart c ON (ac.chart_id = c.id)
+ WHERE c.gifi_accno = '$form->{gifi_accno}'
+ AND ac.transdate < '$form->{fromdate}'
+ AND a.department_id = $department_id
+ $project |;
+
+ } else {
+
+ $query .= qq| $union
+ SELECT SUM(ac.amount)
+ FROM acc_trans ac
+ JOIN $_ a ON (a.id = ac.trans_id)
+ JOIN chart c ON (ac.chart_id = c.id)
+ WHERE c.accno = '$form->{accno}'
+ AND ac.transdate < '$form->{fromdate}'
+ AND a.department_id = $department_id
+ $project |;
+ }
+
+ $union = qq| UNION ALL |;
+ }
+
+ } else {
+
+ if ($form->{accounttype} eq 'gifi') {
+ $query = qq|SELECT SUM(ac.amount)
+ FROM acc_trans ac
+ JOIN chart c ON (ac.chart_id = c.id)
+ WHERE c.gifi_accno = '$form->{gifi_accno}'
+ AND ac.transdate < '$form->{fromdate}'
+ $project |;
+ } else {
+ $query = qq|SELECT SUM(ac.amount)
+ FROM acc_trans ac
+ JOIN chart c ON (ac.chart_id = c.id)
+ WHERE c.accno = '$form->{accno}'
+ AND ac.transdate < '$form->{fromdate}'
+ $project |;
+ }
+ }
+
+ ($form->{balance}) = $dbh->selectrow_array($query);
+
+ }
+ }
+
+ $query = "";
+ $union = "";
+
+ foreach my $id (@id) {
+
+ # get all transactions
+ $query .= qq|$union
+ SELECT a.id, a.reference, a.description, ac.transdate,
+ $false AS invoice, ac.amount, 'gl' as module, ac.cleared,
+ ac.source, '' AS till, ac.chart_id
+ FROM gl a
+ JOIN acc_trans ac ON (ac.trans_id = a.id)
+ $dpt_join
+ WHERE ac.chart_id = $id
+ $fromdate_where
+ $todate_where
+ $dpt_where
+ $project
+
+ UNION ALL
+
+ SELECT a.id, a.invnumber, c.name, ac.transdate,
+ a.invoice, ac.amount, 'ar' as module, ac.cleared,
+ ac.source,
+ a.till, ac.chart_id
+ FROM ar a
+ JOIN acc_trans ac ON (ac.trans_id = a.id)
+ JOIN customer c ON (a.customer_id = c.id)
+ $dpt_join
+ WHERE ac.chart_id = $id
+ $fromdate_where
+ $todate_where
+ $dpt_where
+ $project
+
+ UNION ALL
+
+ SELECT a.id, a.invnumber, v.name, ac.transdate,
+ a.invoice, ac.amount, 'ap' as module, ac.cleared,
+ ac.source, a.till, ac.chart_id
+ FROM ap a
+ JOIN acc_trans ac ON (ac.trans_id = a.id)
+ JOIN vendor v ON (a.vendor_id = v.id)
+ $dpt_join
+ WHERE ac.chart_id = $id
+ $fromdate_where
+ $todate_where
+ $dpt_where
+ $project |;
+
+ $union = qq| UNION ALL |;
+ }
+
+ $query .= qq| ORDER BY $sortorder |;
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ $query = qq|SELECT c.id, c.accno
+ FROM chart c
+ JOIN acc_trans ac ON (ac.chart_id = c.id)
+ WHERE ac.amount >= 0
+ AND (c.link = 'AR' OR c.link = 'AP')
+ AND ac.trans_id = ?|;
+
+ my $dr = $dbh->prepare($query) || $form->dberror($query);
+
+ $query = qq|SELECT c.id, c.accno
+ FROM chart c
+ JOIN acc_trans ac ON (ac.chart_id = c.id)
+ WHERE ac.amount < 0
+ AND (c.link = 'AR' OR c.link = 'AP')
+ AND ac.trans_id = ?|;
+
+ my $cr = $dbh->prepare($query) || $form->dberror($query);
+
+ my $accno;
+ my $chart_id;
+ my %accno;
+
+ while (my $ca = $sth->fetchrow_hashref(NAME_lc)) {
+
+ # gl
+ if ($ca->{module} eq "gl") {
+ $ca->{module} = "gl";
+ }
+
+ # ap
+ if ($ca->{module} eq "ap") {
+ $ca->{module} = ($ca->{invoice}) ? 'ir' : 'ap';
+ $ca->{module} = 'ps' if $ca->{till};
+ }
+
+ # ar
+ if ($ca->{module} eq "ar") {
+ $ca->{module} = ($ca->{invoice}) ? 'is' : 'ar';
+ $ca->{module} = 'ps' if $ca->{till};
+ }
+
+ if ($ca->{amount}) {
+ %accno = ();
+
+ if ($ca->{amount} < 0) {
+ $ca->{debit} = $ca->{amount} * -1;
+ $ca->{credit} = 0;
+ $dr->execute($ca->{id});
+ $ca->{accno} = ();
+
+ while (($chart_id, $accno) = $dr->fetchrow_array) {
+ $accno{$accno} = 1 if $chart_id ne $ca->{chart_id};
+ }
+
+ $dr->finish;
+
+ for (sort keys %accno) { push @{ $ca->{accno} }, "$_ " }
+
+ } else {
+
+ $ca->{credit} = $ca->{amount};
+ $ca->{debit} = 0;
+
+ $cr->execute($ca->{id});
+ $ca->{accno} = ();
+
+ while (($chart_id, $accno) = $cr->fetchrow_array) {
+ $accno{$accno} = 1 if $chart_id ne $ca->{chart_id};
+ }
+
+ $cr->finish;
+
+ for (keys %accno) { push @{ $ca->{accno} }, "$_ " }
+
+ }
+
+ push @{ $form->{CA} }, $ca;
+ }
+ }
+
+ $sth->finish;
+ $dbh->disconnect;
+
+}
+
+1;
+
diff --git a/LedgerSMB/CP.pm b/LedgerSMB/CP.pm
new file mode 100755
index 00000000..897783fe
--- /dev/null
+++ b/LedgerSMB/CP.pm
@@ -0,0 +1,684 @@
+#=====================================================================
+# LedgerSMB
+# Small Medium Business Accounting software
+#
+# See COPYRIGHT file for copyright information
+#======================================================================
+#
+# This file has undergone whitespace cleanup.
+#
+#======================================================================
+#
+# Check and receipt printing payment module backend routines
+# Number to text conversion routines are in
+# locale/{countrycode}/Num2text
+#
+#======================================================================
+
+package CP;
+
+
+sub new {
+
+ my ($type, $countrycode) = @_;
+
+ $self = {};
+
+ if ($countrycode) {
+
+ if (-f "locale/$countrycode/Num2text") {
+ require "locale/$countrycode/Num2text";
+ } else {
+ use LedgerSMB::Num2text;
+ }
+
+ } else {
+ use LedgerSMB::Num2text;
+ }
+
+ bless $self, $type;
+
+}
+
+
+sub paymentaccounts {
+
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ my $query = qq|SELECT accno, description, link
+ FROM chart
+ WHERE link LIKE '%$form->{ARAP}%'
+ ORDER BY accno|;
+
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ $form->{PR}{$form->{ARAP}} = ();
+ $form->{PR}{"$form->{ARAP}_paid"} = ();
+
+ while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
+
+ foreach my $item (split /:/, $ref->{link}) {
+
+ if ($item eq $form->{ARAP}) {
+ push @{ $form->{PR}{$form->{ARAP}} }, $ref;
+ }
+
+ if ($item eq "$form->{ARAP}_paid") {
+ push @{ $form->{PR}{"$form->{ARAP}_paid"} }, $ref;
+ }
+ }
+ }
+
+ $sth->finish;
+
+ # get currencies and closedto
+ $query = qq|SELECT curr, closedto, current_date
+ FROM defaults|;
+
+ ($form->{currencies}, $form->{closedto}, $form->{datepaid}) = $dbh->selectrow_array($query);
+
+ if ($form->{payment} eq 'payments') {
+ # get language codes
+ $query = qq|SELECT *
+ FROM language
+ ORDER BY 2|;
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $self->dberror($query);
+
+ $form->{all_language} = ();
+
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+ push @{ $form->{all_language} }, $ref;
+ }
+
+ $sth->finish;
+
+ $form->all_departments($myconfig, $dbh, $form->{vc});
+ }
+
+ $dbh->disconnect;
+
+}
+
+
+sub get_openvc {
+
+ my ($self, $myconfig, $form) = @_;
+
+ my $dbh = $form->dbconnect($myconfig);
+
+ my $arap = ($form->{vc} eq 'customer') ? 'ar' : 'ap';
+ my $query = qq|SELECT count(*)
+ FROM $form->{vc} ct, $arap a
+ WHERE a.$form->{vc}_id = ct.id
+ AND a.amount != a.paid|;
+
+ my ($count) = $dbh->selectrow_array($query);
+
+ my $sth;
+ my $ref;
+ my $i = 0;
+
+ my $where = qq|WHERE a.$form->{vc}_id = ct.id
+ AND a.amount != a.paid|;
+
+ if ($form->{$form->{vc}}) {
+ my $var = $form->like(lc $form->{$form->{vc}});
+ $where .= " AND lower(name) LIKE '$var'";
+ }
+
+ # build selection list
+ $query = qq|SELECT DISTINCT ct.*
+ FROM $form->{vc} ct, $arap a
+ $where
+ ORDER BY name|;
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+ $i++;
+ push @{ $form->{name_list} }, $ref;
+ }
+
+ $sth->finish;
+
+ $form->all_departments($myconfig, $dbh, $form->{vc});
+
+ # get language codes
+ $query = qq|SELECT *
+ FROM language
+ ORDER BY 2|;
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $self->dberror($query);
+
+ $form->{all_language} = ();
+
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+ push @{ $form->{all_language} }, $ref;
+ }
+
+ $sth->finish;
+
+ # get currency for first name
+ if (@{ $form->{name_list} }) {
+ $query = qq|SELECT curr
+ FROM $form->{vc}
+ WHERE id = $form->{name_list}->[0]->{id}|;
+
+ ($form->{currency}) = $dbh->selectrow_array($query);
+ $form->{currency} ||= $form->{defaultcurrency};
+ }
+
+ $dbh->disconnect;
+
+ $i;
+}
+
+
+sub get_openinvoices {
+
+ my ($self, $myconfig, $form) = @_;
+
+ my $null;
+ my $department_id;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ my $where = qq|WHERE a.$form->{vc}_id = $form->{"$form->{vc}_id"}
+ AND a.amount != a.paid|;
+
+ $where .= qq| AND a.curr = '$form->{currency}'| if $form->{currency};
+
+ my $sortorder = "transdate, invnumber";
+
+ my ($buysell);
+
+ if ($form->{vc} eq 'customer') {
+ $buysell = "buy";
+ } else {
+ $buysell = "sell";
+ }
+
+ if ($form->{payment} eq 'payments') {
+
+ $where = qq|WHERE a.amount != a.paid|;
+ $where .= qq| AND a.curr = '$form->{currency}'| if $form->{currency};
+
+ if ($form->{duedatefrom}) {
+ $where .= qq| AND a.duedate >= '$form->{duedatefrom}'|;
+ }
+
+ if ($form->{duedateto}) {
+ $where .= qq| AND a.duedate <= '$form->{duedateto}'|;
+ }
+
+ $sortorder = "name, transdate";
+ }
+
+
+ ($null, $department_id) = split /--/, $form->{department};
+
+ if ($department_id) {
+ $where .= qq| AND a.department_id = $department_id|;
+ }
+
+ my $query = qq|SELECT a.id, a.invnumber, a.transdate, a.amount, a.paid,
+ a.curr, c.name, a.$form->{vc}_id, c.language_code
+ FROM $form->{arap} a
+ JOIN $form->{vc} c ON (c.id = a.$form->{vc}_id)
+ $where
+ ORDER BY $sortorder|;
+
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ $query = qq|SELECT s.spoolfile
+ FROM status s
+ WHERE s.formname = '$form->{formname}'
+ AND s.trans_id = ?|;
+
+ my $vth = $dbh->prepare($query);
+
+ my $spoolfile;
+
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+
+ # if this is a foreign currency transaction get exchangerate
+ $ref->{exchangerate} = $form->get_exchangerate($dbh, $ref->{curr}, $ref->{transdate}, $buysell) if ($form->{currency} ne $form->{defaultcurrency});
+
+ $vth->execute($ref->{id});
+ $ref->{queue} = "";
+
+ while (($spoolfile) = $vth->fetchrow_array) {
+ $ref->{queued} .= "$form->{formname} $spoolfile ";
+ }
+
+ $vth->finish;
+ $ref->{queued} =~ s/ +$//g;
+
+ push @{ $form->{PR} }, $ref;
+ }
+
+ $sth->finish;
+ $dbh->disconnect;
+
+}
+
+
+
+sub post_payment {
+
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database, turn AutoCommit off
+ my $dbh = $form->dbconnect_noauto($myconfig);
+
+ my $sth;
+
+ my ($paymentaccno) = split /--/, $form->{account};
+
+ # if currency ne defaultcurrency update exchangerate
+ if ($form->{currency} ne $form->{defaultcurrency}) {
+
+ $form->{exchangerate} = $form->parse_amount($myconfig, $form->{exchangerate});
+
+ if ($form->{vc} eq 'customer') {
+ $form->update_exchangerate($dbh, $form->{currency}, $form->{datepaid}, $form->{exchangerate}, 0);
+ } else {
+ $form->update_exchangerate($dbh, $form->{currency}, $form->{datepaid}, 0, $form->{exchangerate});
+ }
+
+ } else {
+ $form->{exchangerate} = 1;
+ }
+
+ my $query = qq|SELECT fxgain_accno_id, fxloss_accno_id
+ FROM defaults|;
+
+ my ($fxgain_accno_id, $fxloss_accno_id) = $dbh->selectrow_array($query);
+
+ my ($buysell);
+
+ if ($form->{vc} eq 'customer') {
+ $buysell = "buy";
+ } else {
+ $buysell = "sell";
+ }
+
+ my $ml;
+ my $where;
+
+ if ($form->{ARAP} eq 'AR') {
+
+ $ml = 1;
+ $where = qq| (c.link = 'AR' OR c.link LIKE 'AR:%') |;
+
+ } else {
+
+ $ml = -1;
+ $where = qq| (c.link = 'AP' OR c.link LIKE '%:AP' OR c.link LIKE '%:AP:%') |;
+
+ }
+
+ my $paymentamount = $form->parse_amount($myconfig, $form->{amount});
+
+ # query to retrieve paid amount
+ $query = qq|SELECT paid
+ FROM $form->{arap}
+ WHERE id = ?
+ FOR UPDATE|;
+
+ my $pth = $dbh->prepare($query) || $form->dberror($query);
+
+ my %audittrail;
+
+ # go through line by line
+ for my $i (1 .. $form->{rowcount}) {
+
+ $form->{"paid_$i"} = $form->parse_amount($myconfig, $form->{"paid_$i"});
+ $form->{"due_$i"} = $form->parse_amount($myconfig, $form->{"due_$i"});
+
+ if ($form->{"checked_$i"} && $form->{"paid_$i"}) {
+
+ $paymentamount -= $form->{"paid_$i"};
+
+ # get exchangerate for original
+ $query = qq|SELECT $buysell
+ FROM exchangerate e
+ JOIN $form->{arap} a ON (a.transdate = e.transdate)
+ WHERE e.curr = '$form->{currency}'
+ AND a.id = $form->{"id_$i"}|;
+
+ my ($exchangerate) = $dbh->selectrow_array($query);
+
+ $exchangerate = 1 unless $exchangerate;
+
+ $query = qq|SELECT c.id
+ FROM chart c
+ JOIN acc_trans a ON (a.chart_id = c.id)
+ WHERE $where
+ AND a.trans_id = $form->{"id_$i"}|;
+
+ my ($id) = $dbh->selectrow_array($query);
+
+ $amount = $form->round_amount($form->{"paid_$i"} * $exchangerate, 2);
+
+ # add AR/AP
+ $query = qq|INSERT INTO acc_trans (trans_id, chart_id, transdate, amount)
+ VALUES ($form->{"id_$i"}, $id, '$form->{datepaid}', $amount * $ml)|;
+
+ $dbh->do($query) || $form->dberror($query);
+
+ # add payment
+ $query = qq|INSERT INTO acc_trans (trans_id, chart_id, transdate,
+ amount, source, memo)
+ VALUES ($form->{"id_$i"}, (SELECT id
+ FROM chart
+ WHERE accno = '$paymentaccno'),
+ '$form->{datepaid}', $form->{"paid_$i"} * $ml * -1, |
+ .$dbh->quote($form->{source}).qq|, |
+ .$dbh->quote($form->{memo}).qq|)|;
+
+ $dbh->do($query) || $form->dberror($query);
+
+ # add exchangerate difference if currency ne defaultcurrency
+ $amount = $form->round_amount($form->{"paid_$i"} * ($form->{exchangerate} - 1), 2);
+
+ if ($amount) {
+ # exchangerate difference
+ $query = qq|INSERT INTO acc_trans (trans_id, chart_id, transdate,
+ amount, cleared, fx_transaction, source)
+ VALUES ($form->{"id_$i"}, (SELECT id
+ FROM chart
+ WHERE accno = '$paymentaccno'),
+ '$form->{datepaid}', $amount * $ml * -1, '0', '1', |
+ .$dbh->quote($form->{source}).qq|)|;
+
+ $dbh->do($query) || $form->dberror($query);
+
+ # gain/loss
+ $amount = ($form->round_amount($form->{"paid_$i"} * $exchangerate,2) - $form->round_amount($form->{"paid_$i"} * $form->{exchangerate},2)) * $ml * -1;
+
+ if ($amount) {
+
+ my $accno_id = ($amount > 0) ? $fxgain_accno_id : $fxloss_accno_id;
+
+ $query = qq|INSERT INTO acc_trans (trans_id, chart_id, transdate,
+ amount, cleared, fx_transaction)
+ VALUES ($form->{"id_$i"}, $accno_id,
+ '$form->{datepaid}', $amount, '0', '1')|;
+
+ $dbh->do($query) || $form->dberror($query);
+ }
+ }
+
+ $form->{"paid_$i"} = $form->round_amount($form->{"paid_$i"} * $exchangerate, 2);
+
+ $pth->execute($form->{"id_$i"}) || $form->dberror;
+ ($amount) = $pth->fetchrow_array;
+ $pth->finish;
+
+ $amount += $form->{"paid_$i"};
+
+ # update AR/AP transaction
+ $query = qq|UPDATE $form->{arap}
+ SET paid = $amount,
+ datepaid = '$form->{datepaid}'
+ WHERE id = $form->{"id_$i"}|;
+
+ $dbh->do($query) || $form->dberror($query);
+
+ %audittrail = ( tablename => $form->{arap},
+ reference => $form->{source},
+ formname => $form->{formname},
+ action => 'posted',
+ id => $form->{"id_$i"} );
+
+ $form->audittrail($dbh, "", \%audittrail);
+
+ }
+ }
+
+
+ # record a AR/AP with a payment
+ if ($form->round_amount($paymentamount, 2)) {
+ $form->{invnumber} = "";
+ OP::overpayment("", $myconfig, $form, $dbh, $paymentamount, $ml, 1);
+ }
+
+ my $rc = $dbh->commit;
+ $dbh->disconnect;
+
+ $rc;
+
+}
+
+
+sub post_payments {
+
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database, turn AutoCommit off
+ my $dbh = $form->dbconnect_noauto($myconfig);
+
+ my $sth;
+
+ my ($paymentaccno) = split /--/, $form->{account};
+
+ # if currency ne defaultcurrency update exchangerate
+ if ($form->{currency} ne $form->{defaultcurrency}) {
+ $form->{exchangerate} = $form->parse_amount($myconfig, $form->{exchangerate});
+
+ if ($form->{vc} eq 'customer') {
+ $form->update_exchangerate($dbh, $form->{currency}, $form->{datepaid}, $form->{exchangerate}, 0);
+ } else {
+ $form->update_exchangerate($dbh, $form->{currency}, $form->{datepaid}, 0, $form->{exchangerate});
+ }
+
+ } else {
+ $form->{exchangerate} = 1;
+ }
+
+ my $query = qq|SELECT fxgain_accno_id, fxloss_accno_id
+ FROM defaults|;
+
+ my ($fxgain_accno_id, $fxloss_accno_id) = $dbh->selectrow_array($query);
+
+ my ($buysell);
+
+ if ($form->{vc} eq 'customer') {
+ $buysell = "buy";
+ } else {
+ $buysell = "sell";
+ }
+
+ my $ml;
+ my $where;
+
+ if ($form->{ARAP} eq 'AR') {
+
+ $ml = 1;
+ $where = qq| (c.link = 'AR' OR c.link LIKE 'AR:%') |;
+
+ } else {
+
+ $ml = -1;
+ $where = qq| (c.link = 'AP' OR c.link LIKE '%:AP' OR c.link LIKE '%:AP:%') |;
+
+ }
+
+ # get AR/AP account
+ $query = qq|SELECT c.accno
+ FROM chart c
+ JOIN acc_trans ac ON (ac.chart_id = c.id)
+ WHERE trans_id = ?
+ AND $where|;
+
+ my $ath = $dbh->prepare($query) || $form->dberror($query);
+
+ # query to retrieve paid amount
+ $query = qq|SELECT paid
+ FROM $form->{arap}
+ WHERE id = ?
+ FOR UPDATE|;
+
+ my $pth = $dbh->prepare($query) || $form->dberror($query);
+
+ my %audittrail;
+
+ my $overpayment = 0;
+ my $accno_id;
+
+ # go through line by line
+ for my $i (1 .. $form->{rowcount}) {
+
+ $ath->execute($form->{"id_$i"});
+ ($form->{$form->{ARAP}}) = $ath->fetchrow_array;
+ $ath->finish;
+
+ $form->{"paid_$i"} = $form->parse_amount($myconfig, $form->{"paid_$i"});
+ $form->{"due_$i"} = $form->parse_amount($myconfig, $form->{"due_$i"});
+
+ if ($form->{"$form->{vc}_id_$i"} ne $sameid) {
+ # record a AR/AP with a payment
+ if ($overpayment > 0 && $form->{$form->{ARAP}}) {
+ $form->{invnumber} = "";
+ OP::overpayment("", $myconfig, $form, $dbh, $overpayment, $ml, 1);
+ }
+
+ $overpayment = 0;
+ $form->{"$form->{vc}_id"} = $form->{"$form->{vc}_id_$i"};
+ for (qw(source memo)) { $form->{$_} = $form->{"${_}_$i"} }
+ }
+
+ if ($form->{"checked_$i"} && $form->{"paid_$i"}) {
+
+ $overpayment += ($form->{"paid_$i"} - $form->{"due_$i"});
+
+ # get exchangerate for original
+ $query = qq|SELECT $buysell
+ FROM exchangerate e
+ JOIN $form->{arap} a ON (a.transdate = e.transdate)
+ WHERE e.curr = '$form->{currency}'
+ AND a.id = $form->{"id_$i"}|;
+
+ my ($exchangerate) = $dbh->selectrow_array($query);
+
+ $exchangerate ||= 1;
+
+ $query = qq|SELECT c.id
+ FROM chart c
+ JOIN acc_trans a ON (a.chart_id = c.id)
+ WHERE $where
+ AND a.trans_id = $form->{"id_$i"}|;
+
+ my ($id) = $dbh->selectrow_array($query);
+
+ $paid = ($form->{"paid_$i"} > $form->{"due_$i"}) ? $form->{"due_$i"} : $form->{"paid_$i"};
+ $amount = $form->round_amount($paid * $exchangerate, 2);
+
+ # add AR/AP
+ $query = qq|INSERT INTO acc_trans (trans_id, chart_id, transdate, amount)
+ VALUES ($form->{"id_$i"}, $id, '$form->{datepaid}',
+ $amount * $ml)|;
+
+ $dbh->do($query) || $form->dberror($query);
+
+ $query = qq|SELECT id
+ FROM chart
+ WHERE accno = '$paymentaccno'|;
+
+ ($accno_id) = $dbh->selectrow_array($query);
+
+ # add payment
+ $query = qq|INSERT INTO acc_trans (trans_id, chart_id, transdate,
+ amount, source, memo)
+ VALUES ($form->{"id_$i"}, $accno_id, '$form->{datepaid}',
+ $paid * $ml * -1, |
+ .$dbh->quote($form->{source}).qq|, |
+ .$dbh->quote($form->{memo}).qq|)|;
+
+ $dbh->do($query) || $form->dberror($query);
+
+ # add exchangerate difference if currency ne defaultcurrency
+ $amount = $form->round_amount($paid * ($form->{exchangerate} - 1) * $ml * -1, 2);
+
+ if ($amount) {
+ # exchangerate difference
+ $query = qq|INSERT INTO acc_trans (trans_id, chart_id, transdate,
+ amount, source)
+ VALUES ($form->{"id_$i"}, $accno_id, '$form->{datepaid}',
+ $amount, |
+ .$dbh->quote($form->{source}).qq|)|;
+
+ $dbh->do($query) || $form->dberror($query);
+
+ # gain/loss
+ $amount = ($form->round_amount($paid * $exchangerate,2) - $form->round_amount($paid * $form->{exchangerate},2)) * $ml * -1;
+
+ if ($amount) {
+ $accno_id = ($amount > 0) ? $fxgain_accno_id : $fxloss_accno_id;
+
+ $query = qq|INSERT INTO acc_trans (trans_id, chart_id, transdate,
+ amount, fx_transaction)
+ VALUES ($form->{"id_$i"}, $accno_id,
+ '$form->{datepaid}', $amount, '1')|;
+
+ $dbh->do($query) || $form->dberror($query);
+ }
+ }
+
+ $paid = $form->round_amount($paid * $exchangerate, 2);
+
+ $pth->execute($form->{"id_$i"}) || $form->dberror;
+ ($amount) = $pth->fetchrow_array;
+ $pth->finish;
+
+ $amount += $paid;
+
+ # update AR/AP transaction
+ $query = qq|UPDATE $form->{arap}
+ SET paid = $amount,
+ datepaid = '$form->{datepaid}'
+ WHERE id = $form->{"id_$i"}|;
+
+ $dbh->do($query) || $form->dberror($query);
+
+ %audittrail = ( tablename => $form->{arap},
+ reference => $form->{source},
+ formname => $form->{formname},
+ action => 'posted',
+ id => $form->{"id_$i"} );
+
+ $form->audittrail($dbh, "", \%audittrail);
+
+ }
+
+ $sameid = $form->{"$form->{vc}_id_$i"};
+
+ }
+
+ # record a AR/AP with a payment
+ if ($overpayment > 0 && $form->{$form->{ARAP}}) {
+ $form->{invnumber} = "";
+ OP::overpayment("", $myconfig, $form, $dbh, $overpayment, $ml, 1);
+ }
+
+ my $rc = $dbh->commit;
+ $dbh->disconnect;
+
+ $rc;
+
+}
+
+
+1;
+
diff --git a/LedgerSMB/CT.pm b/LedgerSMB/CT.pm
new file mode 100755
index 00000000..a99c7292
--- /dev/null
+++ b/LedgerSMB/CT.pm
@@ -0,0 +1,1080 @@
+#=====================================================================
+# LedgerSMB
+# Small Medium Business Accounting software
+#
+# See COPYRIGHT file for copyright information
+#======================================================================
+#
+# This file has undergone whitespace cleanup.
+#
+#======================================================================
+#
+# backend code for customers and vendors
+#
+#======================================================================
+
+package CT;
+
+
+sub create_links {
+
+ my ($self, $myconfig, $form) = @_;
+
+ my $dbh = $form->dbconnect($myconfig);
+ my $query;
+ my $sth;
+ my $ref;
+ my $arap = ($form->{db} eq 'customer') ? "ar" : "ap";
+ my $ARAP = uc $arap;
+
+ if ($form->{id}) {
+ $query = qq|SELECT ct.*, b.description AS business, s.*,
+ e.name AS employee, g.pricegroup AS pricegroup,
+ l.description AS language, ct.curr
+ FROM $form->{db} ct
+ LEFT JOIN business b ON (ct.business_id = b.id)
+ LEFT JOIN shipto s ON (ct.id = s.trans_id)
+ LEFT JOIN employee e ON (ct.employee_id = e.id)
+ LEFT JOIN pricegroup g ON (g.id = ct.pricegroup_id)
+ LEFT JOIN language l ON (l.code = ct.language_code)
+ WHERE ct.id = $form->{id}|;
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ $ref = $sth->fetchrow_hashref(NAME_lc);
+ for (keys %$ref) { $form->{$_} = $ref->{$_} }
+ $sth->finish;
+
+ # check if it is orphaned
+ $query = qq|SELECT a.id
+ FROM $arap a
+ JOIN $form->{db} ct ON (a.$form->{db}_id = ct.id)
+ WHERE ct.id = $form->{id}
+
+ UNION
+
+ SELECT a.id
+ FROM oe a
+ JOIN $form->{db} ct ON (a.$form->{db}_id = ct.id)
+ WHERE ct.id = $form->{id}|;
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ unless ($sth->fetchrow_array) {
+ $form->{status} = "orphaned";
+ }
+
+ $sth->finish;
+
+ # get taxes for customer/vendor
+ $query = qq|SELECT c.accno
+ FROM chart c
+ JOIN $form->{db}tax t ON (t.chart_id = c.id)
+ WHERE t.$form->{db}_id = $form->{id}|;
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+ $form->{tax}{$ref->{accno}}{taxable} = 1;
+ }
+
+ $sth->finish;
+
+ } else {
+
+ ($form->{employee}, $form->{employee_id}) = $form->get_employee($dbh);
+
+ $query = qq|SELECT current_date FROM defaults|;
+ ($form->{startdate}) = $dbh->selectrow_array($query);
+
+ }
+
+ # get tax labels
+ $query = qq|SELECT DISTINCT c.accno, c.description
+ FROM chart c
+ JOIN tax t ON (t.chart_id = c.id)
+ WHERE c.link LIKE '%${ARAP}_tax%'
+ ORDER BY c.accno|;
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+ $form->{taxaccounts} .= "$ref->{accno} ";
+ $form->{tax}{$ref->{accno}}{description} = $ref->{description};
+ }
+
+ $sth->finish;
+ chop $form->{taxaccounts};
+
+
+ # get business types ## needs fixing, this is bad (SELECT * ...) with order by 2. Yuck
+ $query = qq|SELECT *
+ FROM business
+ ORDER BY 2|;
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+ push @{ $form->{all_business} }, $ref;
+ }
+
+ $sth->finish;
+
+ # employees/salespersons
+ $form->all_employees($myconfig, $dbh, undef, ($form->{vc} eq 'customer') ? 1 : 0);
+
+ # get language ## needs fixing, this is bad (SELECT * ...) with order by 2. Yuck
+ $query = qq|SELECT *
+ FROM language
+ ORDER BY 2|;
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+ push @{ $form->{all_language} }, $ref;
+ }
+
+ $sth->finish;
+
+ # get pricegroups ## needs fixing, this is bad (SELECT * ...) with order by 2. Yuck
+ $query = qq|SELECT *
+ FROM pricegroup
+ ORDER BY 2|;
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+ push @{ $form->{all_pricegroup} }, $ref;
+ }
+
+ $sth->finish;
+
+ # get currencies
+ $query = qq|SELECT curr AS currencies
+ FROM defaults|;
+
+ ($form->{currencies}) = $dbh->selectrow_array($query);
+
+ $dbh->disconnect;
+
+}
+
+
+sub save_customer {
+
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect_noauto($myconfig);
+ my $query;
+ my $sth;
+ my $null;
+
+ # remove double spaces
+ $form->{name} =~ s/ / /g;
+ # remove double minus and minus at the end
+ $form->{name} =~ s/--+/-/g;
+ $form->{name} =~ s/-+$//;
+
+ # assign value discount, terms, creditlimit
+ $form->{discount} = $form->parse_amount($myconfig, $form->{discount});
+ $form->{discount} /= 100;
+ $form->{terms} *= 1;
+ $form->{taxincluded} *= 1;
+ $form->{creditlimit} = $form->parse_amount($myconfig, $form->{creditlimit});
+
+
+ if ($form->{id}) {
+ $query = qq|DELETE FROM customertax
+ WHERE customer_id = $form->{id}|;
+
+ $dbh->do($query) || $form->dberror($query);
+
+ $query = qq|DELETE FROM shipto
+ WHERE trans_id = $form->{id}|;
+
+ $dbh->do($query) || $form->dberror($query);
+
+ $query = qq|SELECT id
+ FROM customer
+ WHERE id = $form->{id}|;
+
+ if (! $dbh->selectrow_array($query)) {
+ $query = qq|INSERT INTO customer (id)
+ VALUES ($form->{id})|;
+
+ $dbh->do($query) || $form->dberror($query);
+ }
+
+ # retrieve enddate
+ if ($form->{type} && $form->{enddate}) {
+ my $now;
+ $query = qq|SELECT enddate, current_date AS now FROM customer|;
+ ($form->{enddate}, $now) = $dbh->selectrow_array($query);
+ $form->{enddate} = $now if $form->{enddate} lt $now;
+ }
+
+ } else {
+ my $uid = localtime;
+ $uid .= "$$";
+
+ $query = qq|INSERT INTO customer (name)
+ VALUES ('$uid')|;
+
+ $dbh->do($query) || $form->dberror($query);
+
+ $query = qq|SELECT id
+ FROM customer
+ WHERE name = '$uid'|;
+
+ ($form->{id}) = $dbh->selectrow_array($query);
+
+ }
+
+ my $employee_id;
+ ($null, $employee_id) = split /--/, $form->{employee};
+ $employee_id *= 1;
+
+ my $pricegroup_id;
+ ($null, $pricegroup_id) = split /--/, $form->{pricegroup};
+ $pricegroup_id *= 1;
+
+ my $business_id;
+ ($null, $business_id) = split /--/, $form->{business};
+ $business_id *= 1;
+
+ my $language_code;
+ ($null, $language_code) = split /--/, $form->{language};
+
+ $form->{customernumber} = $form->update_defaults($myconfig, "customernumber", $dbh) if ! $form->{customernumber};
+
+ $query = qq|UPDATE customer
+ SET customernumber = |.$dbh->quote($form->{customernumber}).qq|,
+ name = |.$dbh->quote($form->{name}).qq|,
+ address1 = |.$dbh->quote($form->{address1}).qq|,
+ address2 = |.$dbh->quote($form->{address2}).qq|,
+ city = |.$dbh->quote($form->{city}).qq|,
+ state = |.$dbh->quote($form->{state}).qq|,
+ zipcode = |.$dbh->quote($form->{zipcode}).qq|,
+ country = |.$dbh->quote($form->{country}).qq|,
+ contact = |.$dbh->quote($form->{contact}).qq|,
+ phone = '$form->{phone}',
+ fax = '$form->{fax}',
+ email = '$form->{email}',
+ cc = '$form->{cc}',
+ bcc = '$form->{bcc}',
+ notes = |.$dbh->quote($form->{notes}).qq|,
+ discount = $form->{discount},
+ creditlimit = $form->{creditlimit},
+ terms = $form->{terms},
+ taxincluded = '$form->{taxincluded}',
+ business_id = $business_id,
+ taxnumber = |.$dbh->quote($form->{taxnumber}).qq|,
+ sic_code = '$form->{sic_code}',
+ iban = '$form->{iban}',
+ bic = '$form->{bic}',
+ employee_id = $employee_id,
+ pricegroup_id = $pricegroup_id,
+ language_code = '$language_code',
+ curr = '$form->{curr}',
+ startdate = |.$form->dbquote($form->{startdate}, SQL_DATE).qq|,
+ enddate = |.$form->dbquote($form->{enddate}, SQL_DATE).qq|
+ WHERE id = $form->{id}|;
+
+ $dbh->do($query) || $form->dberror($query);
+
+ # save taxes
+ foreach $item (split / /, $form->{taxaccounts}) {
+
+ if ($form->{"tax_$item"}) {
+ $query = qq|INSERT INTO customertax (customer_id, chart_id)
+ VALUES ($form->{id}, (SELECT id
+ FROM chart
+ WHERE accno = '$item'))|;
+
+ $dbh->do($query) || $form->dberror($query);
+ }
+ }
+
+ # add shipto
+ $form->add_shipto($dbh, $form->{id});
+
+ $dbh->commit;
+ $dbh->disconnect;
+}
+
+
+sub save_vendor {
+
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect_noauto($myconfig);
+
+ my $query;
+ my $sth;
+ my $null;
+
+ # remove double spaces
+ $form->{name} =~ s/ / /g;
+ # remove double minus and minus at the end
+ $form->{name} =~ s/--+/-/g;
+ $form->{name} =~ s/-+$//;
+
+ $form->{discount} = $form->parse_amount($myconfig, $form->{discount});
+ $form->{discount} /= 100;
+ $form->{terms} *= 1;
+ $form->{taxincluded} *= 1;
+ $form->{creditlimit} = $form->parse_amount($myconfig, $form->{creditlimit});
+
+
+ if ($form->{id}) {
+ $query = qq|DELETE FROM vendortax
+ WHERE vendor_id = $form->{id}|;
+
+ $dbh->do($query) || $form->dberror($query);
+
+ $query = qq|DELETE FROM shipto
+ WHERE trans_id = $form->{id}|;
+
+ $dbh->do($query) || $form->dberror($query);
+
+ $query = qq|SELECT id
+ FROM vendor
+ WHERE id = $form->{id}|;
+
+ if (! $dbh->selectrow_array($query)) {
+ $query = qq|INSERT INTO vendor (id)
+ VALUES ($form->{id})|;
+
+ $dbh->do($query) || $form->dberror($query);
+ }
+
+ # retrieve enddate
+ if ($form->{type} && $form->{enddate}) {
+ my $now;
+ $query = qq|SELECT enddate, current_date AS now FROM vendor|;
+ ($form->{enddate}, $now) = $dbh->selectrow_array($query);
+ $form->{enddate} = $now if $form->{enddate} lt $now;
+ }
+
+ } else {
+ my $uid = localtime;
+ $uid .= "$$";
+
+ $query = qq|INSERT INTO vendor (name)
+ VALUES ('$uid')|;
+
+ $dbh->do($query) || $form->dberror($query);
+
+ $query = qq|SELECT id
+ FROM vendor
+ WHERE name = '$uid'|;
+
+ ($form->{id}) = $dbh->selectrow_array($query);
+
+ }
+
+ my $employee_id;
+ ($null, $employee_id) = split /--/, $form->{employee};
+ $employee_id *= 1;
+
+ my $pricegroup_id;
+ ($null, $pricegroup_id) = split /--/, $form->{pricegroup};
+ $pricegroup_id *= 1;
+
+ my $business_id;
+ ($null, $business_id) = split /--/, $form->{business};
+ $business_id *= 1;
+
+ my $language_code;
+ ($null, $language_code) = split /--/, $form->{language};
+
+ $form->{vendornumber} = $form->update_defaults($myconfig, "vendornumber", $dbh) if ! $form->{vendornumber};
+
+ $query = qq|UPDATE vendor
+ SET vendornumber = |.$dbh->quote($form->{vendornumber}).qq|,
+ name = |.$dbh->quote($form->{name}).qq|,
+ address1 = |.$dbh->quote($form->{address1}).qq|,
+ address2 = |.$dbh->quote($form->{address2}).qq|,
+ city = |.$dbh->quote($form->{city}).qq|,
+ state = |.$dbh->quote($form->{state}).qq|,
+ zipcode = |.$dbh->quote($form->{zipcode}).qq|,
+ country = |.$dbh->quote($form->{country}).qq|,
+ contact = |.$dbh->quote($form->{contact}).qq|,
+ phone = '$form->{phone}',
+ fax = '$form->{fax}',
+ email = '$form->{email}',
+ cc = '$form->{cc}',
+ bcc = '$form->{bcc}',
+ notes = |.$dbh->quote($form->{notes}).qq|,
+ terms = $form->{terms},
+ discount = $form->{discount},
+ creditlimit = $form->{creditlimit},
+ taxincluded = '$form->{taxincluded}',
+ gifi_accno = '$form->{gifi_accno}',
+ business_id = $business_id,
+ taxnumber = |.$dbh->quote($form->{taxnumber}).qq|,
+ sic_code = '$form->{sic_code}',
+ iban = '$form->{iban}',
+ bic = '$form->{bic}',
+ employee_id = $employee_id,
+ language_code = '$language_code',
+ pricegroup_id = $pricegroup_id,
+ curr = '$form->{curr}',
+ startdate = |.$form->dbquote($form->{startdate}, SQL_DATE).qq|,
+ enddate = |.$form->dbquote($form->{enddate}, SQL_DATE).qq|
+ WHERE id = $form->{id}|;
+
+ $dbh->do($query) || $form->dberror($query);
+
+ # save taxes
+ foreach $item (split / /, $form->{taxaccounts}) {
+ if ($form->{"tax_$item"}) {
+ $query = qq|INSERT INTO vendortax (vendor_id, chart_id)
+ VALUES ($form->{id}, (SELECT id
+ FROM chart
+ WHERE accno = '$item'))|;
+
+ $dbh->do($query) || $form->dberror($query);
+ }
+ }
+
+ # add shipto
+ $form->add_shipto($dbh, $form->{id});
+
+ $dbh->commit;
+ $dbh->disconnect;
+
+}
+
+
+
+sub delete {
+
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ # delete customer/vendor
+ my $query = qq|DELETE FROM $form->{db}
+ WHERE id = $form->{id}|;
+
+ $dbh->do($query) || $form->dberror($query);
+
+ $dbh->disconnect;
+
+}
+
+
+sub search {
+
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ my $where = "1 = 1";
+ $form->{sort} = ($form->{sort}) ? $form->{sort} : "name";
+ my @a = qw(name);
+ my $sortorder = $form->sort_order(\@a);
+
+ my $var;
+ my $item;
+
+ @a = ("$form->{db}number");
+ push @a, qw(name contact city state zipcode country notes phone email);
+
+ if ($form->{employee}) {
+ $var = $form->like(lc $form->{employee});
+ $where .= " AND lower(e.name) LIKE '$var'";
+ }
+
+ foreach $item (@a) {
+
+ if ($form->{$item} ne "") {
+ $var = $form->like(lc $form->{$item});
+ $where .= " AND lower(ct.$item) LIKE '$var'";
+ }
+ }
+
+ if ($form->{address} ne "") {
+ $var = $form->like(lc $form->{address});
+ $where .= " AND (lower(ct.address1) LIKE '$var' OR lower(ct.address2) LIKE '$var')";
+ }
+
+ if ($form->{startdatefrom}) {
+ $where .= " AND ct.startdate >= '$form->{startdatefrom}'";
+ }
+
+ if ($form->{startdateto}) {
+ $where .= " AND ct.startdate <= '$form->{startdateto}'";
+ }
+
+ if ($form->{status} eq 'active') {
+ $where .= " AND ct.enddate IS NULL";
+ }
+
+ if ($form->{status} eq 'inactive') {
+ $where .= " AND ct.enddate <= current_date";
+ }
+
+ if ($form->{status} eq 'orphaned') {
+ $where .= qq| AND ct.id NOT IN (SELECT o.$form->{db}_id
+ FROM oe o, $form->{db} vc
+ WHERE vc.id = o.$form->{db}_id)|;
+
+ if ($form->{db} eq 'customer') {
+ $where .= qq| AND ct.id NOT IN (SELECT a.customer_id
+ FROM ar a, customer vc
+ WHERE vc.id = a.customer_id)|;
+ }
+
+ if ($form->{db} eq 'vendor') {
+ $where .= qq| AND ct.id NOT IN (SELECT a.vendor_id
+ FROM ap a, vendor vc
+ WHERE vc.id = a.vendor_id)|;
+ }
+
+ $form->{l_invnumber} = $form->{l_ordnumber} = $form->{l_quonumber} = "";
+ }
+
+
+ my $query = qq|SELECT ct.*, b.description AS business,
+ e.name AS employee, g.pricegroup, l.description AS language,
+ m.name AS manager
+ FROM $form->{db} ct
+ LEFT JOIN business b ON (ct.business_id = b.id)
+ LEFT JOIN employee e ON (ct.employee_id = e.id)
+ LEFT JOIN employee m ON (m.id = e.managerid)
+ LEFT JOIN pricegroup g ON (ct.pricegroup_id = g.id)
+ LEFT JOIN language l ON (l.code = ct.language_code)
+ WHERE $where|;
+
+ # redo for invoices, orders and quotations
+ if ($form->{l_transnumber} || $form->{l_invnumber} || $form->{l_ordnumber} || $form->{l_quonumber}) {
+
+ my ($ar, $union, $module);
+ $query = "";
+ my $transwhere;
+ my $openarap = "";
+ my $openoe = "";
+
+ if ($form->{open} || $form->{closed}) {
+ unless ($form->{open} && $form->{closed}) {
+ $openarap = " AND a.amount != a.paid" if $form->{open};
+ $openarap = " AND a.amount = a.paid" if $form->{closed};
+ $openoe = " AND o.closed = '0'" if $form->{open};
+ $openoe = " AND o.closed = '1'" if $form->{closed};
+ }
+ }
+
+ if ($form->{l_transnumber}) {
+
+ $ar = ($form->{db} eq 'customer') ? 'ar' : 'ap';
+ $module = $ar;
+
+ $transwhere = "";
+ $transwhere .= " AND a.transdate >= '$form->{transdatefrom}'" if $form->{transdatefrom};
+ $transwhere .= " AND a.transdate <= '$form->{transdateto}'" if $form->{transdateto};
+
+
+ $query = qq|SELECT ct.*, b.description AS business,
+ a.invnumber, a.ordnumber, a.quonumber, a.id AS invid,
+ '$ar' AS module, 'invoice' AS formtype,
+ (a.amount = a.paid) AS closed, a.amount, a.netamount,
+ e.name AS employee, m.name AS manager
+ FROM $form->{db} ct
+ JOIN $ar a ON (a.$form->{db}_id = ct.id)
+ LEFT JOIN business b ON (ct.business_id = b.id)
+ LEFT JOIN employee e ON (a.employee_id = e.id)
+ LEFT JOIN employee m ON (m.id = e.managerid)
+ WHERE $where
+ AND a.invoice = '0'
+ $transwhere
+ $openarap |;
+
+ $union = qq| UNION |;
+
+ }
+
+ if ($form->{l_invnumber}) {
+ $ar = ($form->{db} eq 'customer') ? 'ar' : 'ap';
+ $module = ($ar eq 'ar') ? 'is' : 'ir';
+
+ $transwhere = "";
+ $transwhere .= " AND a.transdate >= '$form->{transdatefrom}'" if $form->{transdatefrom};
+ $transwhere .= " AND a.transdate <= '$form->{transdateto}'" if $form->{transdateto};
+
+ $query .= qq|$union
+ SELECT ct.*, b.description AS business,
+ a.invnumber, a.ordnumber, a.quonumber, a.id AS invid,
+ '$module' AS module, 'invoice' AS formtype,
+ (a.amount = a.paid) AS closed, a.amount, a.netamount,
+ e.name AS employee, m.name AS manager
+ FROM $form->{db} ct
+ JOIN $ar a ON (a.$form->{db}_id = ct.id)
+ LEFT JOIN business b ON (ct.business_id = b.id)
+ LEFT JOIN employee e ON (a.employee_id = e.id)
+ LEFT JOIN employee m ON (m.id = e.managerid)
+ WHERE $where
+ AND a.invoice = '1'
+ $transwhere
+ $openarap |;
+
+ $union = qq| UNION|;
+
+ }
+
+ if ($form->{l_ordnumber}) {
+
+ $transwhere = "";
+ $transwhere .= " AND o.transdate >= '$form->{transdatefrom}'" if $form->{transdatefrom};
+ $transwhere .= " AND o.transdate <= '$form->{transdateto}'" if $form->{transdateto};
+
+ $query .= qq|$union
+ SELECT ct.*, b.description AS business,
+ ' ' AS invnumber, o.ordnumber, o.quonumber, o.id AS invid,
+ 'oe' AS module, 'order' AS formtype,
+ o.closed, o.amount, o.netamount,
+ e.name AS employee, m.name AS manager
+ FROM $form->{db} ct
+ JOIN oe o ON (o.$form->{db}_id = ct.id)
+ LEFT JOIN business b ON (ct.business_id = b.id)
+ LEFT JOIN employee e ON (o.employee_id = e.id)
+ LEFT JOIN employee m ON (m.id = e.managerid)
+ WHERE $where
+ AND o.quotation = '0'
+ $transwhere
+ $openoe |;
+
+ $union = qq| UNION|;
+
+ }
+
+ if ($form->{l_quonumber}) {
+
+ $transwhere = "";
+ $transwhere .= " AND o.transdate >= '$form->{transdatefrom}'" if $form->{transdatefrom};
+ $transwhere .= " AND o.transdate <= '$form->{transdateto}'" if $form->{transdateto};
+
+ $query .= qq|$union
+ SELECT ct.*, b.description AS business,
+ ' ' AS invnumber, o.ordnumber, o.quonumber, o.id AS invid,
+ 'oe' AS module, 'quotation' AS formtype,
+ o.closed, o.amount, o.netamount,
+ e.name AS employee, m.name AS manager
+ FROM $form->{db} ct
+ JOIN oe o ON (o.$form->{db}_id = ct.id)
+ LEFT JOIN business b ON (ct.business_id = b.id)
+ LEFT JOIN employee e ON (o.employee_id = e.id)
+ LEFT JOIN employee m ON (m.id = e.managerid)
+ WHERE $where
+ AND o.quotation = '1'
+ $transwhere
+ $openoe |;
+
+ }
+
+ $sortorder .= ", invid";
+ }
+
+ $query .= qq| ORDER BY $sortorder|;
+
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ # accounts
+ $query = qq|SELECT c.accno
+ FROM chart c
+ JOIN $form->{db}tax t ON (t.chart_id = c.id)
+ WHERE t.$form->{db}_id = ?|;
+
+ my $tth = $dbh->prepare($query);
+
+ while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
+ $tth->execute($ref->{id});
+
+ while (($item) = $tth->fetchrow_array) {
+ $ref->{taxaccount} .= "$item ";
+ }
+
+ $tth->finish;
+ chop $ref->{taxaccount};
+
+ $ref->{address} = "";
+
+ for (qw(address1 address2 city state zipcode country)) { $ref->{address} .= "$ref->{$_} " }
+ push @{ $form->{CT} }, $ref;
+ }
+
+ $sth->finish;
+ $dbh->disconnect;
+
+}
+
+
+sub get_history {
+
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ my $query;
+ my $where = "1 = 1";
+ $form->{sort} = "partnumber" unless $form->{sort};
+ my $sortorder = $form->{sort};
+ my %ordinal = ();
+ my $var;
+ my $table;
+
+ # setup ASC or DESC
+ $form->sort_order();
+
+ if ($form->{"$form->{db}number"} ne "") {
+ $var = $form->like(lc $form->{"$form->{db}number"});
+ $where .= " AND lower(ct.$form->{db}number) LIKE '$var'";
+ }
+
+ if ($form->{address} ne "") {
+ $var = $form->like(lc $form->{address});
+ $where .= " AND lower(ct.address1) LIKE '$var'";
+ }
+
+ for (qw(name contact email phone notes city state zipcode country)) {
+
+ if ($form->{$_} ne "") {
+ $var = $form->like(lc $form->{$_});
+ $where .= " AND lower(ct.$_) LIKE '$var'";
+ }
+ }
+
+ if ($form->{employee} ne "") {
+ $var = $form->like(lc $form->{employee});
+ $where .= " AND lower(e.name) LIKE '$var'";
+ }
+
+ $where .= " AND a.transdate >= '$form->{transdatefrom}'" if $form->{transdatefrom};
+ $where .= " AND a.transdate <= '$form->{transdateto}'" if $form->{transdateto};
+
+ if ($form->{open} || $form->{closed}) {
+
+ unless ($form->{open} && $form->{closed}) {
+
+ if ($form->{type} eq 'invoice') {
+ $where .= " AND a.amount != a.paid" if $form->{open};
+ $where .= " AND a.amount = a.paid" if $form->{closed};
+ } else {
+ $where .= " AND a.closed = '0'" if $form->{open};
+ $where .= " AND a.closed = '1'" if $form->{closed};
+ }
+ }
+ }
+
+ my $invnumber = 'invnumber';
+ my $deldate = 'deliverydate';
+ my $buysell;
+ my $sellprice = "sellprice";
+
+ if ($form->{db} eq 'customer') {
+ $buysell = "buy";
+
+ if ($form->{type} eq 'invoice') {
+ $where .= qq| AND a.invoice = '1' AND i.assemblyitem = '0'|;
+ $table = 'ar';
+ $sellprice = "fxsellprice";
+ } else {
+ $table = 'oe';
+
+ if ($form->{type} eq 'order') {
+ $invnumber = 'ordnumber';
+ $where .= qq| AND a.quotation = '0'|;
+ } else {
+ $invnumber = 'quonumber';
+ $where .= qq| AND a.quotation = '1'|;
+ }
+
+ $deldate = 'reqdate';
+ }
+ }
+
+ if ($form->{db} eq 'vendor') {
+
+ $buysell = "sell";
+
+ if ($form->{type} eq 'invoice') {
+
+ $where .= qq| AND a.invoice = '1' AND i.assemblyitem = '0'|;
+ $table = 'ap';
+ $sellprice = "fxsellprice";
+
+ } else {
+
+ $table = 'oe';
+
+ if ($form->{type} eq 'order') {
+ $invnumber = 'ordnumber';
+ $where .= qq| AND a.quotation = '0'|;
+ } else {
+ $invnumber = 'quonumber';
+ $where .= qq| AND a.quotation = '1'|;
+ }
+
+ $deldate = 'reqdate';
+ }
+ }
+
+ my $invjoin = qq| JOIN invoice i ON (i.trans_id = a.id)|;
+
+ if ($form->{type} eq 'order') {
+ $invjoin = qq| JOIN orderitems i ON (i.trans_id = a.id)|;
+ }
+
+ if ($form->{type} eq 'quotation') {
+ $invjoin = qq| JOIN orderitems i ON (i.trans_id = a.id)|;
+ $where .= qq| AND a.quotation = '1'|;
+ }
+
+
+ %ordinal = ( partnumber => 9,
+ description => 12,
+ "$deldate" => 16,
+ serialnumber => 17,
+ projectnumber => 18 );
+
+ $sortorder = "2 $form->{direction}, 1, 11, $ordinal{$sortorder} $form->{direction}";
+
+ $query = qq|SELECT ct.id AS ctid, ct.name, ct.address1,
+ ct.address2, ct.city, ct.state,
+ p.id AS pid, p.partnumber, a.id AS invid,
+ a.$invnumber, a.curr, i.description,
+ i.qty, i.$sellprice AS sellprice, i.discount,
+ i.$deldate, i.serialnumber, pr.projectnumber,
+ e.name AS employee, ct.zipcode, ct.country, i.unit,
+ (SELECT $buysell
+ FROM exchangerate ex
+ WHERE a.curr = ex.curr
+ AND a.transdate = ex.transdate) AS exchangerate
+ FROM $form->{db} ct
+ JOIN $table a ON (a.$form->{db}_id = ct.id)
+ $invjoin
+ JOIN parts p ON (p.id = i.parts_id)
+ LEFT JOIN project pr ON (pr.id = i.project_id)
+ LEFT JOIN employee e ON (e.id = a.employee_id)
+ WHERE $where
+ ORDER BY $sortorder|;
+
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
+ $ref->{address} = "";
+ $ref->{exchangerate} ||= 1;
+ for (qw(address1 address2 city state zipcode country)) { $ref->{address} .= "$ref->{$_} " }
+ $ref->{id} = $ref->{ctid};
+ push @{ $form->{CT} }, $ref;
+ }
+
+ $sth->finish;
+ $dbh->disconnect;
+
+}
+
+
+sub pricelist {
+
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ my $query;
+
+ if ($form->{db} eq 'customer') {
+ $query = qq|SELECT p.id, p.partnumber, p.description,
+ p.sellprice, pg.partsgroup, p.partsgroup_id,
+ m.pricebreak, m.sellprice,
+ m.validfrom, m.validto, m.curr
+ FROM partscustomer m
+ JOIN parts p ON (p.id = m.parts_id)
+ LEFT JOIN partsgroup pg ON (pg.id = p.partsgroup_id)
+ WHERE m.customer_id = $form->{id}
+ ORDER BY partnumber|;
+ }
+
+ if ($form->{db} eq 'vendor') {
+ $query = qq|SELECT p.id, p.partnumber AS sku, p.description,
+ pg.partsgroup, p.partsgroup_id,
+ m.partnumber, m.leadtime, m.lastcost, m.curr
+ FROM partsvendor m
+ JOIN parts p ON (p.id = m.parts_id)
+ LEFT JOIN partsgroup pg ON (pg.id = p.partsgroup_id)
+ WHERE m.vendor_id = $form->{id}
+ ORDER BY p.partnumber|;
+ }
+
+ my $sth;
+ my $ref;
+
+ if ($form->{id}) {
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+ push @{ $form->{all_partspricelist} }, $ref;
+ }
+
+ $sth->finish;
+ }
+
+ $query = qq|SELECT curr FROM defaults|;
+ ($form->{currencies}) = $dbh->selectrow_array($query);
+
+ $query = qq|SELECT id, partsgroup
+ FROM partsgroup
+ ORDER BY partsgroup|;
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $self->dberror($query);
+
+ $form->{all_partsgroup} = ();
+
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+ push @{ $form->{all_partsgroup} }, $ref;
+ }
+
+ $sth->finish;
+
+ $dbh->disconnect;
+
+}
+
+
+sub save_pricelist {
+
+ my ($self, $myconfig, $form) = @_;
+
+ my $dbh = $form->dbconnect_noauto($myconfig);
+
+ my $query = qq|DELETE FROM parts$form->{db}
+ WHERE $form->{db}_id = $form->{id}|;
+
+ $dbh->do($query) || $form->dberror($query);
+
+ foreach $i (1 .. $form->{rowcount}) {
+
+ if ($form->{"id_$i"}) {
+
+ if ($form->{db} eq 'customer') {
+
+ for (qw(pricebreak sellprice)) {
+ $form->{"${_}_$i"} = $form->parse_amount($myconfig, $form->{"${_}_$i"})
+ }
+
+ $query = qq|INSERT INTO parts$form->{db} (parts_id, customer_id,
+ pricebreak, sellprice,
+ validfrom, validto, curr)
+ VALUES ($form->{"id_$i"}, $form->{id},
+ $form->{"pricebreak_$i"}, $form->{"sellprice_$i"},|
+ .$form->dbquote($form->{"validfrom_$i"}, SQL_DATE) .qq|,|
+ .$form->dbquote($form->{"validto_$i"}, SQL_DATE) .qq|,
+ '$form->{"curr_$i"}')|;
+ } else {
+
+ for (qw(leadtime lastcost)) {
+ $form->{"${_}_$i"} = $form->parse_amount($myconfig, $form->{"${_}_$i"})
+ }
+
+ $query = qq|INSERT INTO parts$form->{db} (parts_id, vendor_id,
+ partnumber, lastcost,
+ leadtime, curr)
+ VALUES ($form->{"id_$i"}, $form->{id},
+ '$form->{"partnumber_$i"}', $form->{"lastcost_$i"},
+ $form->{"leadtime_$i"}, '$form->{"curr_$i"}')|;
+
+ }
+ $dbh->do($query) || $form->dberror($query);
+ }
+
+ }
+
+ $_ = $dbh->commit;
+ $dbh->disconnect;
+
+}
+
+
+
+sub retrieve_item {
+
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ my $i = $form->{rowcount};
+ my $var;
+ my $null;
+
+ my $where = "WHERE p.obsolete = '0'";
+
+ if ($form->{db} eq 'vendor') {
+ # parts, services, labor
+ $where .= " AND p.assembly = '0'";
+ }
+
+ if ($form->{db} eq 'customer') {
+ # parts, assemblies, services
+ $where .= " AND p.income_accno_id > 0";
+ }
+
+ if ($form->{"partnumber_$i"} ne "") {
+ $var = $form->like(lc $form->{"partnumber_$i"});
+ $where .= " AND lower(p.partnumber) LIKE '$var'";
+ }
+
+ if ($form->{"description_$i"} ne "") {
+ $var = $form->like(lc $form->{"description_$i"});
+ $where .= " AND lower(p.description) LIKE '$var'";
+ }
+
+ if ($form->{"partsgroup_$i"} ne "") {
+ ($null, $var) = split /--/, $form->{"partsgroup_$i"};
+ $var *= 1;
+ $where .= qq| AND p.partsgroup_id = $var|;
+ }
+
+
+ my $query = qq|SELECT p.id, p.partnumber, p.description, p.sellprice,
+ p.lastcost, p.unit, pg.partsgroup, p.partsgroup_id
+ FROM parts p
+ LEFT JOIN partsgroup pg ON (pg.id = p.partsgroup_id)
+ $where
+ ORDER BY partnumber|;
+
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+ my $ref;
+ $form->{item_list} = ();
+
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+ push @{ $form->{item_list} }, $ref;
+ }
+
+ $sth->finish;
+ $dbh->disconnect;
+}
+
+
+1;
+
diff --git a/LedgerSMB/Form.pm b/LedgerSMB/Form.pm
new file mode 100755
index 00000000..0fb59a1a
--- /dev/null
+++ b/LedgerSMB/Form.pm
@@ -0,0 +1,2942 @@
+#=====================================================================
+# LedgerSMB
+# Small Medium Business Accounting software
+#
+# See COPYRIGHT file for copyright information
+#======================================================================
+#
+# This file has undergone whitespace cleanup.
+#
+#======================================================================
+#
+# main package
+#
+#======================================================================
+
+package Form;
+
+
+sub new {
+
+ my $type = shift;
+
+ my $self = {};
+
+ read(STDIN, $_, $ENV{CONTENT_LENGTH});
+
+ if ($ENV{QUERY_STRING}) {
+ $_ = $ENV{QUERY_STRING};
+ }
+
+ if ($ARGV[0]) {
+ $_ = $ARGV[0];
+ }
+
+ %$self = split /[&=]/;
+ for (keys %$self) { $self->{$_} = unescape("", $self->{$_}) }
+
+ if (substr($self->{action}, 0, 1) !~ /( |\.)/) {
+ $self->{action} = lc $self->{action};
+ $self->{action} =~ s/( |-|,|\#|\/|\.$)/_/g;
+ }
+
+ $self->{menubar} = 1 if $self->{path} =~ /lynx/i;
+
+ $self->{version} = "2.6.17";
+ $self->{dbversion} = "2.6.12";
+
+ bless $self, $type;
+
+}
+
+
+sub debug {
+
+ my ($self, $file) = @_;
+
+ if ($file) {
+ open(FH, "> $file") or die $!;
+ for (sort keys %$self) { print FH "$_ = $self->{$_}\n" }
+ close(FH);
+ } else {
+ print "\n";
+ for (sort keys %$self) { print "$_ = $self->{$_}\n" }
+ }
+
+}
+
+
+sub escape {
+ my ($self, $str, $beenthere) = @_;
+
+ # for Apache 2 we escape strings twice
+ if (($ENV{SERVER_SIGNATURE} =~ /Apache\/2\.(\d+)\.(\d+)/) && !$beenthere) {
+ $str = $self->escape($str, 1) if $1 == 0 && $2 < 44;
+ }
+
+ $str =~ s/([^a-zA-Z0-9_.-])/sprintf("%%%02x", ord($1))/ge;
+ $str;
+
+}
+
+
+sub unescape {
+ my ($self, $str) = @_;
+
+ $str =~ tr/+/ /;
+ $str =~ s/\\$//;
+
+ $str =~ s/%([0-9a-fA-Z]{2})/pack("c",hex($1))/eg;
+ $str =~ s/\r?\n/\n/g;
+
+ $str;
+
+}
+
+
+sub quote {
+ my ($self, $str) = @_;
+
+ if ($str && ! ref($str)) {
+ $str =~ s/"/&quot;/g;
+ }
+
+ $str;
+
+}
+
+
+sub unquote {
+ my ($self, $str) = @_;
+
+ if ($str && ! ref($str)) {
+ $str =~ s/&quot;/"/g;
+ }
+
+ $str;
+
+}
+
+
+sub hide_form {
+ my $self = shift;
+
+ if (@_) {
+
+ for (@_) {
+ print qq|<input type="hidden" name="$_" value="|.$self->quote($self->{$_}).qq|" />\n|
+ }
+
+ } else {
+ delete $self->{header};
+
+ for (sort keys %$self) {
+ print qq|<input type="hidden" name="$_" value="|.$self->quote($self->{$_}).qq|" />\n|
+ }
+ }
+}
+
+
+sub error {
+
+ my ($self, $msg) = @_;
+
+ if ($ENV{HTTP_USER_AGENT}) {
+
+ $self->{msg} = $msg;
+ $self->{format} = "html";
+ $self->format_string(msg);
+
+ delete $self->{pre};
+
+ if (!$self->{header}) {
+ $self->header;
+ }
+
+ print qq|<body><h2 class="error:>Error!</h2> <p><b>$self->{msg}</b></body>|;
+
+ exit;
+
+ } else {
+
+ if ($self->{error_function}) {
+ &{ $self->{error_function} }($msg);
+ } else {
+ die "Error: $msg\n";
+ }
+ }
+}
+
+
+sub info {
+ my ($self, $msg) = @_;
+
+ if ($ENV{HTTP_USER_AGENT}) {
+ $msg =~ s/\n/<br>/g;
+
+ delete $self->{pre};
+
+ if (!$self->{header}) {
+ $self->header;
+ print qq| <body>|;
+ $self->{header} = 1;
+ }
+
+ print "<b>$msg</b>";
+
+ } else {
+
+ if ($self->{info_function}) {
+ &{ $self->{info_function} }($msg);
+ } else {
+ print "$msg\n";
+ }
+ }
+}
+
+
+sub numtextrows {
+
+ my ($self, $str, $cols, $maxrows) = @_;
+
+ my $rows = 0;
+
+ for (split /\n/, $str) {
+ $rows += int (((length) - 2)/$cols) + 1
+ }
+
+ $maxrows = $rows unless defined $maxrows;
+
+ return ($rows > $maxrows) ? $maxrows : $rows;
+
+}
+
+
+sub dberror {
+ my ($self, $msg) = @_;
+ $self->error("$msg\n".$DBI::errstr);
+}
+
+
+sub isblank {
+ my ($self, $name, $msg) = @_;
+ $self->error($msg) if $self->{$name} =~ /^\s*$/;
+}
+
+
+sub header {
+
+ my ($self, $init) = @_;
+
+ return if $self->{header};
+
+ my ($stylesheet, $favicon, $charset);
+
+ if ($ENV{HTTP_USER_AGENT}) {
+
+ if ($self->{stylesheet} && (-f "css/$self->{stylesheet}")) {
+ $stylesheet = qq|<link rel="stylesheet" href="css/$self->{stylesheet}" type="text/css" title="LedgerSMB stylesheet" />\n|;
+ }
+
+ if ($self->{favicon} && (-f "$self->{favicon}")) {
+ $favicon = qq|<link rel="icon" href="$self->{favicon}" type="image/x-icon" />
+ <link rel="shortcut icon" href="$self->{favicon}" type="image/x-icon" />\n|;
+ }
+
+ if ($self->{charset}) {
+ $charset = qq|<meta http-equiv="content-type" content="text/html; charset=$self->{charset}" />\n|;
+ }
+
+ $self->{titlebar} = ($self->{title}) ? "$self->{title} - $self->{titlebar}" : $self->{titlebar};
+
+ $self->set_cookie($init);
+
+ print qq|Content-Type: text/html\n\n
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+ <title>$self->{titlebar}</title>
+ <meta http-equiv="Pragma" content="no-cache" />
+ <meta http-equiv="Expires" content="-1" />
+ $favicon
+ $stylesheet
+ $charset
+ <meta name="robots" content="noindex,nofollow" />
+</head>
+
+ $self->{pre} \n|;
+ }
+
+ $self->{header} = 1;
+}
+
+
+sub set_cookie {
+
+ my ($self, $init) = @_;
+
+ $self->{timeout} = ($self->{timeout} > 0) ? $self->{timeout} : 3600;
+ my $t = ($self->{endsession}) ? time : time + $self->{timeout};
+
+ if ($ENV{HTTP_USER_AGENT}) {
+
+ my @d = split / +/, scalar gmtime($t);
+ my $today = "$d[0], $d[2]-$d[1]-$d[4] $d[3] GMT";
+
+ if ($init) {
+ $self->{sessionid} = time;
+ }
+
+ print qq|Set-Cookie: LedgerSMB-$self->{login}=$self->{sessionid}; expires=$today; path=/;\n| if $self->{login};
+ }
+}
+
+
+sub redirect {
+
+ my ($self, $msg) = @_;
+
+ if ($self->{callback}) {
+
+ my ($script, $argv) = split(/\?/, $self->{callback});
+ exec ("perl", $script, $argv);
+
+ } else {
+
+ $self->info($msg);
+ }
+}
+
+
+sub sort_columns {
+
+ my ($self, @columns) = @_;
+
+ if ($self->{sort}) {
+ if (@columns) {
+ @columns = grep !/^$self->{sort}$/, @columns;
+ splice @columns, 0, 0, $self->{sort};
+ }
+ }
+
+ @columns;
+}
+
+
+sub sort_order {
+
+ my ($self, $columns, $ordinal) = @_;
+
+ # setup direction
+ if ($self->{direction}) {
+
+ if ($self->{sort} eq $self->{oldsort}) {
+
+ if ($self->{direction} eq 'ASC') {
+ $self->{direction} = "DESC";
+ } else {
+ $self->{direction} = "ASC";
+ }
+ }
+
+ } else {
+
+ $self->{direction} = "ASC";
+ }
+
+ $self->{oldsort} = $self->{sort};
+
+ my @a = $self->sort_columns(@{$columns});
+
+ if (%$ordinal) {
+ $a[0] = ($ordinal->{$a[$_]}) ? "$ordinal->{$a[0]} $self->{direction}" : "$a[0] $self->{direction}";
+
+ for (1 .. $#a) {
+ $a[$_] = $ordinal->{$a[$_]} if $ordinal->{$a[$_]}
+ }
+
+ } else {
+ $a[0] .= " $self->{direction}";
+ }
+
+ $sortorder = join ',', @a;
+ $sortorder;
+}
+
+
+sub format_amount {
+
+ my ($self, $myconfig, $amount, $places, $dash) = @_;
+
+ if ($places =~ /\d+/) {
+ #$places = 4 if $places == 2;
+ $amount = $self->round_amount($amount, $places);
+ }
+
+ # is the amount negative
+ my $negative = ($amount < 0);
+
+ if ($amount) {
+
+ if ($myconfig->{numberformat}) {
+
+ my ($whole, $dec) = split /\./, "$amount";
+ $whole =~ s/-//;
+ $amount = join '', reverse split //, $whole;
+
+ if ($places) {
+ $dec .= "0" x $places;
+ $dec = substr($dec, 0, $places);
+ }
+
+ if ($myconfig->{numberformat} eq '1,000.00') {
+ $amount =~ s/\d{3,}?/$&,/g;
+ $amount =~ s/,$//;
+ $amount = join '', reverse split //, $amount;
+ $amount .= "\.$dec" if ($dec ne "");
+ }
+
+ if ($myconfig->{numberformat} eq "1'000.00") {
+ $amount =~ s/\d{3,}?/$&'/g;
+ $amount =~ s/'$//;
+ $amount = join '', reverse split //, $amount;
+ $amount .= "\.$dec" if ($dec ne "");
+ }
+
+ if ($myconfig->{numberformat} eq '1.000,00') {
+ $amount =~ s/\d{3,}?/$&./g;
+ $amount =~ s/\.$//;
+ $amount = join '', reverse split //, $amount;
+ $amount .= ",$dec" if ($dec ne "");
+ }
+
+ if ($myconfig->{numberformat} eq '1000,00') {
+ $amount = "$whole";
+ $amount .= ",$dec" if ($dec ne "");
+ }
+
+ if ($myconfig->{numberformat} eq '1000.00') {
+ $amount = "$whole";
+ $amount .= ".$dec" if ($dec ne "");
+ }
+
+ if ($dash =~ /-/) {
+ $amount = ($negative) ? "($amount)" : "$amount";
+ } elsif ($dash =~ /DRCR/) {
+ $amount = ($negative) ? "$amount DR" : "$amount CR";
+ } else {
+ $amount = ($negative) ? "-$amount" : "$amount";
+ }
+ }
+
+ } else {
+
+ if ($dash eq "0" && $places) {
+
+ if ($myconfig->{numberformat} eq '1.000,00') {
+ $amount = "0".","."0" x $places;
+ } else {
+ $amount = "0"."."."0" x $places;
+ }
+
+ } else {
+ $amount = ($dash ne "") ? "$dash" : "";
+ }
+ }
+
+ $amount;
+}
+
+
+sub parse_amount {
+
+ my ($self, $myconfig, $amount) = @_;
+
+ if (($myconfig->{numberformat} eq '1.000,00') ||
+ ($myconfig->{numberformat} eq '1000,00')) {
+
+ $amount =~ s/\.//g;
+ $amount =~ s/,/\./;
+ }
+
+ if ($myconfig->{numberformat} eq "1'000.00") {
+ $amount =~ s/'//g;
+ }
+
+ $amount =~ s/,//g;
+ return ($amount * 1);
+}
+
+
+sub round_amount {
+
+ my ($self, $amount, $places) = @_;
+
+ # $places = 4 if $places == 2;
+ my ($null, $dec) = split /\./, $amount;
+ $dec = length $dec;
+ $dec = ($dec > $places) ? $dec : $places;
+ my $adj = ($amount < 0) ? (1/10**($dec+2)) * -1 : (1/10**($dec+2));
+
+ if (($places * 1) >= 0) {
+ $amount = sprintf("%.${places}f", $amount + $adj) * 1;
+ } else {
+ $places *= -1;
+ $amount = sprintf("%.0f", $amount);
+ $amount = sprintf("%.f", $amount / (10 ** $places)) * (10 ** $places);
+ }
+
+ $amount;
+}
+
+
+sub parse_template {
+
+ my ($self, $myconfig, $userspath) = @_;
+
+ my ($chars_per_line, $lines_on_first_page, $lines_on_second_page) = (0, 0, 0);
+ my ($current_page, $current_line) = (1, 1);
+ my $pagebreak = "";
+ my $sum = 0;
+
+ my $subdir = "";
+ my $err = "";
+
+ my %include = ();
+ my $ok;
+
+ if ($self->{language_code}) {
+
+ if (-f "$self->{templates}/$self->{language_code}/$self->{IN}") {
+ open(IN, "$self->{templates}/$self->{language_code}/$self->{IN}") or $self->error("$self->{IN} : $!");
+ } else {
+ open(IN, "$self->{templates}/$self->{IN}") or $self->error("$self->{IN} : $!");
+ }
+
+ } else {
+ open(IN, "$self->{templates}/$self->{IN}") or $self->error("$self->{IN} : $!");
+ }
+
+ @_ = <IN>;
+ close(IN);
+
+ $self->{copies} = 1 if (($self->{copies} *= 1) <= 0);
+
+ # OUT is used for the media, screen, printer, email
+ # for postscript we store a copy in a temporary file
+ my $fileid = time;
+ my $tmpfile = $self->{IN};
+ $tmpfile =~ s/\./_$self->{fileid}./ if $self->{fileid};
+ $self->{tmpfile} = "$userspath/${fileid}_${tmpfile}";
+
+ if ($self->{format} =~ /(postscript|pdf)/ || $self->{media} eq 'email') {
+ $out = $self->{OUT};
+ $self->{OUT} = ">$self->{tmpfile}";
+ }
+
+ if ($self->{OUT}) {
+ open(OUT, "$self->{OUT}") or $self->error("$self->{OUT} : $!");
+
+ } else {
+ open(OUT, ">-") or $self->error("STDOUT : $!");
+ $self->header;
+ }
+
+ # first we generate a tmpfile
+ # read file and replace <%variable%>
+ while ($_ = shift) {
+
+ $par = "";
+ $var = $_;
+
+ # detect pagebreak block and its parameters
+ if (/<%pagebreak ([0-9]+) ([0-9]+) ([0-9]+)%>/) {
+ $chars_per_line = $1;
+ $lines_on_first_page = $2;
+ $lines_on_second_page = $3;
+
+ while ($_ = shift) {
+ last if (/<%end pagebreak%>/);
+ $pagebreak .= $_;
+ }
+ }
+
+ if (/<%foreach /) {
+
+ # this one we need for the count
+ chomp $var;
+ $var =~ s/.*?<%foreach (.+?)%>/$1/;
+ while ($_ = shift) {
+ last if (/<%end $var%>/);
+
+ # store line in $par
+ $par .= $_;
+ }
+
+ # display contents of $self->{number}[] array
+ for $i (0 .. $#{ $self->{$var} }) {
+
+ if ($var =~ /^(part|service)$/) {
+ next if $self->{$var}[$i] eq 'NULL';
+ }
+
+ # Try to detect whether a manual page break is necessary
+ # but only if there was a <%pagebreak ...%> block before
+
+ if ($var eq 'number' || $var eq 'part' || $var eq 'service') {
+
+ if ($chars_per_line && defined $self->{$var}) {
+
+ my $line;
+ my $lines = 0;
+ my @d = (description);
+ push @d, "itemnotes" if $self->{countitemnotes};
+
+ foreach my $item (@d) {
+
+ if ($self->{$item}[$i]) {
+
+ foreach $line (split /\r?\n/, $self->{$item}[$i]) {
+ $lines++;
+ $lines += int(length($line) / $chars_per_line);
+ }
+ }
+ }
+
+ my $lpp;
+
+ if ($current_page == 1) {
+ $lpp = $lines_on_first_page;
+ } else {
+ $lpp = $lines_on_second_page;
+ }
+
+ # Yes we need a manual page break
+ if (($current_line + $lines) > $lpp) {
+ my $pb = $pagebreak;
+
+ # replace the special variables <%sumcarriedforward%>
+ # and <%lastpage%>
+ my $psum = $self->format_amount($myconfig, $sum, 2);
+ $pb =~ s/<%sumcarriedforward%>/$psum/g;
+ $pb =~ s/<%lastpage%>/$current_page/g;
+
+ # only "normal" variables are supported here
+ # (no <%if, no <%foreach, no <%include)
+ $pb =~ s/<%(.+?)%>/$self->{$1}/g;
+
+ # page break block is ready to rock
+ print(OUT $pb);
+ $current_page++;
+ $current_line = 1;
+ $lines = 0;
+ }
+
+ $current_line += $lines;
+ }
+
+ $sum += $self->parse_amount($myconfig, $self->{linetotal}[$i]);
+ }
+
+ # don't parse par, we need it for each line
+ print OUT $self->format_line($par, $i);
+ }
+ next;
+ }
+
+ # if not comes before if!
+ if (/<%if not /) {
+
+ # check if it is not set and display
+ chop;
+ s/.*?<%if not (.+?)%>/$1/;
+
+ if (! $self->{$_}) {
+
+ while ($_ = shift) {
+ last if (/<%end /);
+
+ # store line in $par
+ $par .= $_;
+ }
+
+ $_ = $par;
+
+ } else {
+
+ while ($_ = shift) {
+ last if (/<%end /);
+ }
+
+ next;
+ }
+ }
+
+ if (/<%if /) {
+
+ # check if it is set and display
+ chop;
+ s/.*?<%if (.+?)%>/$1/;
+
+ if (/\s/) {
+ @a = split;
+ $ok = eval "$self->{$a[0]} $a[1] $a[2]";
+ } else {
+ $ok = $self->{$_};
+ }
+
+ if ($ok) {
+ while ($_ = shift) {
+ last if (/<%end /);
+ # store line in $par
+ $par .= $_;
+ }
+
+ $_ = $par;
+
+ } else {
+
+ while ($_ = shift) {
+ last if (/<%end /);
+ }
+
+ next;
+ }
+ }
+
+ # check for <%include filename%>
+ if (/<%include /) {
+
+ # get the filename
+ chomp $var;
+ $var =~ s/.*?<%include (.+?)%>/$1/;
+
+ # remove / .. for security reasons
+ $var =~ s/(\/|\.\.)//g;
+
+ # assume loop after 10 includes of the same file
+ next if ($include{$var} > 10);
+
+ unless (open(INC, "$self->{templates}/$self->{language_code}/$var")) {
+ $err = $!;
+ $self->cleanup;
+ $self->error("$self->{templates}/$self->{language_code}/$var : $err");
+ }
+
+ unshift(@_, <INC>);
+ close(INC);
+
+ $include{$var}++;
+
+ next;
+ }
+
+ print OUT $self->format_line($_);
+
+ }
+
+ close(OUT);
+
+ delete $self->{countitemnotes};
+
+ # Convert the tex file to postscript
+ if ($self->{format} =~ /(postscript|pdf)/) {
+
+ use Cwd;
+ $self->{cwd} = cwd();
+ $self->{tmpdir} = "$self->{cwd}/$userspath";
+
+ unless (chdir("$userspath")) {
+ $err = $!;
+ $self->cleanup;
+ $self->error("chdir : $err");
+ }
+
+ $self->{tmpfile} =~ s/$userspath\///g;
+
+ $self->{errfile} = $self->{tmpfile};
+ $self->{errfile} =~ s/tex$/err/;
+
+ my $r = 1;
+ if ($self->{format} eq 'postscript') {
+
+ system("latex --interaction=nonstopmode $self->{tmpfile} > $self->{errfile}");
+
+ while ($self->rerun_latex) {
+ system("latex --interaction=nonstopmode $self->{tmpfile} > $self->{errfile}");
+ last if ++$r > 4;
+ }
+
+ $self->{tmpfile} =~ s/tex$/dvi/;
+ $self->error($self->cleanup) if ! (-f $self->{tmpfile});
+
+ system("dvips $self->{tmpfile} -o -q");
+ $self->error($self->cleanup."dvips : $!") if ($?);
+ $self->{tmpfile} =~ s/dvi$/ps/;
+ }
+
+ if ($self->{format} eq 'pdf') {
+ system("pdflatex --interaction=nonstopmode $self->{tmpfile} > $self->{errfile}");
+
+ while ($self->rerun_latex) {
+ system("pdflatex --interaction=nonstopmode $self->{tmpfile} > $self->{errfile}");
+ last if ++$r > 4;
+ }
+
+ $self->{tmpfile} =~ s/tex$/pdf/;
+ $self->error($self->cleanup) if ! (-f $self->{tmpfile});
+ }
+ }
+
+
+ if ($self->{format} =~ /(postscript|pdf)/ || $self->{media} eq 'email') {
+
+ if ($self->{media} eq 'email') {
+
+ use LedgerSMB::Mailer;
+
+ my $mail = new Mailer;
+
+ for (qw(cc bcc subject message version format charset)) {
+ $mail->{$_} = $self->{$_}
+ }
+
+ $mail->{to} = qq|$self->{email}|;
+ $mail->{from} = qq|"$myconfig->{name}" <$myconfig->{email}>|;
+ $mail->{fileid} = "$fileid.";
+
+ # if we send html or plain text inline
+ if (($self->{format} =~ /(html|txt)/) &&
+ ($self->{sendmode} eq 'inline')) {
+
+ my $br = "";
+ $br = "<br>" if $self->{format} eq 'html';
+
+ $mail->{contenttype} = "text/$self->{format}";
+
+ $mail->{message} =~ s/\r?\n/$br\n/g;
+ $myconfig->{signature} =~ s/\\n/$br\n/g;
+ $mail->{message} .= "$br\n-- $br\n$myconfig->{signature}\n$br" if $myconfig->{signature};
+
+ unless (open(IN, $self->{tmpfile})) {
+ $err = $!;
+ $self->cleanup;
+ $self->error("$self->{tmpfile} : $err");
+ }
+
+ while (<IN>) {
+ $mail->{message} .= $_;
+ }
+
+ close(IN);
+
+ } else {
+
+ @{ $mail->{attachments} } = ($self->{tmpfile});
+
+ $myconfig->{signature} =~ s/\\n/\n/g;
+ $mail->{message} .= "\n-- \n$myconfig->{signature}" if $myconfig->{signature};
+
+ }
+
+ if ($err = $mail->send($out)) {
+ $self->cleanup;
+ $self->error($err);
+ }
+
+ } else {
+
+ $self->{OUT} = $out;
+
+ unless (open(IN, $self->{tmpfile})) {
+ $err = $!;
+ $self->cleanup;
+ $self->error("$self->{tmpfile} : $err");
+ }
+
+ binmode(IN);
+
+ $self->{copies} = 1 if $self->{media} =~ /(screen|email|queue)/;
+
+ chdir("$self->{cwd}");
+
+ for my $i (1 .. $self->{copies}) {
+ if ($self->{OUT}) {
+
+ unless (open(OUT, $self->{OUT})) {
+ $err = $!;
+ $self->cleanup;
+ $self->error("$self->{OUT} : $err");
+ }
+
+ } else {
+
+ # launch application
+ print qq|Content-Type: application/$self->{format}
+ Content-Disposition: attachment; filename="$self->{tmpfile}"\n\n|;
+
+ unless (open(OUT, ">-")) {
+ $err = $!;
+ $self->cleanup;
+ $self->error("STDOUT : $err");
+ }
+ }
+
+ binmode(OUT);
+
+ while (<IN>) {
+ print OUT $_;
+ }
+
+ close(OUT);
+ seek IN, 0, 0;
+ }
+
+ close(IN);
+ }
+
+ $self->cleanup;
+ }
+}
+
+
+sub format_line {
+
+ my $self = shift;
+
+ $_ = shift;
+ my $i = shift;
+
+ my $str;
+ my $newstr;
+ my $pos;
+ my $l;
+ my $lf;
+ my $line;
+ my $var = "";
+ my %a;
+ my $offset;
+ my $pad;
+ my $item;
+
+ while (/<%(.+?)%>/) {
+
+ %a = ();
+
+ foreach $item (split / /, $1) {
+ my ($key, $value) = split /=/, $item;
+
+ if ($value ne "") {
+ $a{$key} = $value;
+ } else {
+ $var = $item;
+ }
+ }
+
+ $str = (defined $i) ? $self->{$var}[$i] : $self->{$var};
+ $newstr = $str;
+
+ $self->{countitemnotes} = 1 if $var eq 'itemnotes';
+
+ $var = $1;
+ if ($var =~ /^if\s+not\s+/) {
+
+ if ($str) {
+
+ $var =~ s/if\s+not\s+//;
+ s/<%if\s+not\s+$var%>.*?(<%end\s+$var%>|$)//s;
+
+ } else {
+ s/<%$var%>//;
+ }
+
+ next;
+ }
+
+ if ($var =~ /^if\s+/) {
+
+ if ($str) {
+ s/<%$var%>//;
+ } else {
+ $var =~ s/if\s+//;
+ s/<%if\s+$var%>.*?(<%end\s+$var%>|$)//s;
+ }
+
+ next;
+ }
+
+ if ($var =~ /^end\s+/) {
+ s/<%$var%>//;
+ next;
+ }
+
+ if ($a{align} || $a{width} || $a{offset}) {
+
+ $newstr = "";
+ $offset = 0;
+ $lf = "";
+
+ foreach $str (split /\n/, $str) {
+
+ $line = $str;
+ $l = length $str;
+
+ do {
+
+ if (($pos = length $str) > $a{width}) {
+
+ if (($pos = rindex $str, " ", $a{width}) > 0) {
+ $line = substr($str, 0, $pos);
+ }
+
+ $pos = length $str if $pos == -1;
+ }
+
+ $l = length $line;
+
+ # pad left, right or center
+ $l = ($a{width} - $l);
+
+ $pad = " " x $l;
+
+ if ($a{align} =~ /right/i) {
+ $line = " " x $offset . $pad . $line;
+ }
+
+ if ($a{align} =~ /left/i) {
+ $line = " " x $offset . $line . $pad;
+ }
+
+ if ($a{align} =~ /center/i) {
+ $pad = " " x ($l/2);
+ $line = " " x $offset . $pad . $line;
+ $pad = " " x ($l/2);
+ $line .= $pad;
+ }
+
+ $newstr .= "$lf$line";
+
+ $str = substr($str, $pos + 1);
+ $line = $str;
+ $lf = "\n";
+
+ $offset = $a{offset};
+
+ } while ($str);
+ }
+ }
+
+ s/<%(.+?)%>/$newstr/;
+
+ }
+
+ $_;
+}
+
+
+sub cleanup {
+
+ my $self = shift;
+
+ chdir("$self->{tmpdir}");
+
+ my @err = ();
+
+ if (-f "$self->{errfile}") {
+ open(FH, "$self->{errfile}");
+ @err = <FH>;
+ close(FH);
+ }
+
+ if ($self->{tmpfile}) {
+ # strip extension
+ $self->{tmpfile} =~ s/\.\w+$//g;
+ my $tmpfile = $self->{tmpfile};
+ unlink(<$tmpfile.*>);
+ }
+
+ chdir("$self->{cwd}");
+
+ "@err";
+}
+
+
+sub rerun_latex {
+
+ my $self = shift;
+
+ my $a = 0;
+
+ if (-f "$self->{errfile}") {
+ open(FH, "$self->{errfile}");
+ $a = grep /(longtable Warning:|Warning:.*?LastPage)/, <FH>;
+ close(FH);
+ }
+
+ $a;
+}
+
+
+sub format_string {
+
+ my ($self, @fields) = @_;
+
+ my $format = $self->{format};
+
+ if ($self->{format} =~ /(postscript|pdf)/) {
+ $format = 'tex';
+ }
+
+ my %replace = ( 'order' => { html => [ '<', '>', '\n', '\r' ],
+ txt => [ '\n', '\r' ],
+ tex => [ quotemeta('\\'), '&', '\n',
+ '\r', '\$', '%', '_', '#',
+ quotemeta('^'), '{', '}', '<', '>',
+ '?' ],
+ utf => [ quotemeta('\\'), '&', quotemeta('\n'),
+ '\r', '\$', '%', '_', '#',
+ quotemeta('^'), '{', '}', '<', '>' ] },
+ html => { '<' => '&lt;', '>' => '&gt;',
+ '\n' => '<br />', '\r' => '<br />' },
+ txt => { '\n' => "\n", '\r' => "\r" },
+ tex => {'&' => '\&', '\$' => '\$', '%' => '\%', '_' => '\_',
+ '#' => '\#', quotemeta('^') => '\^\\', '{' => '\{', '}' => '\}',
+ '<' => '$<$', '>' => '$>$',
+ '\n' => '\newline ', '\r' => '\newline ',
+ '?' => '\pounds ', quotemeta('\\') => '/' } );
+
+ my $key;
+
+ foreach $key (@{ $replace{order}{$format} }) {
+ for (@fields) { $self->{$_} =~ s/$key/$replace{$format}{$key}/g }
+ }
+
+}
+
+
+sub datetonum {
+
+ my ($self, $myconfig, $date, $picture) = @_;
+
+ if ($date && $date =~ /\D/) {
+
+ if ($myconfig->{dateformat} =~ /^yy/) {
+ ($yy, $mm, $dd) = split /\D/, $date;
+ }
+
+ if ($myconfig->{dateformat} =~ /^mm/) {
+ ($mm, $dd, $yy) = split /\D/, $date;
+ }
+
+ if ($myconfig->{dateformat} =~ /^dd/) {
+ ($dd, $mm, $yy) = split /\D/, $date;
+ }
+
+ $dd *= 1;
+ $mm *= 1;
+ $yy += 2000 if length $yy == 2;
+
+ $dd = substr("0$dd", -2);
+ $mm = substr("0$mm", -2);
+
+ $date = "$yy$mm$dd";
+ }
+
+ $date;
+}
+
+
+sub add_date {
+
+ my ($self, $myconfig, $date, $repeat, $unit) = @_;
+
+ use Time::Local;
+
+ my $diff = 0;
+ my $spc = $myconfig->{dateformat};
+ $spc =~ s/\w//g;
+ $spc = substr($spc, 0, 1);
+
+ if ($date) {
+
+ if ($date =~ /\D/) {
+
+ if ($myconfig->{dateformat} =~ /^yy/) {
+ ($yy, $mm, $dd) = split /\D/, $date;
+ }
+
+ if ($myconfig->{dateformat} =~ /^mm/) {
+ ($mm, $dd, $yy) = split /\D/, $date;
+ }
+
+ if ($myconfig->{dateformat} =~ /^dd/) {
+ ($dd, $mm, $yy) = split /\D/, $date;
+ }
+
+ } else {
+ # ISO
+ ($yy, $mm, $dd) =~ /(....)(..)(..)/;
+ }
+
+ if ($unit eq 'days') {
+ $diff = $repeat * 86400;
+ }
+
+ if ($unit eq 'weeks') {
+ $diff = $repeat * 604800;
+ }
+
+ if ($unit eq 'months') {
+ $diff = $mm + $repeat;
+
+ my $whole = int($diff / 12);
+ $yy += $whole;
+
+ $mm = ($diff % 12) + 1;
+ $diff = 0;
+ }
+
+ if ($unit eq 'years') {
+ $yy++;
+ }
+
+ $mm--;
+
+ @t = localtime(timelocal(0,0,0,$dd,$mm,$yy) + $diff);
+
+ $t[4]++;
+ $mm = substr("0$t[4]",-2);
+ $dd = substr("0$t[3]",-2);
+ $yy = $t[5] + 1900;
+
+ if ($date =~ /\D/) {
+
+ if ($myconfig->{dateformat} =~ /^yy/) {
+ $date = "$yy$spc$mm$spc$dd";
+ }
+
+ if ($myconfig->{dateformat} =~ /^mm/) {
+ $date = "$mm$spc$dd$spc$yy";
+ }
+
+ if ($myconfig->{dateformat} =~ /^dd/) {
+ $date = "$dd$spc$mm$spc$yy";
+ }
+
+ } else {
+ $date = "$yy$mm$dd";
+ }
+ }
+
+ $date;
+}
+
+
+sub print_button {
+ my ($self, $button, $name) = @_;
+
+ print qq|<input class="submit" type="submit" name="action" value="$button->{$name}{value}" accesskey="$button->{$name}{key}" title="$button->{$name}{value} [Alt-$button->{$name}{key}]" />\n|;
+}
+
+
+# Database routines used throughout
+
+sub dbconnect {
+
+ my ($self, $myconfig) = @_;
+
+ # connect to database
+ my $dbh = DBI->connect($myconfig->{dbconnect}, $myconfig->{dbuser}, $myconfig->{dbpasswd}) or $self->dberror;
+
+ # set db options
+ if ($myconfig->{dboptions}) {
+ $dbh->do($myconfig->{dboptions}) || $self->dberror($myconfig->{dboptions});
+ }
+
+ $dbh;
+}
+
+
+sub dbconnect_noauto {
+
+ my ($self, $myconfig) = @_;
+
+ # connect to database
+ $dbh = DBI->connect($myconfig->{dbconnect}, $myconfig->{dbuser}, $myconfig->{dbpasswd}, {AutoCommit => 0}) or $self->dberror;
+
+ # set db options
+ if ($myconfig->{dboptions}) {
+ $dbh->do($myconfig->{dboptions});
+ }
+
+ $dbh;
+}
+
+
+sub dbquote {
+
+ my ($self, $var, $type) = @_;
+
+ # DBI does not return NULL for SQL_DATE if the date is empty
+ if ($type eq 'SQL_DATE') {
+ $_ = ($var) ? "'$var'" : "NULL";
+ }
+
+ if ($type eq 'SQL_INT') {
+ $_ = $var * 1;
+ }
+
+ $_;
+}
+
+
+sub update_balance {
+
+ my ($self, $dbh, $table, $field, $where, $value) = @_;
+
+ # if we have a value, go do it
+ if ($value) {
+ # retrieve balance from table
+ my $query = "SELECT $field FROM $table WHERE $where FOR UPDATE";
+ my ($balance) = $dbh->selectrow_array($query);
+
+ $balance += $value;
+ # update balance
+ $query = "UPDATE $table SET $field = $balance WHERE $where";
+ $dbh->do($query) || $self->dberror($query);
+ }
+}
+
+
+sub update_exchangerate {
+
+ my ($self, $dbh, $curr, $transdate, $buy, $sell) = @_;
+
+ # some sanity check for currency
+ return if ($curr eq "");
+
+ my $query = qq|SELECT curr
+ FROM exchangerate
+ WHERE curr = '$curr'
+ AND transdate = '$transdate'
+ FOR UPDATE|;
+
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $self->dberror($query);
+
+ my $set;
+
+ if ($buy && $sell) {
+ $set = "buy = $buy, sell = $sell";
+ } elsif ($buy) {
+ $set = "buy = $buy";
+ } elsif ($sell) {
+ $set = "sell = $sell";
+ }
+
+ if ($sth->fetchrow_array) {
+ $query = qq|UPDATE exchangerate
+ SET $set
+ WHERE curr = '$curr'
+ AND transdate = '$transdate'|;
+
+ } else {
+ $query = qq|INSERT INTO exchangerate (curr, buy, sell, transdate)
+ VALUES ('$curr', $buy, $sell, '$transdate')|;
+ }
+
+ $sth->finish;
+ $dbh->do($query) || $self->dberror($query);
+
+}
+
+
+sub save_exchangerate {
+
+ my ($self, $myconfig, $currency, $transdate, $rate, $fld) = @_;
+
+ my $dbh = $self->dbconnect($myconfig);
+
+ my ($buy, $sell) = (0, 0);
+ $buy = $rate if $fld eq 'buy';
+ $sell = $rate if $fld eq 'sell';
+
+ $self->update_exchangerate($dbh, $currency, $transdate, $buy, $sell);
+
+ $dbh->disconnect;
+}
+
+
+sub get_exchangerate {
+
+ my ($self, $dbh, $curr, $transdate, $fld) = @_;
+
+ my $exchangerate = 1;
+
+ if ($transdate) {
+ my $query = qq|SELECT $fld
+ FROM exchangerate
+ WHERE curr = '$curr'
+ AND transdate = '$transdate'|;
+
+ ($exchangerate) = $dbh->selectrow_array($query);
+ }
+
+ $exchangerate;
+}
+
+
+sub check_exchangerate {
+
+ my ($self, $myconfig, $currency, $transdate, $fld) = @_;
+
+ return "" unless $transdate;
+
+ my $dbh = $self->dbconnect($myconfig);
+
+ my $query = qq|SELECT $fld
+ FROM exchangerate
+ WHERE curr = '$currency'
+ AND transdate = '$transdate'|;
+
+ my ($exchangerate) = $dbh->selectrow_array($query);
+
+ $dbh->disconnect;
+
+ $exchangerate;
+}
+
+
+sub add_shipto {
+ my ($self, $dbh, $id) = @_;
+
+ my $shipto;
+
+ foreach my $item (qw(name address1 address2 city state
+ zipcode country contact phone fax email)) {
+
+ if ($self->{"shipto$item"} ne "") {
+ $shipto = 1 if ($self->{$item} ne $self->{"shipto$item"});
+ }
+ }
+
+ if ($shipto) {
+ my $query = qq|INSERT INTO shipto (trans_id, shiptoname, shiptoaddress1,
+ shiptoaddress2, shiptocity, shiptostate,
+ shiptozipcode, shiptocountry, shiptocontact,
+ shiptophone, shiptofax, shiptoemail)
+ VALUES ($id, |
+ .$dbh->quote($self->{shiptoname}).qq|, |
+ .$dbh->quote($self->{shiptoaddress1}).qq|, |
+ .$dbh->quote($self->{shiptoaddress2}).qq|, |
+ .$dbh->quote($self->{shiptocity}).qq|, |
+ .$dbh->quote($self->{shiptostate}).qq|, |
+ .$dbh->quote($self->{shiptozipcode}).qq|, |
+ .$dbh->quote($self->{shiptocountry}).qq|, |
+ .$dbh->quote($self->{shiptocontact}).qq|,
+ '$self->{shiptophone}', '$self->{shiptofax}',
+ '$self->{shiptoemail}')|;
+
+ $dbh->do($query) || $self->dberror($query);
+ }
+}
+
+
+sub get_employee {
+ my ($self, $dbh) = @_;
+
+ my $login = $self->{login};
+ $login =~ s/@.*//;
+
+ my $query = qq|SELECT name, id
+ FROM employee
+ WHERE login = '$login'|;
+
+ my (@a) = $dbh->selectrow_array($query);
+ $a[1] *= 1;
+
+ @a;
+}
+
+
+# this sub gets the id and name from $table
+sub get_name {
+
+ my ($self, $myconfig, $table, $transdate) = @_;
+
+ # connect to database
+ my $dbh = $self->dbconnect($myconfig);
+
+ my $where;
+ if ($transdate) {
+ $where = qq|AND (startdate IS NULL OR startdate <= '$transdate')
+ AND (enddate IS NULL OR enddate >= '$transdate')|;
+ }
+
+ my $name = $self->like(lc $self->{$table});
+
+ my $query = qq|SELECT *
+ FROM $table
+ WHERE (lower(name) LIKE '$name'
+ OR ${table}number LIKE '$name')
+ $where
+ ORDER BY name|;
+
+ my $sth = $dbh->prepare($query);
+
+ $sth->execute || $self->dberror($query);
+
+ my $i = 0;
+ @{ $self->{name_list} } = ();
+
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+ push(@{ $self->{name_list} }, $ref);
+ $i++;
+ }
+
+ $sth->finish;
+ $dbh->disconnect;
+
+ $i;
+
+}
+
+
+sub all_vc {
+
+ my ($self, $myconfig, $vc, $module, $dbh, $transdate, $job) = @_;
+
+ my $ref;
+ my $disconnect = 0;
+
+ if (! $dbh) {
+ $dbh = $self->dbconnect($myconfig);
+ $disconnect = 1;
+ }
+
+ my $sth;
+
+ my $query = qq|SELECT count(*) FROM $vc|;
+ my $where;
+
+ if ($transdate) {
+ $where = qq|AND (startdate IS NULL OR startdate <= '$transdate')
+ AND (enddate IS NULL OR enddate >= '$transdate')|;
+
+ $query .= qq| WHERE 1=1 $where|;
+ }
+
+ my ($count) = $dbh->selectrow_array($query);
+
+ # build selection list
+ if ($count < $myconfig->{vclimit}) {
+
+ $self->{"${vc}_id"} *= 1;
+
+ $query = qq|SELECT id, name
+ FROM $vc
+ WHERE 1=1
+ $where
+
+ UNION
+
+ SELECT id,name
+ FROM $vc
+ WHERE id = $self->{"${vc}_id"}
+ ORDER BY name|;
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $self->dberror($query);
+
+ @{ $self->{"all_$vc"} } = ();
+
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+ push @{ $self->{"all_$vc"} }, $ref;
+ }
+
+ $sth->finish;
+
+ }
+
+ # get self
+ if (! $self->{employee_id}) {
+ ($self->{employee}, $self->{employee_id}) = split /--/, $self->{employee};
+ ($self->{employee}, $self->{employee_id}) = $self->get_employee($dbh) unless $self->{employee_id};
+ }
+
+ $self->all_employees($myconfig, $dbh, $transdate, 1);
+
+ $self->all_departments($myconfig, $dbh, $vc);
+
+ $self->all_projects($myconfig, $dbh, $transdate, $job);
+
+ # get language codes
+ $query = qq|SELECT *
+ FROM language
+ ORDER BY 2|;
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $self->dberror($query);
+
+ $self->{all_language} = ();
+
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+ push @{ $self->{all_language} }, $ref;
+ }
+
+ $sth->finish;
+ $self->all_taxaccounts($myconfig, $dbh, $transdate);
+ $dbh->disconnect if $disconnect;
+}
+
+
+sub all_taxaccounts {
+
+ my ($self, $myconfig, $dbh, $transdate) = @_;
+
+ my $disconnect = ($dbh) ? 0 : 1;
+
+ if (! $dbh) {
+ $dbh = $self->dbconnect($myconfig);
+ }
+
+ my $sth;
+ my $query;
+ my $where;
+
+
+ if ($transdate) {
+ $where = qq| AND (t.validto >= '$transdate' OR t.validto IS NULL)|;
+ }
+
+ if ($self->{taxaccounts}) {
+
+ # rebuild tax rates
+ $query = qq|SELECT t.rate, t.taxnumber
+ FROM tax t
+ JOIN chart c ON (c.id = t.chart_id)
+ WHERE c.accno = ?
+ $where
+ ORDER BY accno, validto|;
+
+ $sth = $dbh->prepare($query) || $self->dberror($query);
+
+ foreach my $accno (split / /, $self->{taxaccounts}) {
+ $sth->execute($accno);
+ ($self->{"${accno}_rate"}, $self->{"${accno}_taxnumber"}) = $sth->fetchrow_array;
+ $sth->finish;
+ }
+ }
+
+ $dbh->disconnect if $disconnect;
+}
+
+
+sub all_employees {
+
+ my ($self, $myconfig, $dbh, $transdate, $sales) = @_;
+
+ # setup employees/sales contacts
+ my $query = qq|SELECT id, name
+ FROM employee
+ WHERE 1 = 1|;
+
+ if ($transdate) {
+ $query .= qq| AND (startdate IS NULL OR startdate <= '$transdate')
+ AND (enddate IS NULL OR enddate >= '$transdate')|;
+ } else {
+ $query .= qq| AND enddate IS NULL|;
+ }
+
+ if ($sales) {
+ $query .= qq| AND sales = '1'|;
+ }
+
+ $query .= qq| ORDER BY name|;
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $self->dberror($query);
+
+ while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
+ push @{ $self->{all_employee} }, $ref;
+ }
+
+ $sth->finish;
+}
+
+
+
+sub all_projects {
+
+ my ($self, $myconfig, $dbh, $transdate, $job) = @_;
+
+ my $disconnect = 0;
+
+ if (! $dbh) {
+ $dbh = $self->dbconnect($myconfig);
+ $disconnect = 1;
+ }
+
+ my $where = "1 = 1";
+
+ $where = qq|id NOT IN (SELECT id
+ FROM parts
+ WHERE project_id > 0)| if ! $job;
+
+ my $query = qq|SELECT *
+ FROM project
+ WHERE $where|;
+
+ if ($form->{language_code}) {
+
+ $query = qq|SELECT pr.*, t.description AS translation
+ FROM project pr
+ LEFT JOIN translation t ON (t.trans_id = pr.id)
+ WHERE t.language_code = '$form->{language_code}'|;
+ }
+
+ if ($transdate) {
+ $query .= qq| AND (startdate IS NULL OR startdate <= '$transdate')
+ AND (enddate IS NULL OR enddate >= '$transdate')|;
+ }
+
+ $query .= qq| ORDER BY projectnumber|;
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $self->dberror($query);
+
+ @{ $self->{all_project} } = ();
+
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+ push @{ $self->{all_project} }, $ref;
+ }
+
+ $sth->finish;
+ $dbh->disconnect if $disconnect;
+}
+
+
+sub all_departments {
+
+ my ($self, $myconfig, $dbh, $vc) = @_;
+
+ my $disconnect = 0;
+ if (! $dbh) {
+ $dbh = $self->dbconnect($myconfig);
+ $disconnect = 1;
+ }
+
+ my $where = "1 = 1";
+
+ if ($vc) {
+ if ($vc eq 'customer') {
+ $where = " role = 'P'";
+ }
+ }
+
+ my $query = qq|SELECT id, description
+ FROM department
+ WHERE $where
+ ORDER BY 2|;
+
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $self->dberror($query);
+
+ @{ $self->{all_department} } = ();
+
+ while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
+ push @{ $self->{all_department} }, $ref;
+ }
+
+ $sth->finish;
+ $self->all_years($myconfig, $dbh);
+ $dbh->disconnect if $disconnect;
+}
+
+
+sub all_years {
+
+ my ($self, $myconfig, $dbh) = @_;
+
+ my $disconnect = 0;
+
+ if (! $dbh) {
+ $dbh = $self->dbconnect($myconfig);
+ $disconnect = 1;
+ }
+
+ # get years
+ my $query = qq|SELECT (SELECT MIN(transdate) FROM acc_trans),
+ (SELECT MAX(transdate) FROM acc_trans)
+ FROM defaults|;
+
+ my ($startdate, $enddate) = $dbh->selectrow_array($query);
+
+ if ($myconfig->{dateformat} =~ /^yy/) {
+ ($startdate) = split /\W/, $startdate;
+ ($enddate) = split /\W/, $enddate;
+ } else {
+ (@_) = split /\W/, $startdate;
+ $startdate = $_[2];
+ (@_) = split /\W/, $enddate;
+ $enddate = $_[2];
+ }
+
+ $self->{all_years} = ();
+ $startdate = substr($startdate,0,4);
+ $enddate = substr($enddate,0,4);
+
+ while ($enddate >= $startdate) {
+ push @{ $self->{all_years} }, $enddate--;
+ }
+
+ #this should probably be changed to use locale
+ %{ $self->{all_month} } = ( '01' => 'January',
+ '02' => 'February',
+ '03' => 'March',
+ '04' => 'April',
+ '05' => 'May ',
+ '06' => 'June',
+ '07' => 'July',
+ '08' => 'August',
+ '09' => 'September',
+ '10' => 'October',
+ '11' => 'November',
+ '12' => 'December' );
+
+ $dbh->disconnect if $disconnect;
+}
+
+
+sub create_links {
+
+ my ($self, $module, $myconfig, $vc, $job) = @_;
+
+ # get last customers or vendors
+ my ($query, $sth);
+
+ my $dbh = $self->dbconnect($myconfig);
+
+ my %xkeyref = ();
+
+
+ # now get the account numbers
+ $query = qq|SELECT accno, description, link
+ FROM chart
+ WHERE link LIKE '%$module%'
+ ORDER BY accno|;
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $self->dberror($query);
+
+ $self->{accounts} = "";
+
+ while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
+
+ foreach my $key (split /:/, $ref->{link}) {
+
+ if ($key =~ /$module/) {
+ # cross reference for keys
+ $xkeyref{$ref->{accno}} = $key;
+
+ push @{ $self->{"${module}_links"}{$key} }, { accno => $ref->{accno},
+ description => $ref->{description} };
+
+ $self->{accounts} .= "$ref->{accno} " unless $key =~ /tax/;
+ }
+ }
+ }
+
+ $sth->finish;
+
+ my $arap = ($vc eq 'customer') ? 'ar' : 'ap';
+
+ if ($self->{id}) {
+
+ $query = qq|SELECT a.invnumber, a.transdate,
+ a.${vc}_id, a.datepaid, a.duedate, a.ordnumber,
+ a.taxincluded, a.curr AS currency, a.notes, a.intnotes,
+ c.name AS $vc, a.department_id, d.description AS department,
+ a.amount AS oldinvtotal, a.paid AS oldtotalpaid,
+ a.employee_id, e.name AS employee, c.language_code,
+ a.ponumber
+ FROM $arap a
+ JOIN $vc c ON (a.${vc}_id = c.id)
+ LEFT JOIN employee e ON (e.id = a.employee_id)
+ LEFT JOIN department d ON (d.id = a.department_id)
+ WHERE a.id = $self->{id}|;
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $self->dberror($query);
+
+ $ref = $sth->fetchrow_hashref(NAME_lc);
+
+ foreach $key (keys %$ref) {
+ $self->{$key} = $ref->{$key};
+ }
+
+ $sth->finish;
+
+
+ # get printed, emailed
+ $query = qq|SELECT s.printed, s.emailed, s.spoolfile, s.formname
+ FROM status s
+ WHERE s.trans_id = $self->{id}|;
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+ $self->{printed} .= "$ref->{formname} " if $ref->{printed};
+ $self->{emailed} .= "$ref->{formname} " if $ref->{emailed};
+ $self->{queued} .= "$ref->{formname} $ref->{spoolfile} " if $ref->{spoolfile};
+ }
+
+ $sth->finish;
+ for (qw(printed emailed queued)) { $self->{$_} =~ s/ +$//g }
+
+ # get recurring
+ $self->get_recurring($dbh);
+
+ # get amounts from individual entries
+ $query = qq|SELECT c.accno, c.description, a.source, a.amount,
+ a.memo, a.transdate, a.cleared, a.project_id,
+ p.projectnumber
+ FROM acc_trans a
+ JOIN chart c ON (c.id = a.chart_id)
+ LEFT JOIN project p ON (p.id = a.project_id)
+ WHERE a.trans_id = $self->{id}
+ AND a.fx_transaction = '0'
+ ORDER BY transdate|;
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $self->dberror($query);
+
+
+ my $fld = ($vc eq 'customer') ? 'buy' : 'sell';
+
+ $self->{exchangerate} = $self->get_exchangerate($dbh, $self->{currency}, $self->{transdate}, $fld);
+
+ # store amounts in {acc_trans}{$key} for multiple accounts
+ while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
+ $ref->{exchangerate} = $self->get_exchangerate($dbh, $self->{currency}, $ref->{transdate}, $fld);
+
+ push @{ $self->{acc_trans}{$xkeyref{$ref->{accno}}} }, $ref;
+ }
+
+ $sth->finish;
+
+ $query = qq|SELECT d.curr AS currencies, d.closedto, d.revtrans
+ FROM defaults d|;
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $self->dberror($query);
+
+ $ref = $sth->fetchrow_hashref(NAME_lc);
+ for (keys %$ref) { $self->{$_} = $ref->{$_} }
+ $sth->finish;
+
+ } else {
+
+ # get date
+ $query = qq|SELECT current_date AS transdate,
+ d.curr AS currencies, d.closedto, d.revtrans
+ FROM defaults d|;
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $self->dberror($query);
+
+ $ref = $sth->fetchrow_hashref(NAME_lc);
+ for (keys %$ref) { $self->{$_} = $ref->{$_} }
+ $sth->finish;
+
+ if (! $self->{"$self->{vc}_id"}) {
+ $self->lastname_used($myconfig, $dbh, $vc, $module);
+ }
+ }
+
+ $self->all_vc($myconfig, $vc, $module, $dbh, $self->{transdate}, $job);
+ $dbh->disconnect;
+}
+
+
+sub lastname_used {
+
+ my ($self, $myconfig, $dbh, $vc, $module) = @_;
+
+ my $arap = ($vc eq 'customer') ? "ar" : "ap";
+ my $where = "1 = 1";
+ my $sth;
+
+ if ($self->{type} =~ /_order/) {
+ $arap = 'oe';
+ $where = "quotation = '0'";
+ }
+
+ if ($self->{type} =~ /_quotation/) {
+ $arap = 'oe';
+ $where = "quotation = '1'";
+ }
+
+ my $query = qq|SELECT id
+ FROM $arap
+ WHERE id IN (SELECT MAX(id)
+ FROM $arap
+ WHERE $where
+ AND ${vc}_id > 0)|;
+
+ my ($trans_id) = $dbh->selectrow_array($query);
+
+ $trans_id *= 1;
+
+ my $DAYS = ($myconfig->{dbdriver} eq 'DB2') ? "DAYS" : "";
+
+ $query = qq|SELECT ct.name AS $vc, a.curr AS currency, a.${vc}_id,
+ current_date + ct.terms $DAYS AS duedate, a.department_id,
+ d.description AS department, ct.notes, ct.curr AS currency
+ FROM $arap a
+ JOIN $vc ct ON (a.${vc}_id = ct.id)
+ LEFT JOIN department d ON (a.department_id = d.id)
+ WHERE a.id = $trans_id|;
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $self->dberror($query);
+
+ my $ref = $sth->fetchrow_hashref(NAME_lc);
+ for (keys %$ref) { $self->{$_} = $ref->{$_} }
+ $sth->finish;
+}
+
+
+
+sub current_date {
+
+ my ($self, $myconfig, $thisdate, $days) = @_;
+
+ my $dbh = $self->dbconnect($myconfig);
+ my $query;
+
+ $days *= 1;
+ if ($thisdate) {
+
+ my $dateformat = $myconfig->{dateformat};
+
+ if ($myconfig->{dateformat} !~ /^y/) {
+ my @a = split /\D/, $thisdate;
+ $dateformat .= "yy" if (length $a[2] > 2);
+ }
+
+ if ($thisdate !~ /\D/) {
+ $dateformat = 'yyyymmdd';
+ }
+
+ if ($myconfig->{dbdriver} eq 'DB2') {
+ $query = qq|SELECT date('$thisdate') + $days DAYS AS thisdate
+ FROM defaults|;
+
+ } else {
+ $query = qq|SELECT to_date('$thisdate', '$dateformat') + $days AS thisdate
+ FROM defaults|;
+ }
+
+ } else {
+ $query = qq|SELECT current_date AS thisdate
+ FROM defaults|;
+ }
+
+ ($thisdate) = $dbh->selectrow_array($query);
+ $dbh->disconnect;
+ $thisdate;
+}
+
+
+sub like {
+
+ my ($self, $str) = @_;
+
+ if ($str !~ /(%|_)/) {
+
+ if ($str =~ /(^").*("$)/) {
+ $str =~ s/(^"|"$)//g;
+ } else {
+ $str = "%$str%";
+ }
+ }
+
+ $str =~ s/'/''/g;
+ $str;
+}
+
+
+sub redo_rows {
+
+ my ($self, $flds, $new, $count, $numrows) = @_;
+
+ my @ndx = ();
+
+ for (1 .. $count) {
+ push @ndx, { num => $new->[$_-1]->{runningnumber}, ndx => $_ }
+ }
+
+ my $i = 0;
+ # fill rows
+ foreach my $item (sort { $a->{num} <=> $b->{num} } @ndx) {
+ $i++;
+ $j = $item->{ndx} - 1;
+ for (@{$flds}) { $self->{"${_}_$i"} = $new->[$j]->{$_} }
+ }
+
+ # delete empty rows
+ for $i ($count + 1 .. $numrows) {
+ for (@{$flds}) { delete $self->{"${_}_$i"} }
+ }
+}
+
+
+sub get_partsgroup {
+
+ my ($self, $myconfig, $p) = @_;
+
+ my $dbh = $self->dbconnect($myconfig);
+
+ my $query = qq|SELECT DISTINCT pg.id, pg.partsgroup
+ FROM partsgroup pg
+ JOIN parts p ON (p.partsgroup_id = pg.id)|;
+
+ my $where;
+ my $sortorder = "partsgroup";
+
+ if ($p->{searchitems} eq 'part') {
+ $where = qq| WHERE (p.inventory_accno_id > 0
+ AND p.income_accno_id > 0)|;
+ }
+
+ if ($p->{searchitems} eq 'service') {
+ $where = qq| WHERE p.inventory_accno_id IS NULL|;
+ }
+
+ if ($p->{searchitems} eq 'assembly') {
+ $where = qq| WHERE p.assembly = '1'|;
+ }
+
+ if ($p->{searchitems} eq 'labor') {
+ $where = qq| WHERE p.inventory_accno_id > 0 AND p.income_accno_id IS NULL|;
+ }
+
+ if ($p->{searchitems} eq 'nolabor') {
+ $where = qq| WHERE p.income_accno_id > 0|;
+ }
+
+ if ($p->{all}) {
+ $query = qq|SELECT id, partsgroup
+ FROM partsgroup|;
+ }
+
+ if ($p->{language_code}) {
+ $sortorder = "translation";
+
+ $query = qq|SELECT DISTINCT pg.id, pg.partsgroup,
+ t.description AS translation
+ FROM partsgroup pg
+ JOIN parts p ON (p.partsgroup_id = pg.id)
+ LEFT JOIN translation t ON (t.trans_id = pg.id AND t.language_code = '$p->{language_code}')|;
+ }
+
+ $query .= qq| $where ORDER BY $sortorder|;
+
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $self->dberror($query);
+
+ $self->{all_partsgroup} = ();
+
+ while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
+ push @{ $self->{all_partsgroup} }, $ref;
+ }
+
+ $sth->finish;
+ $dbh->disconnect;
+}
+
+
+sub update_status {
+
+ my ($self, $myconfig) = @_;
+
+ # no id return
+ return unless $self->{id};
+
+ my $dbh = $self->dbconnect_noauto($myconfig);
+
+ my %queued = split / +/, $self->{queued};
+ my $spoolfile = ($queued{$self->{formname}}) ? "'$queued{$self->{formname}}'" : 'NULL';
+
+ my $query = qq|DELETE FROM status
+ WHERE formname = '$self->{formname}'
+ AND trans_id = $self->{id}|;
+
+ $dbh->do($query) || $self->dberror($query);
+
+ my $printed = ($self->{printed} =~ /$self->{formname}/) ? "1" : "0";
+ my $emailed = ($self->{emailed} =~ /$self->{formname}/) ? "1" : "0";
+
+ $query = qq|INSERT INTO status (trans_id, printed, emailed,
+ spoolfile, formname)
+ VALUES ($self->{id}, '$printed',
+ '$emailed', $spoolfile,
+ '$self->{formname}')|;
+
+ $dbh->do($query) || $self->dberror($query);
+ $dbh->commit;
+ $dbh->disconnect;
+}
+
+
+sub save_status {
+
+ my ($self, $dbh) = @_;
+
+ my $formnames = $self->{printed};
+ my $emailforms = $self->{emailed};
+
+ my $query = qq|DELETE FROM status
+ WHERE trans_id = $self->{id}|;
+
+ $dbh->do($query) || $self->dberror($query);
+
+ my %queued;
+ my $formname;
+
+ if ($self->{queued}) {
+
+ %queued = split / +/, $self->{queued};
+
+ foreach $formname (keys %queued) {
+
+ $printed = ($self->{printed} =~ /$formname/) ? "1" : "0";
+ $emailed = ($self->{emailed} =~ /$formname/) ? "1" : "0";
+
+ if ($queued{$formname}) {
+ $query = qq|INSERT INTO status (trans_id, printed, emailed,
+ spoolfile, formname)
+ VALUES ($self->{id}, '$printed', '$emailed',
+ '$queued{$formname}', '$formname')|;
+
+ $dbh->do($query) || $self->dberror($query);
+ }
+
+ $formnames =~ s/$formname//;
+ $emailforms =~ s/$formname//;
+
+ }
+ }
+
+ # save printed, emailed info
+ $formnames =~ s/^ +//g;
+ $emailforms =~ s/^ +//g;
+
+ my %status = ();
+ for (split / +/, $formnames) { $status{$_}{printed} = 1 }
+ for (split / +/, $emailforms) { $status{$_}{emailed} = 1 }
+
+ foreach my $formname (keys %status) {
+ $printed = ($formnames =~ /$self->{formname}/) ? "1" : "0";
+ $emailed = ($emailforms =~ /$self->{formname}/) ? "1" : "0";
+
+ $query = qq|INSERT INTO status (trans_id, printed, emailed, formname)
+ VALUES ($self->{id}, '$printed', '$emailed', '$formname')|;
+
+ $dbh->do($query) || $self->dberror($query);
+ }
+}
+
+
+sub get_recurring {
+
+ my ($self, $dbh) = @_;
+
+ my $query = qq/SELECT s.*, se.formname || ':' || se.format AS emaila,
+ se.message,
+ sp.formname || ':' || sp.format || ':' || sp.printer AS printa
+ FROM recurring s
+ LEFT JOIN recurringemail se ON (s.id = se.id)
+ LEFT JOIN recurringprint sp ON (s.id = sp.id)
+ WHERE s.id = $self->{id}/;
+
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ for (qw(email print)) { $self->{"recurring$_"} = "" }
+
+ while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
+ for (keys %$ref) { $self->{"recurring$_"} = $ref->{$_} }
+ $self->{recurringemail} .= "$ref->{emaila}:";
+ $self->{recurringprint} .= "$ref->{printa}:";
+ for (qw(emaila printa)) { delete $self->{"recurring$_"} }
+ }
+
+ $sth->finish;
+ chop $self->{recurringemail};
+ chop $self->{recurringprint};
+
+ if ($self->{recurringstartdate}) {
+ $self->{recurringreference} = $self->escape($self->{recurringreference},1);
+ $self->{recurringmessage} = $self->escape($self->{recurringmessage},1);
+ for (qw(reference startdate repeat unit howmany
+ payment print email message)) {
+
+ $self->{recurring} .= qq|$self->{"recurring$_"},|
+ }
+
+ chop $self->{recurring};
+ }
+}
+
+
+sub save_recurring {
+
+ my ($self, $dbh, $myconfig) = @_;
+
+ my $disconnect = 0;
+
+ if (! $dbh) {
+ $dbh = $self->dbconnect_noauto($myconfig);
+ $disconnect = 1;
+ }
+
+ my $query;
+
+ $query = qq|DELETE FROM recurring
+ WHERE id = $self->{id}|;
+
+ $dbh->do($query) || $self->dberror($query);
+
+ $query = qq|DELETE FROM recurringemail
+ WHERE id = $self->{id}|;
+
+ $dbh->do($query) || $self->dberror($query);
+
+ $query = qq|DELETE FROM recurringprint
+ WHERE id = $self->{id}|;
+
+ $dbh->do($query) || $self->dberror($query);
+
+ if ($self->{recurring}) {
+
+ my %s = ();
+ ($s{reference}, $s{startdate}, $s{repeat}, $s{unit}, $s{howmany},
+ $s{payment}, $s{print}, $s{email}, $s{message}) = split /,/, $self->{recurring};
+
+ for (qw(reference message)) { $s{$_} = $self->unescape($s{$_}) }
+ for (qw(repeat howmany payment)) { $s{$_} *= 1 }
+
+ # calculate enddate
+ my $advance = $s{repeat} * ($s{howmany} - 1);
+ my %interval = ( 'Pg' => "(date '$s{startdate}' + interval '$advance $s{unit}')",
+ 'DB2' => qq|(date ('$s{startdate}') + "$advance $s{unit}")|, );
+
+ $interval{Oracle} = $interval{PgPP} = $interval{Pg};
+
+ $query = qq|SELECT $interval{$myconfig->{dbdriver}}
+ FROM defaults|;
+
+ my ($enddate) = $dbh->selectrow_array($query);
+
+ # calculate nextdate
+ $query = qq|SELECT current_date - date '$s{startdate}' AS a,
+ date '$enddate' - current_date AS b
+ FROM defaults|;
+
+ my ($a, $b) = $dbh->selectrow_array($query);
+
+ if ($a + $b) {
+ $advance = int(($a / ($a + $b)) * ($s{howmany} - 1) + 1) * $s{repeat};
+ } else {
+ $advance = 0;
+ }
+
+ my $nextdate = $enddate;
+ if ($advance > 0) {
+ if ($advance < ($s{repeat} * $s{howmany})) {
+ %interval = ( 'Pg' => "(date '$s{startdate}' + interval '$advance $s{unit}')",
+ 'DB2' => qq|(date ('$s{startdate}') + "$advance $s{unit}")|,);
+
+ $interval{Oracle} = $interval{PgPP} = $interval{Pg};
+
+ $query = qq|SELECT $interval{$myconfig->{dbdriver}}
+ FROM defaults|;
+
+ ($nextdate) = $dbh->selectrow_array($query);
+ }
+
+ } else {
+ $nextdate = $s{startdate};
+ }
+
+ if ($self->{recurringnextdate}) {
+
+ $nextdate = $self->{recurringnextdate};
+
+ $query = qq|SELECT '$enddate' - date '$nextdate'
+ FROM defaults|;
+
+ if ($dbh->selectrow_array($query) < 0) {
+ undef $nextdate;
+ }
+ }
+
+ $self->{recurringpayment} *= 1;
+
+ $query = qq|INSERT INTO recurring (id, reference, startdate, enddate,
+ nextdate, repeat, unit, howmany, payment)
+ VALUES ($self->{id}, |.$dbh->quote($s{reference}).qq|,
+ '$s{startdate}', '$enddate', |.
+ $self->dbquote($nextdate, SQL_DATE).
+ qq|, $s{repeat}, '$s{unit}', $s{howmany}, '$s{payment}')|;
+
+ $dbh->do($query) || $self->dberror($query);
+
+ my @p;
+ my $p;
+ my $i;
+ my $sth;
+
+ if ($s{email}) {
+ # formname:format
+ @p = split /:/, $s{email};
+
+ $query = qq|INSERT INTO recurringemail (id, formname, format, message)
+ VALUES ($self->{id}, ?, ?, ?)|;
+
+ $sth = $dbh->prepare($query) || $self->dberror($query);
+
+ for ($i = 0; $i <= $#p; $i += 2) {
+ $sth->execute($p[$i], $p[$i+1], $s{message});
+ }
+
+ $sth->finish;
+ }
+
+ if ($s{print}) {
+ # formname:format:printer
+ @p = split /:/, $s{print};
+
+ $query = qq|INSERT INTO recurringprint (id, formname, format, printer)
+ VALUES ($self->{id}, ?, ?, ?)|;
+
+ $sth = $dbh->prepare($query) || $self->dberror($query);
+
+ for ($i = 0; $i <= $#p; $i += 3) {
+ $p = ($p[$i+2]) ? $p[$i+2] : "";
+ $sth->execute($p[$i], $p[$i+1], $p);
+ }
+
+ $sth->finish;
+ }
+ }
+
+ if ($disconnect) {
+ $dbh->commit;
+ $dbh->disconnect;
+ }
+}
+
+
+sub save_intnotes {
+
+ my ($self, $myconfig, $vc) = @_;
+
+ # no id return
+ return unless $self->{id};
+
+ my $dbh = $self->dbconnect($myconfig);
+
+ my $query = qq|UPDATE $vc
+ SET intnotes = |.$dbh->quote($self->{intnotes}).qq|
+ WHERE id = $self->{id}|;
+
+ $dbh->do($query) || $self->dberror($query);
+ $dbh->disconnect;
+}
+
+
+sub update_defaults {
+
+ my ($self, $myconfig, $fld, $dbh) = @_;
+
+ my $closedb;
+
+ if (! $dbh) {
+ $dbh = $self->dbconnect_noauto($myconfig);
+ $closedb = 1;
+ }
+
+ my $query = qq|SELECT $fld FROM defaults FOR UPDATE|;
+ ($_) = $dbh->selectrow_array($query);
+
+ $_ = "0" unless $_;
+
+ # check for and replace
+ # <%DATE%>, <%YYMMDD%>, <%YEAR%>, <%MONTH%>, <%DAY%> or variations of
+ # <%NAME 1 1 3%>, <%BUSINESS%>, <%BUSINESS 10%>, <%CURR...%>
+ # <%DESCRIPTION 1 1 3%>, <%ITEM 1 1 3%>, <%PARTSGROUP 1 1 3%> only for parts
+ # <%PHONE%> for customer and vendors
+
+ my $num = $_;
+ $num =~ s/.*?<%.*?%>//g;
+ ($num) = $num =~ /(\d+)/;
+
+ if (defined $num) {
+ my $incnum;
+ # if we have leading zeros check how long it is
+
+ if ($num =~ /^0/) {
+ my $l = length $num;
+ $incnum = $num + 1;
+ $l -= length $incnum;
+
+ # pad it out with zeros
+ my $padzero = "0" x $l;
+ $incnum = ("0" x $l) . $incnum;
+ } else {
+ $incnum = $num + 1;
+ }
+
+ s/$num/$incnum/;
+ }
+
+ my $dbvar = $_;
+ my $var = $_;
+ my $str;
+ my $param;
+
+ if (/<%/) {
+
+ while (/<%/) {
+
+ s/<%.*?%>//;
+ last unless $&;
+ $param = $&;
+ $str = "";
+
+ if ($param =~ /<%date%>/i) {
+ $str = ($self->split_date($myconfig->{dateformat}, $self->{transdate}))[0];
+ $var =~ s/$param/$str/;
+ }
+
+ if ($param =~ /<%(name|business|description|item|partsgroup|phone|custom)/i) {
+
+ my $fld = lc $&;
+ $fld =~ s/<%//;
+
+ if ($fld =~ /name/) {
+ if ($self->{type}) {
+ $fld = $self->{vc};
+ }
+ }
+
+ my $p = $param;
+ $p =~ s/(<|>|%)//g;
+ my @p = split / /, $p;
+ my @n = split / /, uc $self->{$fld};
+
+ if ($#p > 0) {
+
+ for (my $i = 1; $i <= $#p; $i++) {
+ $str .= substr($n[$i-1], 0, $p[$i]);
+ }
+
+ } else {
+ ($str) = split /--/, $self->{$fld};
+ }
+
+ $var =~ s/$param/$str/;
+ $var =~ s/\W//g if $fld eq 'phone';
+ }
+
+ if ($param =~ /<%(yy|mm|dd)/i) {
+
+ my $p = $param;
+ $p =~ s/(<|>|%)//g;
+ my $spc = $p;
+ $spc =~ s/\w//g;
+ $spc = substr($spc, 0, 1);
+ my %d = ( yy => 1, mm => 2, dd => 3 );
+ my @p = ();
+
+ my @a = $self->split_date($myconfig->{dateformat}, $self->{transdate});
+ for (sort keys %d) { push @p, $a[$d{$_}] if ($p =~ /$_/) }
+ $str = join $spc, @p;
+ $var =~ s/$param/$str/;
+ }
+
+ if ($param =~ /<%curr/i) {
+ $var =~ s/$param/$self->{currency}/;
+ }
+ }
+ }
+
+ $query = qq|UPDATE defaults
+ SET $fld = '$dbvar'|;
+
+ $dbh->do($query) || $form->dberror($query);
+
+ if ($closedb) {
+ $dbh->commit;
+ $dbh->disconnect;
+ }
+
+ $var;
+}
+
+
+sub split_date {
+
+ my ($self, $dateformat, $date) = @_;
+
+ my @d = localtime;
+ my $mm;
+ my $dd;
+ my $yy;
+ my $rv;
+
+ if (! $date) {
+ $dd = $d[3];
+ $mm = ++$d[4];
+ $yy = substr($d[5],-2);
+ $mm = substr("0$mm", -2);
+ $dd = substr("0$dd", -2);
+ }
+
+ if ($dateformat =~ /^yy/) {
+
+ if ($date) {
+
+ if ($date =~ /\D/) {
+ ($yy, $mm, $dd) = split /\D/, $date;
+ $mm *= 1;
+ $dd *= 1;
+ $mm = substr("0$mm", -2);
+ $dd = substr("0$dd", -2);
+ $yy = substr($yy, -2);
+ $rv = "$yy$mm$dd";
+ } else {
+ $rv = $date;
+ }
+ } else {
+ $rv = "$yy$mm$dd";
+ }
+ }
+
+ if ($dateformat =~ /^mm/) {
+
+ if ($date) {
+
+ if ($date =~ /\D/) {
+ ($mm, $dd, $yy) = split /\D/, $date;
+ $mm *= 1;
+ $dd *= 1;
+ $mm = substr("0$mm", -2);
+ $dd = substr("0$dd", -2);
+ $yy = substr($yy, -2);
+ $rv = "$mm$dd$yy";
+ } else {
+ $rv = $date;
+ }
+ } else {
+ $rv = "$mm$dd$yy";
+ }
+ }
+
+ if ($dateformat =~ /^dd/) {
+
+ if ($date) {
+
+ if ($date =~ /\D/) {
+ ($dd, $mm, $yy) = split /\D/, $date;
+ $mm *= 1;
+ $dd *= 1;
+ $mm = substr("0$mm", -2);
+ $dd = substr("0$dd", -2);
+ $yy = substr($yy, -2);
+ $rv = "$dd$mm$yy";
+ } else {
+ $rv = $date;
+ }
+ } else {
+ $rv = "$dd$mm$yy";
+ }
+ }
+
+ ($rv, $yy, $mm, $dd);
+}
+
+
+sub from_to {
+
+ my ($self, $yy, $mm, $interval) = @_;
+
+ use Time::Local;
+
+ my @t;
+ my $dd = 1;
+ my $fromdate = "$yy${mm}01";
+ my $bd = 1;
+
+ if (defined $interval) {
+
+ if ($interval == 12) {
+ $yy++;
+ } else {
+
+ if (($mm += $interval) > 12) {
+ $mm -= 12;
+ $yy++;
+ }
+
+ if ($interval == 0) {
+ @t = localtime(time);
+ $dd = $t[3];
+ $mm = $t[4] + 1;
+ $yy = $t[5] + 1900;
+ $bd = 0;
+ }
+ }
+
+ } else {
+
+ if (++$mm > 12) {
+ $mm -= 12;
+ $yy++;
+ }
+ }
+
+ $mm--;
+ @t = localtime(timelocal(0,0,0,$dd,$mm,$yy) - $bd);
+
+ $t[4]++;
+ $t[4] = substr("0$t[4]",-2);
+ $t[3] = substr("0$t[3]",-2);
+ $t[5] += 1900;
+
+ ($fromdate, "$t[5]$t[4]$t[3]");
+}
+
+
+sub audittrail {
+
+ my ($self, $dbh, $myconfig, $audittrail) = @_;
+
+ # table, $reference, $formname, $action, $id, $transdate) = @_;
+
+ my $query;
+ my $rv;
+ my $disconnect;
+
+ if (! $dbh) {
+ $dbh = $self->dbconnect($myconfig);
+ $disconnect = 1;
+ }
+
+ # if we have an id add audittrail, otherwise get a new timestamp
+
+ if ($audittrail->{id}) {
+
+ $query = qq|SELECT audittrail FROM defaults|;
+
+ if ($dbh->selectrow_array($query)) {
+
+ my ($null, $employee_id) = $self->get_employee($dbh);
+
+ if ($self->{audittrail} && !$myconfig) {
+
+ chop $self->{audittrail};
+
+ my @a = split /\|/, $self->{audittrail};
+ my %newtrail = ();
+ my $key;
+ my $i;
+ my @flds = qw(tablename reference formname action transdate);
+
+ # put into hash and remove dups
+ while (@a) {
+ $key = "$a[2]$a[3]";
+ $i = 0;
+ $newtrail{$key} = { map { $_ => $a[$i++] } @flds };
+ splice @a, 0, 5;
+ }
+
+ $query = qq|INSERT INTO audittrail (trans_id, tablename, reference,
+ formname, action, employee_id, transdate)
+ VALUES ($audittrail->{id}, ?, ?, ?, ?, $employee_id, ?)|;
+
+ my $sth = $dbh->prepare($query) || $self->dberror($query);
+
+ foreach $key (sort { $newtrail{$a}{transdate} cmp $newtrail{$b}{transdate} } keys %newtrail) {
+
+ $i = 1;
+ for (@flds) { $sth->bind_param($i++, $newtrail{$key}{$_}) }
+
+ $sth->execute || $self->dberror;
+ $sth->finish;
+ }
+ }
+
+ if ($audittrail->{transdate}) {
+
+ $query = qq|INSERT INTO audittrail (trans_id, tablename, reference,
+ formname, action, employee_id, transdate)
+ VALUES ($audittrail->{id}, '$audittrail->{tablename}', |
+ .$dbh->quote($audittrail->{reference}).qq|',
+ '$audittrail->{formname}', '$audittrail->{action}',
+ $employee_id, '$audittrail->{transdate}')|;
+
+ } else {
+ $query = qq|INSERT INTO audittrail (trans_id, tablename, reference,
+ formname, action, employee_id)
+ VALUES ($audittrail->{id},
+ '$audittrail->{tablename}', |
+ .$dbh->quote($audittrail->{reference}).qq|,
+ '$audittrail->{formname}', '$audittrail->{action}',
+ $employee_id)|;
+ }
+
+ $dbh->do($query);
+ }
+
+ } else {
+
+ $query = qq|SELECT current_timestamp FROM defaults|;
+ my ($timestamp) = $dbh->selectrow_array($query);
+
+ $rv = "$audittrail->{tablename}|$audittrail->{reference}|$audittrail->{formname}|$audittrail->{action}|$timestamp|";
+ }
+
+ $dbh->disconnect if $disconnect;
+ $rv;
+}
+
+package Locale;
+
+sub new {
+ my ($type, $country, $NLS_file) = @_;
+ my $self = {};
+
+ %self = ();
+
+ if ($country && -d "locale/$country") {
+ $self->{countrycode} = $country;
+ eval { require "locale/$country/$NLS_file"; };
+ }
+
+ $self->{NLS_file} = $NLS_file;
+ $self->{charset} = $self{charset};
+
+ push @{ $self->{LONG_MONTH} }, ("January", "February", "March", "April", "May ", "June", "July", "August", "September", "October", "November", "December");
+ push @{ $self->{SHORT_MONTH} }, (qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec));
+
+ bless $self, $type;
+}
+
+
+sub text {
+ my ($self, $text) = @_;
+ return (exists $self{texts}{$text}) ? $self{texts}{$text} : $text;
+}
+
+
+sub findsub {
+
+ my ($self, $text) = @_;
+
+ if (exists $self{subs}{$text}) {
+ $text = $self{subs}{$text};
+ } else {
+ if ($self->{countrycode} && $self->{NLS_file}) {
+ Form->error("$text not defined in locale/$self->{countrycode}/$self->{NLS_file}");
+ }
+ }
+
+ $text;
+}
+
+
+sub date {
+
+ my ($self, $myconfig, $date, $longformat) = @_;
+
+ my $longdate = "";
+ my $longmonth = ($longformat) ? 'LONG_MONTH' : 'SHORT_MONTH';
+
+
+ if ($date) {
+
+ # get separator
+ $spc = $myconfig->{dateformat};
+ $spc =~ s/\w//g;
+ $spc = substr($spc, 0, 1);
+
+ if ($date =~ /\D/) {
+
+ if ($myconfig->{dateformat} =~ /^yy/) {
+ ($yy, $mm, $dd) = split /\D/, $date;
+ }
+
+ if ($myconfig->{dateformat} =~ /^mm/) {
+ ($mm, $dd, $yy) = split /\D/, $date;
+ }
+
+ if ($myconfig->{dateformat} =~ /^dd/) {
+ ($dd, $mm, $yy) = split /\D/, $date;
+ }
+
+ } else {
+
+ $date = substr($date, 2);
+ ($yy, $mm, $dd) = ($date =~ /(..)(..)(..)/);
+ }
+
+ $dd *= 1;
+ $mm--;
+ $yy += 2000 if length $yy == 2;
+
+ if ($myconfig->{dateformat} =~ /^dd/) {
+
+ $mm++;
+ $dd = substr("0$dd", -2);
+ $mm = substr("0$mm", -2);
+ $longdate = "$dd$spc$mm$spc$yy";
+
+ if (defined $longformat) {
+ $longdate = "$dd";
+ $longdate .= ($spc eq '.') ? ". " : " ";
+ $longdate .= &text($self, $self->{$longmonth}[--$mm])." $yy";
+ }
+
+ } elsif ($myconfig->{dateformat} =~ /^yy/) {
+
+ $mm++;
+ $dd = substr("0$dd", -2);
+ $mm = substr("0$mm", -2);
+ $longdate = "$yy$spc$mm$spc$dd";
+
+ if (defined $longformat) {
+ $longdate = &text($self, $self->{$longmonth}[--$mm])." $dd $yy";
+ }
+
+ } else {
+
+ $mm++;
+ $dd = substr("0$dd", -2);
+ $mm = substr("0$mm", -2);
+ $longdate = "$mm$spc$dd$spc$yy";
+
+ if (defined $longformat) {
+ $longdate = &text($self, $self->{$longmonth}[--$mm])." $dd $yy";
+ }
+ }
+ }
+
+ $longdate;
+}
+
+1;
diff --git a/LedgerSMB/GL.pm b/LedgerSMB/GL.pm
new file mode 100755
index 00000000..7c1d4fdd
--- /dev/null
+++ b/LedgerSMB/GL.pm
@@ -0,0 +1,526 @@
+#=====================================================================
+# LedgerSMB
+# Small Medium Business Accounting software
+#
+# See COPYRIGHT file for copyright information
+#======================================================================
+#
+# This file has undergone whitespace cleanup.
+#
+#======================================================================
+#
+# General ledger backend code
+#
+#======================================================================
+
+package GL;
+
+
+sub delete_transaction {
+
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect_noauto($myconfig);
+
+ my %audittrail = ( tablename => 'gl',
+ reference => $form->{reference},
+ formname => 'transaction',
+ action => 'deleted',
+ id => $form->{id} );
+
+ $form->audittrail($dbh, "", \%audittrail);
+
+ my $query = qq|DELETE FROM gl WHERE id = $form->{id}|;
+ $dbh->do($query) || $form->dberror($query);
+
+ $query = qq|DELETE FROM acc_trans WHERE trans_id = $form->{id}|;
+ $dbh->do($query) || $form->dberror($query);
+
+ # commit and redirect
+ my $rc = $dbh->commit;
+ $dbh->disconnect;
+
+ $rc;
+}
+
+
+sub post_transaction {
+
+ my ($self, $myconfig, $form) = @_;
+
+ my $null;
+ my $project_id;
+ my $department_id;
+ my $i;
+
+ # connect to database, turn off AutoCommit
+ my $dbh = $form->dbconnect_noauto($myconfig);
+
+ my $query;
+ my $sth;
+
+ if ($form->{id}) {
+
+ $query = qq|SELECT id FROM gl WHERE id = $form->{id}|;
+ ($form->{id}) = $dbh->selectrow_array($query);
+
+ if ($form->{id}) {
+ # delete individual transactions
+ $query = qq|DELETE FROM acc_trans
+ WHERE trans_id = $form->{id}|;
+
+ $dbh->do($query) || $form->dberror($query);
+ }
+ }
+
+ if (! $form->{id}) {
+
+ my $uid = localtime;
+ $uid .= "$$";
+
+ $query = qq|INSERT INTO gl (reference, employee_id)
+ VALUES ('$uid', (SELECT id FROM employee
+ WHERE login = '$form->{login}'))|;
+
+ $dbh->do($query) || $form->dberror($query);
+
+ $query = qq|SELECT id
+ FROM gl
+ WHERE reference = '$uid'|;
+
+ ($form->{id}) = $dbh->selectrow_array($query);
+ }
+
+ ($null, $department_id) = split /--/, $form->{department};
+ $department_id *= 1;
+
+ $form->{reference} = $form->update_defaults($myconfig, 'glnumber', $dbh) unless $form->{reference};
+ $form->{reference} ||= $form->{id};
+
+ $query = qq|UPDATE gl
+ SET reference = |.$dbh->quote($form->{reference}).qq|,
+ description = |.$dbh->quote($form->{description}).qq|,
+ notes = |.$dbh->quote($form->{notes}).qq|,
+ transdate = '$form->{transdate}',
+ department_id = $department_id
+ WHERE id = $form->{id}|;
+
+ $dbh->do($query) || $form->dberror($query);
+
+ my $amount = 0;
+ my $posted = 0;
+ my $debit;
+ my $credit;
+
+ # insert acc_trans transactions
+ for $i (1 .. $form->{rowcount}) {
+
+ $debit = $form->parse_amount($myconfig, $form->{"debit_$i"});
+ $credit = $form->parse_amount($myconfig, $form->{"credit_$i"});
+
+ # extract accno
+ ($accno) = split(/--/, $form->{"accno_$i"});
+
+ if ($credit) {
+ $amount = $credit;
+ $posted = 0;
+ }
+
+ if ($debit) {
+ $amount = $debit * -1;
+ $posted = 0;
+ }
+
+ # add the record
+ if (! $posted) {
+
+ ($null, $project_id) = split /--/, $form->{"projectnumber_$i"};
+ $project_id ||= 'NULL';
+
+ for (qw(fx_transaction cleared)) { $form->{"${_}_$i"} *= 1 }
+
+
+ $query = qq|INSERT INTO acc_trans (trans_id, chart_id, amount,
+ transdate, source, project_id,
+ fx_transaction, memo, cleared)
+ VALUES ($form->{id}, (SELECT id
+ FROM chart
+ WHERE accno = '$accno'),
+ $amount, '$form->{transdate}', |.
+ $dbh->quote($form->{"source_$i"}) .qq|,
+ $project_id, '$form->{"fx_transaction_$i"}', |.
+ $dbh->quote($form->{"memo_$i"}).qq|,
+ '$form->{"cleared_$i"}')|;
+
+ $dbh->do($query) || $form->dberror($query);
+
+ $posted = 1;
+ }
+ }
+
+ my %audittrail = ( tablename => 'gl',
+ reference => $form->{reference},
+ formname => 'transaction',
+ action => 'posted',
+ id => $form->{id} );
+
+ $form->audittrail($dbh, "", \%audittrail);
+
+ $form->save_recurring($dbh, $myconfig);
+
+ # commit and redirect
+ my $rc = $dbh->commit;
+ $dbh->disconnect;
+
+ $rc;
+}
+
+
+
+sub all_transactions {
+
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+ my $query;
+ my $sth;
+ my $var;
+ my $null;
+
+ my ($glwhere, $arwhere, $apwhere) = ("1 = 1", "1 = 1", "1 = 1");
+
+ if ($form->{reference} ne "") {
+ $var = $form->like(lc $form->{reference});
+ $glwhere .= " AND lower(g.reference) LIKE '$var'";
+ $arwhere .= " AND lower(a.invnumber) LIKE '$var'";
+ $apwhere .= " AND lower(a.invnumber) LIKE '$var'";
+ }
+
+ if ($form->{department} ne "") {
+ ($null, $var) = split /--/, $form->{department};
+ $glwhere .= " AND g.department_id = $var";
+ $arwhere .= " AND a.department_id = $var";
+ $apwhere .= " AND a.department_id = $var";
+ }
+
+ if ($form->{source} ne "") {
+ $var = $form->like(lc $form->{source});
+ $glwhere .= " AND lower(ac.source) LIKE '$var'";
+ $arwhere .= " AND lower(ac.source) LIKE '$var'";
+ $apwhere .= " AND lower(ac.source) LIKE '$var'";
+ }
+
+ if ($form->{memo} ne "") {
+ $var = $form->like(lc $form->{memo});
+ $glwhere .= " AND lower(ac.memo) LIKE '$var'";
+ $arwhere .= " AND lower(ac.memo) LIKE '$var'";
+ $apwhere .= " AND lower(ac.memo) LIKE '$var'";
+ }
+
+ ($form->{datefrom}, $form->{dateto}) = $form->from_to($form->{year}, $form->{month}, $form->{interval}) if $form->{year} && $form->{month};
+
+ if ($form->{datefrom}) {
+ $glwhere .= " AND ac.transdate >= '$form->{datefrom}'";
+ $arwhere .= " AND ac.transdate >= '$form->{datefrom}'";
+ $apwhere .= " AND ac.transdate >= '$form->{datefrom}'";
+ }
+
+ if ($form->{dateto}) {
+ $glwhere .= " AND ac.transdate <= '$form->{dateto}'";
+ $arwhere .= " AND ac.transdate <= '$form->{dateto}'";
+ $apwhere .= " AND ac.transdate <= '$form->{dateto}'";
+ }
+
+ if ($form->{amountfrom}) {
+ $glwhere .= " AND abs(ac.amount) >= $form->{amountfrom}";
+ $arwhere .= " AND abs(ac.amount) >= $form->{amountfrom}";
+ $apwhere .= " AND abs(ac.amount) >= $form->{amountfrom}";
+ }
+
+ if ($form->{amountto}) {
+ $glwhere .= " AND abs(ac.amount) <= $form->{amountto}";
+ $arwhere .= " AND abs(ac.amount) <= $form->{amountto}";
+ $apwhere .= " AND abs(ac.amount) <= $form->{amountto}";
+ }
+
+ if ($form->{description}) {
+
+ $var = $form->like(lc $form->{description});
+ $glwhere .= " AND lower(g.description) LIKE '$var'";
+ $arwhere .= " AND (lower(ct.name) LIKE '$var'
+ OR lower(ac.memo) LIKE '$var'
+ OR a.id IN (SELECT DISTINCT trans_id
+ FROM invoice
+ WHERE lower(description) LIKE '$var'))";
+
+ $apwhere .= " AND (lower(ct.name) LIKE '$var'
+ OR lower(ac.memo) LIKE '$var'
+ OR a.id IN (SELECT DISTINCT trans_id
+ FROM invoice
+ WHERE lower(description) LIKE '$var'))";
+ }
+
+ if ($form->{notes}) {
+ $var = $form->like(lc $form->{notes});
+ $glwhere .= " AND lower(g.notes) LIKE '$var'";
+ $arwhere .= " AND lower(a.notes) LIKE '$var'";
+ $apwhere .= " AND lower(a.notes) LIKE '$var'";
+ }
+
+ if ($form->{accno}) {
+ $glwhere .= " AND c.accno = '$form->{accno}'";
+ $arwhere .= " AND c.accno = '$form->{accno}'";
+ $apwhere .= " AND c.accno = '$form->{accno}'";
+ }
+
+ if ($form->{gifi_accno}) {
+ $glwhere .= " AND c.gifi_accno = '$form->{gifi_accno}'";
+ $arwhere .= " AND c.gifi_accno = '$form->{gifi_accno}'";
+ $apwhere .= " AND c.gifi_accno = '$form->{gifi_accno}'";
+ }
+
+ if ($form->{category} ne 'X') {
+ $glwhere .= " AND c.category = '$form->{category}'";
+ $arwhere .= " AND c.category = '$form->{category}'";
+ $apwhere .= " AND c.category = '$form->{category}'";
+ }
+
+ if ($form->{accno}) {
+
+ # get category for account
+ $query = qq|SELECT category, link, contra, description
+ FROM chart
+ WHERE accno = '$form->{accno}'|;
+
+ ($form->{category}, $form->{link}, $form->{contra},
+ $form->{account_description}) = $dbh->selectrow_array($query);
+
+ if ($form->{datefrom}) {
+
+ $query = qq|SELECT SUM(ac.amount)
+ FROM acc_trans ac
+ JOIN chart c ON (ac.chart_id = c.id)
+ WHERE c.accno = '$form->{accno}'
+ AND ac.transdate < date '$form->{datefrom}' |;
+
+ ($form->{balance}) = $dbh->selectrow_array($query);
+ }
+ }
+
+ if ($form->{gifi_accno}) {
+
+ # get category for account
+ $query = qq|SELECT c.category, c.link, c.contra, g.description
+ FROM chart c
+ LEFT JOIN gifi g ON (g.accno = c.gifi_accno)
+ WHERE c.gifi_accno = '$form->{gifi_accno}'|;
+
+ ($form->{category}, $form->{link}, $form->{contra},
+ $form->{gifi_account_description}) = $dbh->selectrow_array($query);
+
+ if ($form->{datefrom}) {
+
+ $query = qq|SELECT SUM(ac.amount)
+ FROM acc_trans ac
+ JOIN chart c ON (ac.chart_id = c.id)
+ WHERE c.gifi_accno = '$form->{gifi_accno}'
+ AND ac.transdate < date '$form->{datefrom}' |;
+
+ ($form->{balance}) = $dbh->selectrow_array($query);
+ }
+ }
+
+ my $false = ($myconfig->{dbdriver} =~ /Pg/) ? FALSE : q|'0'|;
+
+ my %ordinal = ( id => 1,
+ reference => 4,
+ description => 5,
+ transdate => 6,
+ source => 7,
+ accno => 9,
+ department => 15,
+ memo => 16 );
+
+ my @a = (id, transdate, reference, source, description, accno);
+ my $sortorder = $form->sort_order(\@a, \%ordinal);
+
+ my $query = qq|SELECT g.id, 'gl' AS type, $false AS invoice, g.reference,
+ g.description, ac.transdate, ac.source,
+ ac.amount, c.accno, c.gifi_accno, g.notes, c.link,
+ '' AS till, ac.cleared, d.description AS department,
+ ac.memo
+ FROM gl AS g
+ JOIN acc_trans ac ON (g.id = ac.trans_id)
+ JOIN chart c ON (ac.chart_id = c.id)
+ LEFT JOIN department d ON (d.id = g.department_id)
+ WHERE $glwhere
+
+ UNION ALL
+
+ SELECT a.id, 'ar' AS type, a.invoice, a.invnumber,
+ ct.name, ac.transdate, ac.source,
+ ac.amount, c.accno, c.gifi_accno, a.notes, c.link,
+ a.till, ac.cleared, d.description AS department,
+ ac.memo
+ FROM ar a
+ JOIN acc_trans ac ON (a.id = ac.trans_id)
+ JOIN chart c ON (ac.chart_id = c.id)
+ JOIN customer ct ON (a.customer_id = ct.id)
+ LEFT JOIN department d ON (d.id = a.department_id)
+ WHERE $arwhere
+
+ UNION ALL
+
+ SELECT a.id, 'ap' AS type, a.invoice, a.invnumber,
+ ct.name, ac.transdate, ac.source,
+ ac.amount, c.accno, c.gifi_accno, a.notes, c.link,
+ a.till, ac.cleared, d.description AS department,
+ ac.memo
+ FROM ap a
+ JOIN acc_trans ac ON (a.id = ac.trans_id)
+ JOIN chart c ON (ac.chart_id = c.id)
+ JOIN vendor ct ON (a.vendor_id = ct.id)
+ LEFT JOIN department d ON (d.id = a.department_id)
+ WHERE $apwhere
+ ORDER BY $sortorder|;
+
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
+
+ # gl
+ if ($ref->{type} eq "gl") {
+ $ref->{module} = "gl";
+ }
+
+ # ap
+ if ($ref->{type} eq "ap") {
+
+ if ($ref->{invoice}) {
+ $ref->{module} = "ir";
+ } else {
+ $ref->{module} = "ap";
+ }
+ }
+
+ # ar
+ if ($ref->{type} eq "ar") {
+
+ if ($ref->{invoice}) {
+ $ref->{module} = ($ref->{till}) ? "ps" : "is";
+ } else {
+ $ref->{module} = "ar";
+ }
+ }
+
+ if ($ref->{amount} < 0) {
+ $ref->{debit} = $ref->{amount} * -1;
+ $ref->{credit} = 0;
+ } else {
+ $ref->{credit} = $ref->{amount};
+ $ref->{debit} = 0;
+ }
+
+ push @{ $form->{GL} }, $ref;
+ }
+
+ $sth->finish;
+ $dbh->disconnect;
+}
+
+
+sub transaction {
+
+ my ($self, $myconfig, $form) = @_;
+
+ my ($query, $sth, $ref);
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ if ($form->{id}) {
+
+ $query = "SELECT closedto, revtrans
+ FROM defaults";
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ ($form->{closedto}, $form->{revtrans}) = $sth->fetchrow_array;
+ $sth->finish;
+
+ $query = qq|SELECT g.*, d.description AS department
+ FROM gl g
+ LEFT JOIN department d ON (d.id = g.department_id)
+ WHERE g.id = $form->{id}|;
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ $ref = $sth->fetchrow_hashref(NAME_lc);
+ for (keys %$ref) { $form->{$_} = $ref->{$_} }
+ $sth->finish;
+
+ # retrieve individual rows
+ $query = qq|SELECT ac.*, c.accno, c.description, p.projectnumber
+ FROM acc_trans ac
+ JOIN chart c ON (ac.chart_id = c.id)
+ LEFT JOIN project p ON (p.id = ac.project_id)
+ WHERE ac.trans_id = $form->{id}
+ ORDER BY accno|;
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+
+ if ($ref->{fx_transaction}) {
+ $form->{transfer} = 1;
+ }
+ push @{ $form->{GL} }, $ref;
+ }
+
+ # get recurring transaction
+ $form->get_recurring($dbh);
+
+ } else {
+ $query = "SELECT current_date AS transdate, closedto, revtrans
+ FROM defaults";
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ ($form->{transdate}, $form->{closedto}, $form->{revtrans}) = $sth->fetchrow_array;
+ }
+
+ $sth->finish;
+
+ # get chart of accounts
+ $query = qq|SELECT accno,description
+ FROM chart
+ WHERE charttype = 'A'
+ ORDER BY accno|;
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+ push @{ $form->{all_accno} }, $ref;
+ }
+
+ $sth->finish;
+
+ # get departments
+ $form->all_departments($myconfig, $dbh);
+
+ # get projects
+ $form->all_projects($myconfig, $dbh, $form->{transdate});
+
+ $dbh->disconnect;
+
+}
+
+1;
diff --git a/LedgerSMB/HR.pm b/LedgerSMB/HR.pm
new file mode 100755
index 00000000..5ff8c253
--- /dev/null
+++ b/LedgerSMB/HR.pm
@@ -0,0 +1,555 @@
+#=====================================================================
+# LedgerSMB
+# Small Medium Business Accounting software
+#
+# See COPYRIGHT file for copyright information
+#======================================================================
+#
+# This file has NOT undergone whitespace cleanup.
+#
+#======================================================================
+#
+# backend code for human resources and payroll
+#
+#======================================================================
+
+package HR;
+
+
+sub get_employee {
+ my ($self, $myconfig, $form) = @_;
+
+ my $dbh = $form->dbconnect($myconfig);
+
+ my $query;
+ my $sth;
+ my $ref;
+ my $notid = "";
+
+ if ($form->{id}) {
+ $query = qq|SELECT e.*
+ FROM employee e
+ WHERE e.id = $form->{id}|;
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ $ref = $sth->fetchrow_hashref(NAME_lc);
+
+ # check if employee can be deleted, orphaned
+ $form->{status} = "orphaned" unless $ref->{login};
+
+$form->{status} = 'orphaned'; # leave orphaned for now until payroll is done
+
+ $ref->{employeelogin} = $ref->{login};
+ delete $ref->{login};
+ for (keys %$ref) { $form->{$_} = $ref->{$_} }
+
+ $sth->finish;
+
+ # get manager
+ $form->{managerid} *= 1;
+ $query = qq|SELECT name
+ FROM employee
+ WHERE id = $form->{managerid}|;
+ ($form->{manager}) = $dbh->selectrow_array($query);
+
+
+######### disabled for now
+if ($form->{deductions}) {
+ # get allowances
+ $query = qq|SELECT d.id, d.description, da.before, da.after, da.rate
+ FROM employeededuction da
+ JOIN deduction d ON (da.deduction_id = d.id)
+ WHERE da.employee_id = $form->{id}|;
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+ $ref->{rate} *= 100;
+ push @{ $form->{all_employeededuction} }, $ref;
+ }
+ $sth->finish;
+}
+
+ $notid = qq|AND id != $form->{id}|;
+
+ } else {
+
+ $query = qq|SELECT current_date FROM defaults|;
+ ($form->{startdate}) = $dbh->selectrow_array($query);
+
+ }
+
+ # get managers
+ $query = qq|SELECT id, name
+ FROM employee
+ WHERE sales = '1'
+ AND role = 'manager'
+ $notid
+ ORDER BY 2|;
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+ push @{ $form->{all_manager} }, $ref;
+ }
+ $sth->finish;
+
+
+ # get deductions
+if ($form->{deductions}) {
+ $query = qq|SELECT id, description
+ FROM deduction
+ ORDER BY 2|;
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+ push @{ $form->{all_deduction} }, $ref;
+ }
+ $sth->finish;
+}
+
+ $dbh->disconnect;
+
+}
+
+
+
+sub save_employee {
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect_noauto($myconfig);
+ my $query;
+ my $sth;
+
+ if (! $form->{id}) {
+ my $uid = localtime;
+ $uid .= "$$";
+
+ $query = qq|INSERT INTO employee (name)
+ VALUES ('$uid')|;
+ $dbh->do($query) || $form->dberror($query);
+
+ $query = qq|SELECT id FROM employee
+ WHERE name = '$uid'|;
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ ($form->{id}) = $sth->fetchrow_array;
+ $sth->finish;
+ }
+
+ my ($null, $managerid) = split /--/, $form->{manager};
+ $managerid *= 1;
+ $form->{sales} *= 1;
+
+ $form->{employeenumber} = $form->update_defaults($myconfig, "employeenumber", $dbh) if ! $form->{employeenumber};
+
+ $query = qq|UPDATE employee SET
+ employeenumber = |.$dbh->quote($form->{employeenumber}).qq|,
+ name = |.$dbh->quote($form->{name}).qq|,
+ address1 = |.$dbh->quote($form->{address1}).qq|,
+ address2 = |.$dbh->quote($form->{address2}).qq|,
+ city = |.$dbh->quote($form->{city}).qq|,
+ state = |.$dbh->quote($form->{state}).qq|,
+ zipcode = |.$dbh->quote($form->{zipcode}).qq|,
+ country = |.$dbh->quote($form->{country}).qq|,
+ workphone = '$form->{workphone}',
+ homephone = '$form->{homephone}',
+ startdate = |.$form->dbquote($form->{startdate}, SQL_DATE).qq|,
+ enddate = |.$form->dbquote($form->{enddate}, SQL_DATE).qq|,
+ notes = |.$dbh->quote($form->{notes}).qq|,
+ role = '$form->{role}',
+ sales = '$form->{sales}',
+ email = |.$dbh->quote($form->{email}).qq|,
+ ssn = '$form->{ssn}',
+ dob = |.$form->dbquote($form->{dob}, SQL_DATE).qq|,
+ iban = '$form->{iban}',
+ bic = '$form->{bic}',
+ managerid = $managerid
+ WHERE id = $form->{id}|;
+ $dbh->do($query) || $form->dberror($query);
+
+# for now
+if ($form->{selectdeduction}) {
+ # insert deduction and allowances for payroll
+ $query = qq|DELETE FROM employeededuction
+ WHERE employee_id = $form->{id}|;
+ $dbh->do($query) || $form->dberror($query);
+
+ $query = qq|INSERT INTO employeededuction (employee_id, deduction_id,
+ before, after, rate) VALUES ($form->{id},?,?,?,?)|;
+ my $sth = $dbh->prepare($query) || $form->dberror($query);
+
+ for ($i = 1; $i <= $form->{deduction_rows}; $i++) {
+ for (qw(before after)) { $form->{"${_}_$i"} = $form->parse_amount($myconfig, $form->{"${_}_$i"}) }
+ ($null, $deduction_id) = split /--/, $form->{"deduction_$i"};
+ if ($deduction_id) {
+ $sth->execute($deduction_id, $form->{"before_$i"}, $form->{"after_$i"}, $form->{"rate_$i"} / 100) || $form->dberror($query);
+ }
+ }
+ $sth->finish;
+}
+
+ $dbh->commit;
+ $dbh->disconnect;
+
+}
+
+
+sub delete_employee {
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect_noauto($myconfig);
+
+ # delete employee
+ my $query = qq|DELETE FROM $form->{db}
+ WHERE id = $form->{id}|;
+ $dbh->do($query) || $form->dberror($query);
+
+ $dbh->commit;
+ $dbh->disconnect;
+
+}
+
+
+sub employees {
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ my $where = "1 = 1";
+ $form->{sort} = ($form->{sort}) ? $form->{sort} : "name";
+ my @a = qw(name);
+ my $sortorder = $form->sort_order(\@a);
+
+ my $var;
+
+ if ($form->{startdatefrom}) {
+ $where .= " AND e.startdate >= '$form->{startdatefrom}'";
+ }
+ if ($form->{startdateto}) {
+ $where .= " AND e.startddate <= '$form->{startdateto}'";
+ }
+ if ($form->{name} ne "") {
+ $var = $form->like(lc $form->{name});
+ $where .= " AND lower(e.name) LIKE '$var'";
+ }
+ if ($form->{notes} ne "") {
+ $var = $form->like(lc $form->{notes});
+ $where .= " AND lower(e.notes) LIKE '$var'";
+ }
+ if ($form->{sales} eq 'Y') {
+ $where .= " AND e.sales = '1'";
+ }
+ if ($form->{status} eq 'orphaned') {
+ $where .= qq| AND e.login IS NULL|;
+ }
+ if ($form->{status} eq 'active') {
+ $where .= qq| AND e.enddate IS NULL|;
+ }
+ if ($form->{status} eq 'inactive') {
+ $where .= qq| AND e.enddate <= current_date|;
+ }
+
+ my $query = qq|SELECT e.*, m.name AS manager
+ FROM employee e
+ LEFT JOIN employee m ON (m.id = e.managerid)
+ WHERE $where
+ ORDER BY $sortorder|;
+
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
+ $ref->{address} = "";
+ for (qw(address1 address2 city state zipcode country)) { $ref->{address} .= "$ref->{$_} " }
+ push @{ $form->{all_employee} }, $ref;
+ }
+
+ $sth->finish;
+ $dbh->disconnect;
+
+}
+
+
+sub get_deduction {
+ my ($self, $myconfig, $form) = @_;
+
+ my $dbh = $form->dbconnect($myconfig);
+ my $query;
+ my $sth;
+ my $ref;
+ my $item;
+ my $i;
+
+ if ($form->{id}) {
+ $query = qq|SELECT d.*,
+ c1.accno AS ap_accno,
+ c1.description AS ap_description,
+ c2.accno AS expense_accno,
+ c2.description AS expense_description
+ FROM deduction d
+ LEFT JOIN chart c1 ON (c1.id = d.ap_accno_id)
+ LEFT JOIN chart c2 ON (c2.id = d.expense_accno_id)
+ WHERE d.id = $form->{id}|;
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ $ref = $sth->fetchrow_hashref(NAME_lc);
+ for (keys %$ref) { $form->{$_} = $ref->{$_} }
+
+ $sth->finish;
+
+ # check if orphaned
+$form->{status} = 'orphaned'; # for now
+
+
+ # get the rates
+ $query = qq|SELECT rate, amount, above, below
+ FROM deductionrate
+ WHERE trans_id = $form->{id}
+ ORDER BY rate, amount|;
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+ push @{ $form->{deductionrate} }, $ref;
+ }
+ $sth->finish;
+
+ # get all for deductionbase
+ $query = qq|SELECT d.description, d.id, db.maximum
+ FROM deductionbase db
+ JOIN deduction d ON (d.id = db.deduction_id)
+ WHERE db.trans_id = $form->{id}|;
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+ push @{ $form->{deductionbase} }, $ref;
+ }
+ $sth->finish;
+
+ # get all for deductionafter
+ $query = qq|SELECT d.description, d.id
+ FROM deductionafter da
+ JOIN deduction d ON (d.id = da.deduction_id)
+ WHERE da.trans_id = $form->{id}|;
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+ push @{ $form->{deductionafter} }, $ref;
+ }
+ $sth->finish;
+
+ # build selection list for base and after
+ $query = qq|SELECT id, description
+ FROM deduction
+ WHERE id != $form->{id}
+ ORDER BY 2|;
+
+ } else {
+ # build selection list for base and after
+ $query = qq|SELECT id, description
+ FROM deduction
+ ORDER BY 2|;
+ }
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+ push @{ $form->{all_deduction} }, $ref;
+ }
+ $sth->finish;
+
+
+ my %category = ( ap => 'L',
+ expense => 'E' );
+
+ foreach $item (keys %category) {
+ $query = qq|SELECT accno, description
+ FROM chart
+ WHERE charttype = 'A'
+ AND category = '$category{$item}'
+ ORDER BY accno|;
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+ push @{ $form->{"${item}_accounts"} }, $ref;
+ }
+ $sth->finish;
+ }
+
+
+ $dbh->disconnect;
+
+}
+
+
+sub deductions {
+ my ($self, $myconfig, $form) = @_;
+
+ my $dbh = $form->dbconnect($myconfig);
+
+ my $query = qq|SELECT d.id, d.description, d.employeepays, d.employerpays,
+ c1.accno AS ap_accno, c2.accno AS expense_accno,
+ dr.rate, dr.amount, dr.above, dr.below
+ FROM deduction d
+ JOIN deductionrate dr ON (dr.trans_id = d.id)
+ LEFT JOIN chart c1 ON (d.ap_accno_id = c1.id)
+ LEFT JOIN chart c2 ON (d.expense_accno_id = c2.id)
+ ORDER BY 2, 7, 8|;
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
+ push @{ $form->{all_deduction} }, $ref;
+ }
+
+ $sth->finish;
+ $dbh->disconnect;
+
+}
+
+
+sub save_deduction {
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect_noauto($myconfig);
+
+ ($form->{ap_accno}) = split /--/, $form->{ap_accno};
+ ($form->{expense_accno}) = split /--/, $form->{expense_accno};
+
+ my $null;
+ my $deduction_id;
+ my $query;
+ my $sth;
+
+ if (! $form->{id}) {
+ my $uid = localtime;
+ $uid .= "$$";
+
+ $query = qq|INSERT INTO deduction (description)
+ VALUES ('$uid')|;
+ $dbh->do($query) || $form->dberror($query);
+
+ $query = qq|SELECT id FROM deduction
+ WHERE description = '$uid'|;
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ ($form->{id}) = $sth->fetchrow_array;
+ $sth->finish;
+ }
+
+
+ for (qw(employeepays employerpays)) { $form->{$_} = $form->parse_amount($myconfig, $form->{$_}) }
+
+ $query = qq|UPDATE deduction SET
+ description = |.$dbh->quote($form->{description}).qq|,
+ ap_accno_id =
+ (SELECT id FROM chart
+ WHERE accno = '$form->{ap_accno}'),
+ expense_accno_id =
+ (SELECT id FROM chart
+ WHERE accno = '$form->{expense_accno}'),
+ employerpays = '$form->{employerpays}',
+ employeepays = '$form->{employeepays}',
+ fromage = |.$form->dbquote($form->{fromage}, SQL_INT).qq|,
+ toage = |.$form->dbquote($form->{toage}, SQL_INT).qq|
+ WHERE id = $form->{id}|;
+ $dbh->do($query) || $form->dberror($query);
+
+
+ $query = qq|DELETE FROM deductionrate
+ WHERE trans_id = $form->{id}|;
+ $dbh->do($query) || $form->dberror($query);
+
+ $query = qq|INSERT INTO deductionrate
+ (trans_id, rate, amount, above, below) VALUES (?,?,?,?,?)|;
+ $sth = $dbh->prepare($query) || $form->dberror($query);
+
+ for ($i = 1; $i <= $form->{rate_rows}; $i++) {
+ for (qw(rate amount above below)) { $form->{"${_}_$i"} = $form->parse_amount($myconfig, $form->{"${_}_$i"}) }
+ $form->{"rate_$i"} /= 100;
+
+ if ($form->{"rate_$i"} || $form->{"amount_$i"}) {
+ $sth->execute($form->{id}, $form->{"rate_$i"}, $form->{"amount_$i"}, $form->{"above_$i"}, $form->{"below_$i"}) || $form->dberror($query);
+ }
+ }
+ $sth->finish;
+
+
+ $query = qq|DELETE FROM deductionbase
+ WHERE trans_id = $form->{id}|;
+ $dbh->do($query) || $form->dberror($query);
+
+ $query = qq|INSERT INTO deductionbase
+ (trans_id, deduction_id, maximum) VALUES (?,?,?)|;
+ $sth = $dbh->prepare($query) || $form->dberror($query);
+
+ for ($i = 1; $i <= $form->{base_rows}; $i++) {
+ ($null, $deduction_id) = split /--/, $form->{"base_$i"};
+ $form->{"maximum_$i"} = $form->parse_amount($myconfig, $form->{"maximum_$i"});
+ if ($deduction_id) {
+ $sth->execute($form->{id}, $deduction_id, $form->{"maximum_$i"}) || $form->dberror($query);
+ }
+ }
+ $sth->finish;
+
+
+ $query = qq|DELETE FROM deductionafter
+ WHERE trans_id = $form->{id}|;
+ $dbh->do($query) || $form->dberror($query);
+
+ $query = qq|INSERT INTO deductionafter
+ (trans_id, deduction_id) VALUES (?,?)|;
+ $sth = $dbh->prepare($query) || $form->dberror($query);
+
+ for ($i = 1; $i <= $form->{after_rows}; $i++) {
+ ($null, $deduction_id) = split /--/, $form->{"after_$i"};
+ if ($deduction_id) {
+ $sth->execute($form->{id}, $deduction_id) || $form->dberror($query);
+ }
+ }
+ $sth->finish;
+
+ $dbh->commit;
+ $dbh->disconnect;
+
+}
+
+
+sub delete_deduction {
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect_noauto($myconfig);
+
+ # delete deduction
+ my $query = qq|DELETE FROM $form->{db}
+ WHERE id = $form->{id}|;
+ $dbh->do($query) || $form->dberror($query);
+
+ foreach $item (qw(rate base after)) {
+ $query = qq|DELETE FROM deduction$item
+ WHERE trans_id = $form->{id}|;
+ $dbh->do($query) || $form->dberror($query);
+ }
+
+ $dbh->commit;
+ $dbh->disconnect;
+
+}
+
+1;
+
diff --git a/LedgerSMB/IC.pm b/LedgerSMB/IC.pm
new file mode 100755
index 00000000..52ad9654
--- /dev/null
+++ b/LedgerSMB/IC.pm
@@ -0,0 +1,1714 @@
+#=====================================================================
+# LedgerSMB
+# Small Medium Business Accounting software
+#
+# See COPYRIGHT file for copyright information
+#======================================================================
+#
+# This file has NOT undergone whitespace cleanup.
+#
+#======================================================================
+#
+# Inventory Control backend
+#
+#======================================================================
+
+package IC;
+
+
+sub get_part {
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to db
+ my $dbh = $form->dbconnect($myconfig);
+ my $i;
+
+ my $query = qq|SELECT p.*,
+ c1.accno AS inventory_accno, c1.description AS inventory_description,
+ c2.accno AS income_accno, c2.description AS income_description,
+ c3.accno AS expense_accno, c3.description AS expense_description,
+ pg.partsgroup
+ FROM parts p
+ LEFT JOIN chart c1 ON (p.inventory_accno_id = c1.id)
+ LEFT JOIN chart c2 ON (p.income_accno_id = c2.id)
+ LEFT JOIN chart c3 ON (p.expense_accno_id = c3.id)
+ LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
+ WHERE p.id = $form->{id}|;
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+ my $ref = $sth->fetchrow_hashref(NAME_lc);
+
+ # copy to $form variables
+ for (keys %$ref) { $form->{$_} = $ref->{$_} }
+ $sth->finish;
+
+ my %oid = ('Pg' => 'a.oid',
+ 'PgPP' => 'a.oid',
+ 'Oracle' => 'a.rowid',
+ 'DB2' => '1=1'
+ );
+
+ # part, service item or labor
+ $form->{item} = ($form->{inventory_accno_id}) ? 'part' : 'service';
+ $form->{item} = 'labor' if ! $form->{income_accno_id};
+
+ if ($form->{assembly}) {
+ $form->{item} = 'assembly';
+
+ # retrieve assembly items
+ $query = qq|SELECT p.id, p.partnumber, p.description,
+ p.sellprice, p.weight, a.qty, a.bom, a.adj, p.unit,
+ p.lastcost, p.listprice,
+ pg.partsgroup, p.assembly, p.partsgroup_id
+ FROM parts p
+ JOIN assembly a ON (a.parts_id = p.id)
+ LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
+ WHERE a.id = $form->{id}
+ ORDER BY $oid{$myconfig->{dbdriver}}|;
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ $form->{assembly_rows} = 0;
+ while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
+ $form->{assembly_rows}++;
+ foreach my $key ( keys %{ $ref } ) {
+ $form->{"${key}_$form->{assembly_rows}"} = $ref->{$key};
+ }
+ }
+ $sth->finish;
+
+ }
+
+ # setup accno hash for <option checked> {amount} is used in create_links
+ for (qw(inventory income expense)) { $form->{amount}{"IC_$_"} = { accno => $form->{"${_}_accno"}, description => $form->{"${_}_description"} } }
+
+
+ if ($form->{item} =~ /(part|assembly)/) {
+ # get makes
+ if ($form->{makemodel} ne "") {
+ $query = qq|SELECT make, model
+ FROM makemodel
+ WHERE parts_id = $form->{id}|;
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+ push @{ $form->{makemodels} }, $ref;
+ }
+ $sth->finish;
+ }
+ }
+
+ # now get accno for taxes
+ $query = qq|SELECT c.accno
+ FROM chart c, partstax pt
+ WHERE pt.chart_id = c.id
+ AND pt.parts_id = $form->{id}|;
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while (($key) = $sth->fetchrow_array) {
+ $form->{amount}{$key} = $key;
+ }
+
+ $sth->finish;
+
+ # is it an orphan
+ $query = qq|SELECT parts_id
+ FROM invoice
+ WHERE parts_id = $form->{id}
+ UNION
+ SELECT parts_id
+ FROM orderitems
+ WHERE parts_id = $form->{id}
+ UNION
+ SELECT parts_id
+ FROM assembly
+ WHERE parts_id = $form->{id}
+ UNION
+ SELECT parts_id
+ FROM jcitems
+ WHERE parts_id = $form->{id}|;
+ ($form->{orphaned}) = $dbh->selectrow_array($query);
+ $form->{orphaned} = !$form->{orphaned};
+
+ $form->{orphaned} = 0 if $form->{project_id};
+
+ if ($form->{item} eq 'assembly') {
+ if ($form->{orphaned}) {
+ $form->{orphaned} = !$form->{onhand};
+ }
+ }
+
+ if ($form->{item} =~ /(part|service)/) {
+ # get vendors
+ $query = qq|SELECT v.id, v.name, pv.partnumber,
+ pv.lastcost, pv.leadtime, pv.curr AS vendorcurr
+ FROM partsvendor pv
+ JOIN vendor v ON (v.id = pv.vendor_id)
+ WHERE pv.parts_id = $form->{id}
+ ORDER BY 2|;
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+ push @{ $form->{vendormatrix} }, $ref;
+ }
+ $sth->finish;
+ }
+
+ # get matrix
+ if ($form->{item} ne 'labor') {
+ $query = qq|SELECT pc.pricebreak, pc.sellprice AS customerprice,
+ pc.curr AS customercurr,
+ pc.validfrom, pc.validto,
+ c.name, c.id AS cid, g.pricegroup, g.id AS gid
+ FROM partscustomer pc
+ LEFT JOIN customer c ON (c.id = pc.customer_id)
+ LEFT JOIN pricegroup g ON (g.id = pc.pricegroup_id)
+ WHERE pc.parts_id = $form->{id}
+ ORDER BY c.name, g.pricegroup, pc.pricebreak|;
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+ push @{ $form->{customermatrix} }, $ref;
+ }
+ $sth->finish;
+ }
+
+ $dbh->disconnect;
+
+}
+
+
+sub save {
+ my ($self, $myconfig, $form) = @_;
+
+ ($form->{inventory_accno}) = split(/--/, $form->{IC_inventory});
+ ($form->{expense_accno}) = split(/--/, $form->{IC_expense});
+ ($form->{income_accno}) = split(/--/, $form->{IC_income});
+
+ # connect to database, turn off AutoCommit
+ my $dbh = $form->dbconnect_noauto($myconfig);
+
+ # save the part
+ # make up a unique handle and store in partnumber field
+ # then retrieve the record based on the unique handle to get the id
+ # replace the partnumber field with the actual variable
+ # add records for makemodel
+
+ # if there is a $form->{id} then replace the old entry
+ # delete all makemodel entries and add the new ones
+
+ # undo amount formatting
+ for (qw(rop weight listprice sellprice lastcost stock)) { $form->{$_} = $form->parse_amount($myconfig, $form->{$_}) }
+
+ $form->{makemodel} = (($form->{make_1}) || ($form->{model_1})) ? 1 : 0;
+
+ $form->{assembly} = ($form->{item} eq 'assembly') ? 1 : 0;
+ for (qw(alternate obsolete onhand)) { $form->{$_} *= 1 }
+
+ my $query;
+ my $sth;
+ my $i;
+ my $null;
+ my $vendor_id;
+ my $customer_id;
+
+ if ($form->{id}) {
+
+ # get old price
+ $query = qq|SELECT id, listprice, sellprice, lastcost, weight, project_id
+ FROM parts
+ WHERE id = $form->{id}|;
+ my ($id, $listprice, $sellprice, $lastcost, $weight, $project_id) = $dbh->selectrow_array($query);
+
+ if ($id) {
+
+ if (!$project_id) {
+ # if item is part of an assembly adjust all assemblies
+ $query = qq|SELECT id, qty, adj
+ FROM assembly
+ WHERE parts_id = $form->{id}|;
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+ while (my ($id, $qty, $adj) = $sth->fetchrow_array) {
+ &update_assembly($dbh, $form, $id, $qty, $adj, $listprice * 1, $sellprice * 1, $lastcost * 1, $weight * 1);
+ }
+ $sth->finish;
+ }
+
+ if ($form->{item} =~ /(part|service)/) {
+ # delete partsvendor records
+ $query = qq|DELETE FROM partsvendor
+ WHERE parts_id = $form->{id}|;
+ $dbh->do($query) || $form->dberror($query);
+ }
+
+ if ($form->{item} !~ /(service|labor)/) {
+ # delete makemodel records
+ $query = qq|DELETE FROM makemodel
+ WHERE parts_id = $form->{id}|;
+ $dbh->do($query) || $form->dberror($query);
+ }
+
+ if ($form->{item} eq 'assembly') {
+
+ if ($form->{onhand}) {
+ &adjust_inventory($dbh, $form, $form->{id}, $form->{onhand} * -1);
+ }
+
+ if ($form->{orphaned}) {
+ # delete assembly records
+ $query = qq|DELETE FROM assembly
+ WHERE id = $form->{id}|;
+ $dbh->do($query) || $form->dberror($query);
+ } else {
+
+ for $i (1 .. $form->{assembly_rows} - 1) {
+ # update BOM, A only
+ for (qw(bom adj)) { $form->{"${_}_$i"} *= 1 }
+
+ $query = qq|UPDATE assembly SET
+ bom = '$form->{"bom_$i"}',
+ adj = '$form->{"adj_$i"}'
+ WHERE id = $form->{id}
+ AND parts_id = $form->{"id_$i"}|;
+ $dbh->do($query) || $form->dberror($query);
+ }
+ }
+
+ $form->{onhand} += $form->{stock};
+
+ }
+
+ # delete tax records
+ $query = qq|DELETE FROM partstax
+ WHERE parts_id = $form->{id}|;
+ $dbh->do($query) || $form->dberror($query);
+
+ # delete matrix
+ $query = qq|DELETE FROM partscustomer
+ WHERE parts_id = $form->{id}|;
+ $dbh->do($query) || $form->dberror($query);
+
+ } else {
+ $query = qq|INSERT INTO parts (id)
+ VALUES ($form->{id})|;
+ $dbh->do($query) || $form->dberror($query);
+ }
+
+ }
+
+
+ if (!$form->{id}) {
+ my $uid = localtime;
+ $uid .= "$$";
+
+ $query = qq|INSERT INTO parts (partnumber)
+ VALUES ('$uid')|;
+ $dbh->do($query) || $form->dberror($query);
+
+ $query = qq|SELECT id FROM parts
+ WHERE partnumber = '$uid'|;
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+ ($form->{id}) = $sth->fetchrow_array;
+ $sth->finish;
+
+ $form->{orphaned} = 1;
+ $form->{onhand} = ($form->{stock} * 1) if $form->{item} eq 'assembly';
+
+ }
+
+ my $partsgroup_id;
+ ($null, $partsgroup_id) = split /--/, $form->{partsgroup};
+ $partsgroup_id *= 1;
+
+ $form->{partnumber} = $form->update_defaults($myconfig, "partnumber", $dbh) if ! $form->{partnumber};
+
+ $query = qq|UPDATE parts SET
+ partnumber = |.$dbh->quote($form->{partnumber}).qq|,
+ description = |.$dbh->quote($form->{description}).qq|,
+ makemodel = '$form->{makemodel}',
+ alternate = '$form->{alternate}',
+ assembly = '$form->{assembly}',
+ listprice = $form->{listprice},
+ sellprice = $form->{sellprice},
+ lastcost = $form->{lastcost},
+ weight = $form->{weight},
+ priceupdate = |.$form->dbquote($form->{priceupdate}, SQL_DATE).qq|,
+ unit = |.$dbh->quote($form->{unit}).qq|,
+ notes = |.$dbh->quote($form->{notes}).qq|,
+ rop = $form->{rop},
+ bin = |.$dbh->quote($form->{bin}).qq|,
+ inventory_accno_id = (SELECT id FROM chart
+ WHERE accno = '$form->{inventory_accno}'),
+ income_accno_id = (SELECT id FROM chart
+ WHERE accno = '$form->{income_accno}'),
+ expense_accno_id = (SELECT id FROM chart
+ WHERE accno = '$form->{expense_accno}'),
+ obsolete = '$form->{obsolete}',
+ image = '$form->{image}',
+ drawing = '$form->{drawing}',
+ microfiche = '$form->{microfiche}',
+ partsgroup_id = $partsgroup_id
+ WHERE id = $form->{id}|;
+ $dbh->do($query) || $form->dberror($query);
+
+
+ # insert makemodel records
+ if ($form->{item} =~ /(part|assembly)/) {
+ for $i (1 .. $form->{makemodel_rows}) {
+ if (($form->{"make_$i"} ne "") || ($form->{"model_$i"} ne "")) {
+ $query = qq|INSERT INTO makemodel (parts_id, make, model)
+ VALUES ($form->{id},|
+ .$dbh->quote($form->{"make_$i"}).qq|, |
+ .$dbh->quote($form->{"model_$i"}).qq|)|;
+ $dbh->do($query) || $form->dberror($query);
+ }
+ }
+ }
+
+
+ # insert taxes
+ for (split / /, $form->{taxaccounts}) {
+ if ($form->{"IC_tax_$_"}) {
+ $query = qq|INSERT INTO partstax (parts_id, chart_id)
+ VALUES ($form->{id},
+ (SELECT id
+ FROM chart
+ WHERE accno = '$_'))|;
+ $dbh->do($query) || $form->dberror($query);
+ }
+ }
+
+
+ @a = localtime;
+ $a[5] += 1900;
+ $a[4]++;
+ $a[4] = substr("0$a[4]", -2);
+ $a[3] = substr("0$a[3]", -2);
+ my $shippingdate = "$a[5]$a[4]$a[3]";
+
+ ($form->{employee}, $form->{employee_id}) = $form->get_employee($dbh);
+
+ # add assembly records
+ if ($form->{item} eq 'assembly' && !$project_id) {
+
+ if ($form->{orphaned}) {
+ for $i (1 .. $form->{assembly_rows}) {
+ $form->{"qty_$i"} = $form->parse_amount($myconfig, $form->{"qty_$i"});
+
+ if ($form->{"qty_$i"}) {
+ for (qw(bom adj)) { $form->{"${_}_$i"} *= 1 }
+ $query = qq|INSERT INTO assembly (id, parts_id, qty, bom, adj)
+ VALUES ($form->{id}, $form->{"id_$i"},
+ $form->{"qty_$i"}, '$form->{"bom_$i"}',
+ '$form->{"adj_$i"}')|;
+ $dbh->do($query) || $form->dberror($query);
+ }
+ }
+ }
+
+ # adjust onhand for the parts
+ if ($form->{onhand}) {
+ &adjust_inventory($dbh, $form, $form->{id}, $form->{onhand});
+ }
+
+ }
+
+ # add vendors
+ if ($form->{item} ne 'assembly') {
+ $updparts{$form->{id}} = 1;
+
+ for $i (1 .. $form->{vendor_rows}) {
+ if (($form->{"vendor_$i"} ne "") && $form->{"lastcost_$i"}) {
+
+ ($null, $vendor_id) = split /--/, $form->{"vendor_$i"};
+
+ for (qw(lastcost leadtime)) { $form->{"${_}_$i"} = $form->parse_amount($myconfig, $form->{"${_}_$i"})}
+
+ $query = qq|INSERT INTO partsvendor (vendor_id, parts_id, partnumber,
+ lastcost, leadtime, curr)
+ VALUES ($vendor_id, $form->{id},|
+ .$dbh->quote($form->{"partnumber_$i"}).qq|,
+ $form->{"lastcost_$i"},
+ $form->{"leadtime_$i"}, '$form->{"vendorcurr_$i"}')|;
+ $dbh->do($query) || $form->dberror($query);
+ }
+ }
+ }
+
+
+ # add pricematrix
+ for $i (1 .. $form->{customer_rows}) {
+
+ for (qw(pricebreak customerprice)) { $form->{"${_}_$i"} = $form->parse_amount($myconfig, $form->{"${_}_$i"})}
+
+ if ($form->{"customerprice_$i"}) {
+
+ ($null, $customer_id) = split /--/, $form->{"customer_$i"};
+ $customer_id *= 1;
+
+ ($null, $pricegroup_id) = split /--/, $form->{"pricegroup_$i"};
+ $pricegroup_id *= 1;
+
+ $query = qq|INSERT INTO partscustomer (parts_id, customer_id,
+ pricegroup_id, pricebreak, sellprice, curr,
+ validfrom, validto)
+ VALUES ($form->{id}, $customer_id,
+ $pricegroup_id, $form->{"pricebreak_$i"},
+ $form->{"customerprice_$i"}, '$form->{"customercurr_$i"}',|
+ .$form->dbquote($form->{"validfrom_$i"}, SQL_DATE).qq|, |
+ .$form->dbquote($form->{"validto_$i"}, SQL_DATE).qq|)|;
+ $dbh->do($query) || $form->dberror($query);
+ }
+ }
+
+ # commit
+ my $rc = $dbh->commit;
+ $dbh->disconnect;
+
+ $rc;
+
+}
+
+
+
+sub update_assembly {
+ my ($dbh, $form, $id, $qty, $adj, $listprice, $sellprice, $lastcost, $weight) = @_;
+
+ my $formlistprice = $form->{listprice};
+ my $formsellprice = $form->{sellprice};
+
+ if (!$adj) {
+ $formlistprice = $listprice;
+ $formsellprice = $sellprice;
+ }
+
+ my $query = qq|SELECT id, qty, adj
+ FROM assembly
+ WHERE parts_id = $id|;
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ $form->{$id} = 1;
+
+ while (my ($pid, $aqty, $aadj) = $sth->fetchrow_array) {
+ &update_assembly($dbh, $form, $pid, $aqty * $qty, $aadj, $listprice, $sellprice, $lastcost, $weight) if !$form->{$pid};
+ }
+ $sth->finish;
+
+ $query = qq|UPDATE parts
+ SET listprice = listprice +
+ $qty * ($formlistprice - $listprice),
+ sellprice = sellprice +
+ $qty * ($formsellprice - $sellprice),
+ lastcost = lastcost +
+ $qty * ($form->{lastcost} - $lastcost),
+ weight = weight +
+ $qty * ($form->{weight} - $weight)
+ WHERE id = $id|;
+ $dbh->do($query) || $form->dberror($query);
+
+ delete $form->{$id};
+
+}
+
+
+
+sub retrieve_assemblies {
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ my $where = '1 = 1';
+
+ if ($form->{partnumber} ne "") {
+ my $partnumber = $form->like(lc $form->{partnumber});
+ $where .= " AND lower(p.partnumber) LIKE '$partnumber'";
+ }
+
+ if ($form->{description} ne "") {
+ my $description = $form->like(lc $form->{description});
+ $where .= " AND lower(p.description) LIKE '$description'";
+ }
+ $where .= qq| AND p.obsolete = '0'
+ AND p.project_id IS NULL|;
+
+ my %ordinal = ( 'partnumber' => 2,
+ 'description' => 3,
+ 'bin' => 4
+ );
+
+ my @a = qw(partnumber description bin);
+ my $sortorder = $form->sort_order(\@a, \%ordinal);
+
+
+ # retrieve assembly items
+ my $query = qq|SELECT p.id, p.partnumber, p.description,
+ p.bin, p.onhand, p.rop
+ FROM parts p
+ WHERE $where
+ AND p.assembly = '1'
+ ORDER BY $sortorder|;
+
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ $query = qq|SELECT sum(p.inventory_accno_id), p.assembly
+ FROM parts p
+ JOIN assembly a ON (a.parts_id = p.id)
+ WHERE a.id = ?
+ GROUP BY p.assembly|;
+ my $svh = $dbh->prepare($query) || $form->dberror($query);
+
+ my $inh;
+ if ($form->{checkinventory}) {
+ $query = qq|SELECT p.id, p.onhand, a.qty
+ FROM parts p
+ JOIN assembly a ON (a.parts_id = p.id)
+ WHERE (p.inventory_accno_id > 0 OR p.assembly)
+ AND p.income_accno_id > 0
+ AND a.id = ?|;
+ $inh = $dbh->prepare($query) || $form->dberror($query);
+ }
+
+ my %available = ();
+ my %required;
+ my $ref;
+ my $aref;
+ my $stock;
+ my $howmany;
+ my $ok;
+
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+ $svh->execute($ref->{id});
+ ($ref->{inventory}, $ref->{assembly}) = $svh->fetchrow_array;
+ $svh->finish;
+
+ if ($ref->{inventory} || $ref->{assembly}) {
+ $ok = 1;
+ if ($form->{checkinventory}) {
+ $inh->execute($ref->{id}) || $form->dberror($query);;
+ $ok = 0;
+ %required = ();
+
+ while ($aref = $inh->fetchrow_hashref(NAME_lc)) {
+ $available{$aref->{id}} = (exists $available{$aref->{id}}) ? $available{$aref->{id}} : $aref->{onhand};
+ $required{$aref->{id}} = $aref->{qty};
+
+ if ($available{$aref->{id}} >= $aref->{qty}) {
+
+ $howmany = ($aref->{qty}) ? int $available{$aref->{id}}/$aref->{qty} : 1;
+ if ($stock) {
+ $stock = ($stock > $howmany) ? $howmany : $stock;
+ } else {
+ $stock = $howmany;
+ }
+ $ok = 1;
+
+ $available{$aref->{id}} -= $aref->{qty} * $stock;
+
+ } else {
+ $ok = 0;
+ for (keys %required) { $available{$_} += $required{$_} * $stock }
+ $stock = 0;
+ last;
+ }
+ }
+ $inh->finish;
+ $ref->{stock} = $stock;
+
+ }
+ push @{ $form->{assembly_items} }, $ref if $ok;
+ }
+ }
+ $sth->finish;
+
+ $dbh->disconnect;
+
+}
+
+
+sub restock_assemblies {
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect_noauto($myconfig);
+
+ for my $i (1 .. $form->{rowcount}) {
+
+ $form->{"qty_$i"} = $form->parse_amount($myconfig, $form->{"qty_$i"});
+
+ if ($form->{"qty_$i"}) {
+ &adjust_inventory($dbh, $form, $form->{"id_$i"}, $form->{"qty_$i"});
+ }
+
+ }
+
+ my $rc = $dbh->commit;
+ $dbh->disconnect;
+
+ $rc;
+
+}
+
+
+sub adjust_inventory {
+ my ($dbh, $form, $id, $qty) = @_;
+
+ my $query = qq|SELECT p.id, p.inventory_accno_id, p.assembly, a.qty
+ FROM parts p
+ JOIN assembly a ON (a.parts_id = p.id)
+ WHERE a.id = $id|;
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
+
+ # is it a service item then loop
+ if (! $ref->{inventory_accno_id}) {
+ next if ! $ref->{assembly}; # assembly
+ }
+
+ # adjust parts onhand
+ $form->update_balance($dbh,
+ "parts",
+ "onhand",
+ qq|id = $ref->{id}|,
+ $qty * $ref->{qty} * -1);
+ }
+
+ $sth->finish;
+
+ # update assembly
+ $form->update_balance($dbh,
+ "parts",
+ "onhand",
+ qq|id = $id|,
+ $qty);
+
+}
+
+
+sub delete {
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database, turn off AutoCommit
+ my $dbh = $form->dbconnect_noauto($myconfig);
+
+ my $query;
+
+ $query = qq|DELETE FROM parts
+ WHERE id = $form->{id}|;
+ $dbh->do($query) || $form->dberror($query);
+
+ $query = qq|DELETE FROM partstax
+ WHERE parts_id = $form->{id}|;
+ $dbh->do($query) || $form->dberror($query);
+
+
+ if ($form->{item} ne 'assembly') {
+ $query = qq|DELETE FROM partsvendor
+ WHERE parts_id = $form->{id}|;
+ $dbh->do($query) || $form->dberror($query);
+ }
+
+ # check if it is a part, assembly or service
+ if ($form->{item} ne 'service') {
+ $query = qq|DELETE FROM makemodel
+ WHERE parts_id = $form->{id}|;
+ $dbh->do($query) || $form->dberror($query);
+ }
+
+ if ($form->{item} eq 'assembly') {
+ $query = qq|DELETE FROM assembly
+ WHERE id = $form->{id}|;
+ $dbh->do($query) || $form->dberror($query);
+ }
+
+ if ($form->{item} eq 'alternate') {
+ $query = qq|DELETE FROM alternate
+ WHERE id = $form->{id}|;
+ $dbh->do($query) || $form->dberror($query);
+ }
+
+ $query = qq|DELETE FROM inventory
+ WHERE parts_id = $form->{id}|;
+ $dbh->do($query) || $form->dberror($query);
+
+ $query = qq|DELETE FROM partscustomer
+ WHERE parts_id = $form->{id}|;
+ $dbh->do($query) || $form->dberror($query);
+
+ $query = qq|DELETE FROM translation
+ WHERE trans_id = $form->{id}|;
+ $dbh->do($query) || $form->dberror($query);
+
+ # commit
+ my $rc = $dbh->commit;
+ $dbh->disconnect;
+
+ $rc;
+
+}
+
+
+sub assembly_item {
+ my ($self, $myconfig, $form) = @_;
+
+ my $i = $form->{assembly_rows};
+ my $var;
+ my $null;
+ my $where = "p.obsolete = '0'";
+
+ if ($form->{"partnumber_$i"} ne "") {
+ $var = $form->like(lc $form->{"partnumber_$i"});
+ $where .= " AND lower(p.partnumber) LIKE '$var'";
+ }
+ if ($form->{"description_$i"} ne "") {
+ $var = $form->like(lc $form->{"description_$i"});
+ $where .= " AND lower(p.description) LIKE '$var'";
+ }
+ if ($form->{"partsgroup_$i"} ne "") {
+ ($null, $var) = split /--/, $form->{"partsgroup_$i"};
+ $where .= qq| AND p.partsgroup_id = $var|;
+ }
+
+ if ($form->{id}) {
+ $where .= " AND p.id != $form->{id}";
+ }
+
+ if ($form->{"description_$i"} ne "") {
+ $where .= " ORDER BY p.description";
+ } else {
+ $where .= " ORDER BY p.partnumber";
+ }
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ my $query = qq|SELECT p.id, p.partnumber, p.description, p.sellprice,
+ p.weight, p.onhand, p.unit, p.lastcost,
+ pg.partsgroup, p.partsgroup_id
+ FROM parts p
+ LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
+ WHERE $where|;
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
+ push @{ $form->{item_list} }, $ref;
+ }
+
+ $sth->finish;
+ $dbh->disconnect;
+
+}
+
+
+sub all_parts {
+ my ($self, $myconfig, $form) = @_;
+
+ my $where = '1 = 1';
+ my $null;
+ my $var;
+ my $ref;
+
+ for (qw(partnumber drawing microfiche)) {
+ if ($form->{$_} ne "") {
+ $var = $form->like(lc $form->{$_});
+ $where .= " AND lower(p.$_) LIKE '$var'";
+ }
+ }
+ # special case for description
+ if ($form->{description} ne "") {
+ unless ($form->{bought} || $form->{sold} || $form->{onorder} || $form->{ordered} || $form->{rfq} || $form->{quoted}) {
+ $var = $form->like(lc $form->{description});
+ $where .= " AND lower(p.description) LIKE '$var'";
+ }
+ }
+
+ # assembly components
+ my $assemblyflds;
+ if ($form->{searchitems} eq 'component') {
+ $assemblyflds = qq|, p1.partnumber AS assemblypartnumber, a.id AS assembly_id|;
+ }
+
+ # special case for serialnumber
+ if ($form->{l_serialnumber}) {
+ if ($form->{serialnumber} ne "") {
+ $var = $form->like(lc $form->{serialnumber});
+ $where .= " AND lower(i.serialnumber) LIKE '$var'";
+ }
+ }
+
+ if (($form->{warehouse} ne "") || $form->{l_warehouse}) {
+ $form->{l_warehouse} = 1;
+ }
+
+ if ($form->{searchitems} eq 'part') {
+ $where .= " AND p.inventory_accno_id > 0 AND p.income_accno_id > 0";
+ }
+ if ($form->{searchitems} eq 'assembly') {
+ $form->{bought} = "";
+ $where .= " AND p.assembly = '1'";
+ }
+ if ($form->{searchitems} eq 'service') {
+ $where .= " AND p.assembly = '0' AND p.inventory_accno_id IS NULL";
+ }
+ if ($form->{searchitems} eq 'labor') {
+ $where .= " AND p.inventory_accno_id > 0 AND p.income_accno_id IS NULL";
+ }
+
+ # items which were never bought, sold or on an order
+ if ($form->{itemstatus} eq 'orphaned') {
+ $where .= qq| AND p.onhand = 0
+ AND p.id NOT IN (SELECT p.id FROM parts p
+ JOIN invoice i ON (p.id = i.parts_id))
+ AND p.id NOT IN (SELECT p.id FROM parts p
+ JOIN assembly a ON (p.id = a.parts_id))
+ AND p.id NOT IN (SELECT p.id FROM parts p
+ JOIN orderitems o ON (p.id = o.parts_id))
+ AND p.id NOT IN (SELECT p.id FROM parts p
+ JOIN jcitems j ON (p.id = j.parts_id))|;
+ }
+
+ if ($form->{itemstatus} eq 'active') {
+ $where .= " AND p.obsolete = '0'";
+ }
+ if ($form->{itemstatus} eq 'obsolete') {
+ $where .= " AND p.obsolete = '1'";
+ }
+ if ($form->{itemstatus} eq 'onhand') {
+ $where .= " AND p.onhand > 0";
+ }
+ if ($form->{itemstatus} eq 'short') {
+ $where .= " AND p.onhand < p.rop";
+ }
+
+ my $makemodelflds = qq|, '', ''|;;
+ my $makemodeljoin;
+
+ if (($form->{make} ne "") || $form->{l_make} || ($form->{model} ne "") || $form->{l_model}) {
+ $makemodelflds = qq|, m.make, m.model|;
+ $makemodeljoin = qq|LEFT JOIN makemodel m ON (m.parts_id = p.id)|;
+
+ if ($form->{make} ne "") {
+ $var = $form->like(lc $form->{make});
+ $where .= " AND lower(m.make) LIKE '$var'";
+ }
+ if ($form->{model} ne "") {
+ $var = $form->like(lc $form->{model});
+ $where .= " AND lower(m.model) LIKE '$var'";
+ }
+ }
+ if ($form->{partsgroup} ne "") {
+ ($null, $var) = split /--/, $form->{partsgroup};
+ $where .= qq| AND p.partsgroup_id = $var|;
+ }
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ my %ordinal = ( 'partnumber' => 2,
+ 'description' => 3,
+ 'bin' => 6,
+ 'priceupdate' => 13,
+ 'drawing' => 15,
+ 'microfiche' => 16,
+ 'partsgroup' => 18,
+ 'make' => 21,
+ 'model' => 22,
+ 'assemblypartnumber' => 23
+ );
+
+ my @a = qw(partnumber description);
+ my $sortorder = $form->sort_order(\@a, \%ordinal);
+
+ my $query = qq|SELECT curr FROM defaults|;
+ my ($curr) = $dbh->selectrow_array($query);
+ $curr =~ s/:.*//;
+
+ my $flds = qq|p.id, p.partnumber, p.description, p.onhand, p.unit,
+ p.bin, p.sellprice, p.listprice, p.lastcost, p.rop,
+ p.avgcost,
+ p.weight, p.priceupdate, p.image, p.drawing, p.microfiche,
+ p.assembly, pg.partsgroup, '$curr' AS curr,
+ c1.accno AS inventory, c2.accno AS income, c3.accno AS expense,
+ p.notes
+ $makemodelflds $assemblyflds
+ |;
+
+ $query = qq|SELECT $flds
+ FROM parts p
+ LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
+ LEFT JOIN chart c1 ON (c1.id = p.inventory_accno_id)
+ LEFT JOIN chart c2 ON (c2.id = p.income_accno_id)
+ LEFT JOIN chart c3 ON (c3.id = p.expense_accno_id)
+ $makemodeljoin
+ WHERE $where
+ ORDER BY $sortorder|;
+
+ # redo query for components report
+ if ($form->{searchitems} eq 'component') {
+
+ $flds =~ s/p.onhand/a.qty AS onhand/;
+
+ $query = qq|SELECT $flds
+ FROM assembly a
+ JOIN parts p ON (a.parts_id = p.id)
+ JOIN parts p1 ON (a.id = p1.id)
+ LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
+ LEFT JOIN chart c1 ON (c1.id = p.inventory_accno_id)
+ LEFT JOIN chart c2 ON (c2.id = p.income_accno_id)
+ LEFT JOIN chart c3 ON (c3.id = p.expense_accno_id)
+ $makemodeljoin
+ WHERE $where
+ ORDER BY $sortorder|;
+ }
+
+
+ # rebuild query for bought and sold items
+ if ($form->{bought} || $form->{sold} || $form->{onorder} || $form->{ordered} || $form->{rfq} || $form->{quoted}) {
+
+ $form->sort_order();
+ @a = qw(partnumber description curr employee name serialnumber id);
+ push @a, "invnumber" if ($form->{bought} || $form->{sold});
+ push @a, "ordnumber" if ($form->{onorder} || $form->{ordered});
+ push @a, "quonumber" if ($form->{rfq} || $form->{quoted});
+
+ %ordinal = ( 'partnumber' => 2,
+ 'description' => 3,
+ 'serialnumber' => 4,
+ 'bin' => 7,
+ 'priceupdate' => 14,
+ 'partsgroup' => 19,
+ 'invnumber' => 20,
+ 'ordnumber' => 21,
+ 'quonumber' => 22,
+ 'name' => 24,
+ 'employee' => 25,
+ 'curr' => 26,
+ 'make' => 29,
+ 'model' => 30
+ );
+
+ $sortorder = $form->sort_order(\@a, \%ordinal);
+
+ my $union = "";
+ $query = "";
+
+ if ($form->{bought} || $form->{sold}) {
+
+ my $invwhere = "$where";
+ my $transdate = ($form->{method} eq 'accrual') ? "transdate" : "datepaid";
+
+ $invwhere .= " AND i.assemblyitem = '0'";
+ $invwhere .= " AND a.$transdate >= '$form->{transdatefrom}'" if $form->{transdatefrom};
+ $invwhere .= " AND a.$transdate <= '$form->{transdateto}'" if $form->{transdateto};
+
+ if ($form->{description} ne "") {
+ $var = $form->like(lc $form->{description});
+ $invwhere .= " AND lower(i.description) LIKE '$var'";
+ }
+
+ if ($form->{open} || $form->{closed}) {
+ if ($form->{open} && $form->{closed}) {
+ if ($form->{method} eq 'cash') {
+ $invwhere .= " AND a.amount = a.paid";
+ }
+ } else {
+ if ($form->{open}) {
+ if ($form->{method} eq 'cash') {
+ $invwhere .= " AND a.id = 0";
+ } else {
+ $invwhere .= " AND NOT a.amount = a.paid";
+ }
+ } else {
+ $invwhere .= " AND a.amount = a.paid";
+ }
+ }
+ } else {
+ $invwhere .= " AND a.id = 0";
+ }
+
+ my $flds = qq|p.id, p.partnumber, i.description, i.serialnumber,
+ i.qty AS onhand, i.unit, p.bin, i.sellprice,
+ p.listprice, p.lastcost, p.rop, p.weight,
+ p.avgcost,
+ p.priceupdate, p.image, p.drawing, p.microfiche,
+ p.assembly,
+ pg.partsgroup, a.invnumber, a.ordnumber, a.quonumber,
+ i.trans_id, ct.name, e.name AS employee, a.curr, a.till,
+ p.notes
+ $makemodelfld|;
+
+
+ if ($form->{bought}) {
+ my $rflds = $flds;
+ $rflds =~ s/i.qty AS onhand/i.qty * -1 AS onhand/;
+
+ $query = qq|
+ SELECT $rflds, 'ir' AS module, '' AS type,
+ (SELECT sell FROM exchangerate ex
+ WHERE ex.curr = a.curr
+ AND ex.transdate = a.$transdate) AS exchangerate,
+ i.discount
+ FROM invoice i
+ JOIN parts p ON (p.id = i.parts_id)
+ JOIN ap a ON (a.id = i.trans_id)
+ JOIN vendor ct ON (a.vendor_id = ct.id)
+ LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
+ LEFT JOIN employee e ON (a.employee_id = e.id)
+ $makemodeljoin
+ WHERE $invwhere|;
+ $union = "
+ UNION ALL";
+ }
+
+ if ($form->{sold}) {
+ $query .= qq|$union
+ SELECT $flds, 'is' AS module, '' AS type,
+ (SELECT buy FROM exchangerate ex
+ WHERE ex.curr = a.curr
+ AND ex.transdate = a.$transdate) AS exchangerate,
+ i.discount
+ FROM invoice i
+ JOIN parts p ON (p.id = i.parts_id)
+ JOIN ar a ON (a.id = i.trans_id)
+ JOIN customer ct ON (a.customer_id = ct.id)
+ LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
+ LEFT JOIN employee e ON (a.employee_id = e.id)
+ $makemodeljoin
+ WHERE $invwhere|;
+ $union = "
+ UNION ALL";
+ }
+ }
+
+ if ($form->{onorder} || $form->{ordered}) {
+ my $ordwhere = "$where
+ AND a.quotation = '0'";
+ $ordwhere .= " AND a.transdate >= '$form->{transdatefrom}'" if $form->{transdatefrom};
+ $ordwhere .= " AND a.transdate <= '$form->{transdateto}'" if $form->{transdateto};
+
+ if ($form->{description} ne "") {
+ $var = $form->like(lc $form->{description});
+ $ordwhere .= " AND lower(i.description) LIKE '$var'";
+ }
+
+ if ($form->{open} || $form->{closed}) {
+ unless ($form->{open} && $form->{closed}) {
+ $ordwhere .= " AND a.closed = '0'" if $form->{open};
+ $ordwhere .= " AND a.closed = '1'" if $form->{closed};
+ }
+ } else {
+ $ordwhere .= " AND a.id = 0";
+ }
+
+ $flds = qq|p.id, p.partnumber, i.description, i.serialnumber,
+ i.qty AS onhand, i.unit, p.bin, i.sellprice,
+ p.listprice, p.lastcost, p.rop, p.weight,
+ p.avgcost,
+ p.priceupdate, p.image, p.drawing, p.microfiche,
+ p.assembly,
+ pg.partsgroup, '' AS invnumber, a.ordnumber, a.quonumber,
+ i.trans_id, ct.name, e.name AS employee, a.curr, '0' AS till,
+ p.notes
+ $makemodelfld|;
+
+ if ($form->{ordered}) {
+ $query .= qq|$union
+ SELECT $flds, 'oe' AS module, 'sales_order' AS type,
+ (SELECT buy FROM exchangerate ex
+ WHERE ex.curr = a.curr
+ AND ex.transdate = a.transdate) AS exchangerate,
+ i.discount
+ FROM orderitems i
+ JOIN parts p ON (i.parts_id = p.id)
+ JOIN oe a ON (i.trans_id = a.id)
+ JOIN customer ct ON (a.customer_id = ct.id)
+ LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
+ LEFT JOIN employee e ON (a.employee_id = e.id)
+ $makemodeljoin
+ WHERE $ordwhere
+ AND a.customer_id > 0|;
+ $union = "
+ UNION ALL";
+ }
+
+ if ($form->{onorder}) {
+ $flds = qq|p.id, p.partnumber, i.description, i.serialnumber,
+ i.qty AS onhand, i.unit, p.bin, i.sellprice,
+ p.listprice, p.lastcost, p.rop, p.weight,
+ p.avgcost,
+ p.priceupdate, p.image, p.drawing, p.microfiche,
+ p.assembly,
+ pg.partsgroup, '' AS invnumber, a.ordnumber, a.quonumber,
+ i.trans_id, ct.name,e.name AS employee, a.curr, '0' AS till,
+ p.notes
+ $makemodelfld|;
+
+ $query .= qq|$union
+ SELECT $flds, 'oe' AS module, 'purchase_order' AS type,
+ (SELECT sell FROM exchangerate ex
+ WHERE ex.curr = a.curr
+ AND ex.transdate = a.transdate) AS exchangerate,
+ i.discount
+ FROM orderitems i
+ JOIN parts p ON (i.parts_id = p.id)
+ JOIN oe a ON (i.trans_id = a.id)
+ JOIN vendor ct ON (a.vendor_id = ct.id)
+ LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
+ LEFT JOIN employee e ON (a.employee_id = e.id)
+ $makemodeljoin
+ WHERE $ordwhere
+ AND a.vendor_id > 0|;
+ }
+
+ }
+
+ if ($form->{rfq} || $form->{quoted}) {
+ my $quowhere = "$where
+ AND a.quotation = '1'";
+ $quowhere .= " AND a.transdate >= '$form->{transdatefrom}'" if $form->{transdatefrom};
+ $quowhere .= " AND a.transdate <= '$form->{transdateto}'" if $form->{transdateto};
+
+ if ($form->{description} ne "") {
+ $var = $form->like(lc $form->{description});
+ $quowhere .= " AND lower(i.description) LIKE '$var'";
+ }
+
+ if ($form->{open} || $form->{closed}) {
+ unless ($form->{open} && $form->{closed}) {
+ $ordwhere .= " AND a.closed = '0'" if $form->{open};
+ $ordwhere .= " AND a.closed = '1'" if $form->{closed};
+ }
+ } else {
+ $ordwhere .= " AND a.id = 0";
+ }
+
+
+ $flds = qq|p.id, p.partnumber, i.description, i.serialnumber,
+ i.qty AS onhand, i.unit, p.bin, i.sellprice,
+ p.listprice, p.lastcost, p.rop, p.weight,
+ p.avgcost,
+ p.priceupdate, p.image, p.drawing, p.microfiche,
+ p.assembly,
+ pg.partsgroup, '' AS invnumber, a.ordnumber, a.quonumber,
+ i.trans_id, ct.name, e.name AS employee, a.curr, '0' AS till,
+ p.notes
+ $makemodelfld|;
+
+ if ($form->{quoted}) {
+ $query .= qq|$union
+ SELECT $flds, 'oe' AS module, 'sales_quotation' AS type,
+ (SELECT buy FROM exchangerate ex
+ WHERE ex.curr = a.curr
+ AND ex.transdate = a.transdate) AS exchangerate,
+ i.discount
+ FROM orderitems i
+ JOIN parts p ON (i.parts_id = p.id)
+ JOIN oe a ON (i.trans_id = a.id)
+ JOIN customer ct ON (a.customer_id = ct.id)
+ LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
+ LEFT JOIN employee e ON (a.employee_id = e.id)
+ $makemodeljoin
+ WHERE $quowhere
+ AND a.customer_id > 0|;
+ $union = "
+ UNION ALL";
+ }
+
+ if ($form->{rfq}) {
+ $flds = qq|p.id, p.partnumber, i.description, i.serialnumber,
+ i.qty AS onhand, i.unit, p.bin, i.sellprice,
+ p.listprice, p.lastcost, p.rop, p.weight,
+ p.avgcost,
+ p.priceupdate, p.image, p.drawing, p.microfiche,
+ p.assembly,
+ pg.partsgroup, '' AS invnumber, a.ordnumber, a.quonumber,
+ i.trans_id, ct.name, e.name AS employee, a.curr, '0' AS till,
+ p.notes
+ $makemodelfld|;
+
+ $query .= qq|$union
+ SELECT $flds, 'oe' AS module, 'request_quotation' AS type,
+ (SELECT sell FROM exchangerate ex
+ WHERE ex.curr = a.curr
+ AND ex.transdate = a.transdate) AS exchangerate,
+ i.discount
+ FROM orderitems i
+ JOIN parts p ON (i.parts_id = p.id)
+ JOIN oe a ON (i.trans_id = a.id)
+ JOIN vendor ct ON (a.vendor_id = ct.id)
+ LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
+ LEFT JOIN employee e ON (a.employee_id = e.id)
+ $makemodeljoin
+ WHERE $quowhere
+ AND a.vendor_id > 0|;
+ }
+
+ }
+
+ $query .= qq|
+ ORDER BY $sortorder|;
+
+ }
+
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ $query = qq|SELECT c.accno
+ FROM chart c
+ JOIN partstax pt ON (pt.chart_id = c.id)
+ WHERE pt.parts_id = ?
+ ORDER BY accno|;
+ my $pth = $dbh->prepare($query) || $form->dberror($query);
+
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+ $pth->execute($ref->{id});
+ while (($accno) = $pth->fetchrow_array) {
+ $ref->{tax} .= "$accno ";
+ }
+ $pth->finish;
+
+ push @{ $form->{parts} }, $ref;
+ }
+ $sth->finish;
+
+ @a = ();
+
+ # include individual items for assembly
+ if (($form->{searchitems} eq 'assembly') && $form->{individual}) {
+
+ if ($form->{sold} || $form->{ordered} || $form->{quoted}) {
+ $flds = qq|p.id, p.partnumber, p.description, p.onhand AS perassembly, p.unit,
+ p.bin, p.sellprice, p.listprice, p.lastcost, p.rop,
+ p.avgcost,
+ p.weight, p.priceupdate, p.image, p.drawing, p.microfiche,
+ p.assembly, pg.partsgroup, p.notes
+ $makemodelflds $assemblyflds
+ |;
+ } else {
+ # replace p.onhand with a.qty AS onhand
+ $flds =~ s/p.onhand/a.qty AS perassembly/;
+ }
+
+ for (@{ $form->{parts} }) {
+ push @a, $_;
+ $_->{perassembly} = 1;
+ $flds =~ s/p\.onhand*AS perassembly/p\.onhand, a\.qty AS perassembly/;
+ push @a, &include_assembly($dbh, $myconfig, $form, $_->{id}, $flds, $makemodeljoin);
+ push @a, {id => $_->{id}, assemblyitem => 1}; # this is just for
+ # adding a blank line
+ }
+
+ # copy assemblies to $form->{parts}
+ @{ $form->{parts} } = @a;
+
+ }
+
+
+ @a = ();
+ if (($form->{warehouse} ne "") || $form->{l_warehouse}) {
+
+ if ($form->{warehouse} ne "") {
+ ($null, $var) = split /--/, $form->{warehouse};
+ $var *= 1;
+ $query = qq|SELECT SUM(qty) AS onhand, '$null' AS description
+ FROM inventory
+ WHERE warehouse_id = $var
+ AND parts_id = ?|;
+ } else {
+ $query = qq|SELECT SUM(i.qty) AS onhand, w.description AS warehouse
+ FROM inventory i
+ JOIN warehouse w ON (w.id = i.warehouse_id)
+ WHERE i.parts_id = ?
+ GROUP BY w.description|;
+ }
+
+ $sth = $dbh->prepare($query) || $form->dberror($query);
+
+ for (@{ $form->{parts} }) {
+
+ $sth->execute($_->{id}) || $form->dberror($query);
+
+ if ($form->{warehouse} ne "") {
+
+ $ref = $sth->fetchrow_hashref(NAME_lc);
+ if ($ref->{onhand} != 0) {
+ $_->{onhand} = $ref->{onhand};
+ push @a, $_;
+ }
+
+ } else {
+
+ push @a, $_;
+
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+ if ($ref->{onhand} > 0) {
+ push @a, $ref;
+ }
+ }
+ }
+
+ $sth->finish;
+ }
+
+ @{ $form->{parts} } = @a;
+
+ }
+
+ $dbh->disconnect;
+
+}
+
+
+sub include_assembly {
+ my ($dbh, $myconfig, $form, $id, $flds, $makemodeljoin) = @_;
+
+ $form->{stagger}++;
+ if ($form->{stagger} > $form->{pncol}) {
+ $form->{pncol} = $form->{stagger};
+ }
+
+ $form->{$id} = 1;
+
+ my %oid = ('Pg' => 'a.oid',
+ 'PgPP' => 'a.oid',
+ 'Oracle' => 'a.rowid',
+ 'DB2' => '1=1'
+ );
+
+ my @a = qw(partnumber description bin);
+ if ($form->{sort} eq 'partnumber') {
+ $sortorder = "$oid{$myconfig->{dbdriver}}";
+ } else {
+ @a = grep !/$form->{sort}/, @a;
+ $sortorder = "$form->{sort} $form->{direction}, ". join ',', @a;
+ }
+
+ @a = ();
+ my $query = qq|SELECT $flds
+ FROM parts p
+ JOIN assembly a ON (a.parts_id = p.id)
+ LEFT JOIN partsgroup pg ON (pg.id = p.id)
+ LEFT JOIN chart c1 ON (c1.id = p.inventory_accno_id)
+ LEFT JOIN chart c2 ON (c2.id = p.income_accno_id)
+ LEFT JOIN chart c3 ON (c3.id = p.expense_accno_id)
+ $makemodeljoin
+ WHERE a.id = $id
+ ORDER BY $sortorder|;
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+ $ref->{assemblyitem} = 1;
+ $ref->{stagger} = $form->{stagger};
+
+ push @a, $ref;
+ if ($ref->{assembly} && !$form->{$ref->{id}}) {
+ push @a, &include_assembly($dbh, $myconfig, $form, $ref->{id}, $flds, $makemodeljoin);
+ if ($form->{stagger} > $form->{pncol}) {
+ $form->{pncol} = $form->{stagger};
+ }
+ }
+ }
+ $sth->finish;
+
+ $form->{$id} = 0;
+ $form->{stagger}--;
+
+ @a;
+
+}
+
+
+sub requirements {
+ my ($self, $myconfig, $form) = @_;
+
+ my $null;
+ my $var;
+ my $ref;
+
+ my $where = qq|p.obsolete = '0'|;
+ my $dwhere;
+
+ for (qw(partnumber description)) {
+ if ($form->{$_} ne "") {
+ $var = $form->like(lc $form->{$_});
+ $where .= qq| AND lower(p.$_) LIKE '$var'|;
+ }
+ }
+
+ if ($form->{partsgroup} ne "") {
+ ($null, $var) = split /--/, $form->{partsgroup};
+ $where .= qq| AND p.partsgroup_id = $var|;
+ }
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ my ($transdatefrom, $transdateto);
+ if ($form->{year}) {
+ ($transdatefrom, $transdateto) = $form->from_to($form->{year}, '01', 12);
+
+ $dwhere = qq| AND a.transdate >= '$transdatefrom'
+ AND a.transdate <= '$transdateto'|;
+ }
+
+ $query = qq|SELECT p.id, p.partnumber, p.description,
+ sum(i.qty) AS qty, p.onhand,
+ extract(MONTH FROM a.transdate) AS month,
+ '0' AS so, '0' AS po
+ FROM invoice i
+ JOIN parts p ON (p.id = i.parts_id)
+ JOIN ar a ON (a.id = i.trans_id)
+ WHERE $where
+ $dwhere
+ AND p.inventory_accno_id > 0
+ GROUP BY p.id, p.partnumber, p.description, p.onhand,
+ extract(MONTH FROM a.transdate)|;
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ my %parts;
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+ $parts{$ref->{id}} = $ref;
+ }
+ $sth->finish;
+
+ my %ofld = ( customer => so,
+ vendor => po );
+
+ for (qw(customer vendor)) {
+ $query = qq|SELECT p.id, p.partnumber, p.description,
+ sum(qty) - sum(ship) AS $ofld{$_}, p.onhand,
+ 0 AS month
+ FROM orderitems i
+ JOIN parts p ON (p.id = i.parts_id)
+ JOIN oe a ON (a.id = i.trans_id)
+ WHERE $where
+ AND p.inventory_accno_id > 0
+ AND p.assembly = '0'
+ AND a.closed = '0'
+ AND a.${_}_id > 0
+ GROUP BY p.id, p.partnumber, p.description, p.onhand,
+ month|;
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+ if (exists $parts{$ref->{id}}->{$ofld{$_}}) {
+ $parts{$ref->{id}}->{$ofld{$_}} += $ref->{$ofld{$_}};
+ } else {
+ $parts{$ref->{id}} = $ref;
+ }
+ }
+ $sth->finish;
+ }
+
+ # add assemblies from open sales orders
+ $query = qq|SELECT DISTINCT a.id AS orderid, b.id, i.qty - i.ship AS qty
+ FROM parts p
+ JOIN assembly b ON (b.parts_id = p.id)
+ JOIN orderitems i ON (i.parts_id = b.id)
+ JOIN oe a ON (a.id = i.trans_id)
+ WHERE $where
+ AND (p.inventory_accno_id > 0 OR p.assembly = '1')
+ AND a.closed = '0'|;
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+ &requirements_assembly($dbh, $form, \%parts, $ref->{id}, $ref->{qty}, $where) if $ref->{qty};
+ }
+ $sth->finish;
+
+ $dbh->disconnect;
+
+ for (sort { $parts{$a}->{$form->{sort}} cmp $parts{$b}->{$form->{sort}} } keys %parts) {
+ push @{ $form->{parts} }, $parts{$_};
+ }
+
+}
+
+
+sub requirements_assembly {
+ my ($dbh, $form, $parts, $id, $qty, $where) = @_;
+
+ # assemblies
+ my $query = qq|SELECT p.id, p.partnumber, p.description,
+ a.qty * $qty AS so, p.onhand, p.assembly,
+ p.partsgroup_id
+ FROM assembly a
+ JOIN parts p ON (p.id = a.parts_id)
+ WHERE $where
+ AND a.id = $id
+ AND p.inventory_accno_id > 0
+
+ UNION
+
+ SELECT p.id, p.partnumber, p.description,
+ a.qty * $qty AS so, p.onhand, p.assembly,
+ p.partsgroup_id
+ FROM assembly a
+ JOIN parts p ON (p.id = a.parts_id)
+ WHERE a.id = $id
+ AND p.assembly = '1'|;
+
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
+ if ($ref->{assembly}) {
+ &requirements_assembly($dbh, $form, $parts, $ref->{id}, $ref->{so}, $where);
+ next;
+ }
+
+ if (exists $parts->{$ref->{id}}{so}) {
+ $parts->{$ref->{id}}{so} += $ref->{so};
+ } else {
+ $parts->{$ref->{id}} = $ref;
+ }
+ }
+ $sth->finish;
+
+}
+
+
+sub create_links {
+ my ($self, $module, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ my $ref;
+
+ my $query = qq|SELECT accno, description, link
+ FROM chart
+ WHERE link LIKE '%$module%'
+ ORDER BY accno|;
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+ foreach my $key (split /:/, $ref->{link}) {
+ if ($key =~ /$module/) {
+ push @{ $form->{"${module}_links"}{$key} }, { accno => $ref->{accno},
+ description => $ref->{description} };
+ }
+ }
+ }
+ $sth->finish;
+
+ if ($form->{item} ne 'assembly') {
+ $query = qq|SELECT count(*) FROM vendor|;
+ my ($count) = $dbh->selectrow_array($query);
+
+ if ($count < $myconfig->{vclimit}) {
+ $query = qq|SELECT id, name
+ FROM vendor
+ ORDER BY name|;
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+ push @{ $form->{all_vendor} }, $ref;
+ }
+ $sth->finish;
+ }
+ }
+
+ # pricegroups, customers
+ $query = qq|SELECT count(*) FROM customer|;
+ ($count) = $dbh->selectrow_array($query);
+
+ if ($count < $myconfig->{vclimit}) {
+ $query = qq|SELECT id, name
+ FROM customer
+ ORDER BY name|;
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+ push @{ $form->{all_customer} }, $ref;
+ }
+ $sth->finish;
+ }
+
+ $query = qq|SELECT id, pricegroup
+ FROM pricegroup
+ ORDER BY pricegroup|;
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+ push @{ $form->{all_pricegroup} }, $ref;
+ }
+ $sth->finish;
+
+
+ if ($form->{id}) {
+ $query = qq|SELECT weightunit, curr AS currencies
+ FROM defaults|;
+ ($form->{weightunit}, $form->{currencies}) = $dbh->selectrow_array($query);
+
+ } else {
+ $query = qq|SELECT d.weightunit, current_date AS priceupdate,
+ d.curr AS currencies,
+ c1.accno AS inventory_accno, c1.description AS inventory_description,
+ c2.accno AS income_accno, c2.description AS income_description,
+ c3.accno AS expense_accno, c3.description AS expense_description
+ FROM defaults d
+ LEFT JOIN chart c1 ON (d.inventory_accno_id = c1.id)
+ LEFT JOIN chart c2 ON (d.income_accno_id = c2.id)
+ LEFT JOIN chart c3 ON (d.expense_accno_id = c3.id)|;
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ $ref = $sth->fetchrow_hashref(NAME_lc);
+ for (qw(weightunit priceupdate currencies)) { $form->{$_} = $ref->{$_} }
+ # setup accno hash, {amount} is used in create_links
+ for (qw(inventory income expense)) { $form->{amount}{"IC_$_"} = { accno => $ref->{"${_}_accno"}, description => $ref->{"${_}_description"} } }
+
+ $sth->finish;
+ }
+
+ $dbh->disconnect;
+
+}
+
+
+sub get_warehouses {
+ my ($self, $myconfig, $form) = @_;
+
+ my $dbh = $form->dbconnect($myconfig);
+
+ my $query = qq|SELECT id, description
+ FROM warehouse|;
+
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
+ push @{ $form->{all_warehouse} }, $ref;
+ }
+ $sth->finish;
+
+ $dbh->disconnect;
+
+}
+
+1;
+
diff --git a/LedgerSMB/IR.pm b/LedgerSMB/IR.pm
new file mode 100755
index 00000000..d06a233c
--- /dev/null
+++ b/LedgerSMB/IR.pm
@@ -0,0 +1,1123 @@
+#=====================================================================
+# LedgerSMB
+# Small Medium Business Accounting software
+#
+# See COPYRIGHT file for copyright information
+#======================================================================
+#
+# This file has NOT undergone whitespace cleanup.
+#
+#======================================================================
+#
+# Inventory received module
+#
+#======================================================================
+
+package IR;
+
+
+sub post_invoice {
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database, turn off autocommit
+ my $dbh = $form->dbconnect_noauto($myconfig);
+
+ my $query;
+ my $sth;
+ my $ref;
+ my $null;
+ my $project_id;
+ my $exchangerate = 0;
+ my $allocated;
+ my $taxrate;
+ my $taxamount;
+ my $diff = 0;
+ my $item;
+ my $invoice_id;
+ my $keepcleared;
+
+ ($null, $form->{employee_id}) = split /--/, $form->{employee};
+ unless ($form->{employee_id}) {
+ ($form->{employee}, $form->{employee_id}) = $form->get_employee($dbh);
+ }
+
+ ($null, $form->{department_id}) = split(/--/, $form->{department});
+ $form->{department_id} *= 1;
+
+ $query = qq|SELECT fxgain_accno_id, fxloss_accno_id
+ FROM defaults d|;
+ my ($fxgain_accno_id, $fxloss_accno_id) = $dbh->selectrow_array($query);
+
+ $query = qq|SELECT inventory_accno_id, income_accno_id, expense_accno_id
+ FROM parts
+ WHERE id = ?|;
+ my $pth = $dbh->prepare($query) || $form->dberror($query);
+
+ my %updparts = ();
+
+ if ($form->{id}) {
+ $keepcleared = 1;
+ $query = qq|SELECT id FROM ap
+ WHERE id = $form->{id}|;
+
+ if ($dbh->selectrow_array($query)) {
+ $query = qq|SELECT p.id, p.inventory_accno_id, p.income_accno_id
+ FROM invoice i
+ JOIN parts p ON (p.id = i.parts_id)
+ WHERE i.trans_id = $form->{id}|;
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+ while ($ref = $sth->fetchrow_hashref) {
+ if ($ref->{inventory_accno_id} && $ref->{income_accno_id}) {
+ $updparts{$ref->{id}} = 1;
+ }
+ }
+ $sth->finish;
+
+ &reverse_invoice($dbh, $form);
+ } else {
+ $query = qq|INSERT INTO ap (id)
+ VALUES ($form->{id})|;
+ $dbh->do($query) || $form->dberror($query);
+ }
+ }
+
+ my $uid = localtime;
+ $uid .= "$$";
+
+ if (! $form->{id}) {
+
+ $query = qq|INSERT INTO ap (invnumber, employee_id)
+ VALUES ('$uid', (SELECT id FROM employee
+ WHERE login = '$form->{login}'))|;
+ $dbh->do($query) || $form->dberror($query);
+
+ $query = qq|SELECT id FROM ap
+ WHERE invnumber = '$uid'|;
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ ($form->{id}) = $sth->fetchrow_array;
+ $sth->finish;
+
+ }
+
+ my $amount;
+ my $grossamount;
+ my $allocated;
+ my $invamount = 0;
+ my $invnetamount = 0;
+
+ if ($form->{currency} eq $form->{defaultcurrency}) {
+ $form->{exchangerate} = 1;
+ } else {
+ $exchangerate = $form->check_exchangerate($myconfig, $form->{currency}, $form->{transdate}, 'sell');
+ }
+
+ $form->{exchangerate} = ($exchangerate) ? $exchangerate : $form->parse_amount($myconfig, $form->{exchangerate});
+
+ for my $i (1 .. $form->{rowcount}) {
+ $form->{"qty_$i"} = $form->parse_amount($myconfig, $form->{"qty_$i"});
+
+ if ($form->{"qty_$i"}) {
+
+ $pth->execute($form->{"id_$i"});
+ $ref = $pth->fetchrow_hashref(NAME_lc);
+ for (keys %$ref) {
+ $form->{"${_}_$i"} = $ref->{$_};
+ }
+ $pth->finish;
+
+ # project
+ $project_id = 'NULL';
+ if ($form->{"projectnumber_$i"} ne "") {
+ ($null, $project_id) = split /--/, $form->{"projectnumber_$i"};
+ }
+
+ # undo discount formatting
+ $form->{"discount_$i"} = $form->parse_amount($myconfig, $form->{"discount_$i"}) / 100;
+
+ # keep entered selling price
+ my $fxsellprice = $form->parse_amount($myconfig, $form->{"sellprice_$i"});
+
+ my ($dec) = ($fxsellprice =~ /\.(\d+)/);
+ $dec = length $dec;
+ my $decimalplaces = ($dec > 2) ? $dec : 2;
+
+ # deduct discount
+ $form->{"sellprice_$i"} = $fxsellprice - $form->round_amount($fxsellprice * $form->{"discount_$i"}, $decimalplaces);
+
+ # linetotal
+ my $fxlinetotal = $form->round_amount($form->{"sellprice_$i"} * $form->{"qty_$i"}, 2);
+
+ $amount = $fxlinetotal * $form->{exchangerate};
+ my $linetotal = $form->round_amount($amount, 2);
+ $fxdiff += $amount - $linetotal;
+
+ @taxaccounts = split / /, $form->{"taxaccounts_$i"};
+
+ $ml = 1;
+ $tax = 0;
+ $fxtax = 0;
+
+ for (0 .. 1) {
+ $taxrate = 0;
+
+ # add tax rates
+ for (@taxaccounts) {
+ $taxrate += $form->{"${_}_rate"} if ($form->{"${_}_rate"} * $ml) > 0;
+ }
+
+ if ($form->{taxincluded}) {
+ $tax += $amount = $linetotal * ($taxrate / (1 + ($taxrate * $ml)));
+ $form->{"sellprice_$i"} -= $amount / $form->{"qty_$i"};
+ } else {
+ $tax += $amount = $linetotal * $taxrate;
+ $fxtax += $fxlinetotal * $taxrate;
+ }
+
+ for (@taxaccounts) {
+ $form->{acc_trans}{$form->{id}}{$_}{amount} += $amount * $form->{"${_}_rate"} / $taxrate if ($form->{"${_}_rate"} * $ml) > 0;
+ }
+
+ $ml = -1;
+ }
+
+ $grossamount = $form->round_amount($linetotal, 2);
+
+ if ($form->{taxincluded}) {
+ $amount = $form->round_amount($tax, 2);
+ $linetotal -= $form->round_amount($tax - $diff, 2);
+ $diff = ($amount - $tax);
+ }
+
+ $amount = $form->round_amount($linetotal, 2);
+ $allocated = 0;
+
+ # adjust and round sellprice
+ $form->{"sellprice_$i"} = $form->round_amount($form->{"sellprice_$i"} * $form->{exchangerate}, $decimalplaces);
+
+ # save detail record in invoice table
+ $query = qq|INSERT INTO invoice (description)
+ VALUES ('$uid')|;
+ $dbh->do($query) || $form->dberror($query);
+
+ $query = qq|SELECT id FROM invoice
+ WHERE description = '$uid'|;
+ ($invoice_id) = $dbh->selectrow_array($query);
+
+ $query = qq|UPDATE invoice SET
+ trans_id = $form->{id},
+ parts_id = $form->{"id_$i"},
+ description = |.$dbh->quote($form->{"description_$i"}).qq|,
+ qty = $form->{"qty_$i"} * -1,
+ sellprice = $form->{"sellprice_$i"},
+ fxsellprice = $fxsellprice,
+ discount = $form->{"discount_$i"},
+ allocated = $allocated,
+ unit = |.$dbh->quote($form->{"unit_$i"}).qq|,
+ deliverydate = |.$form->dbquote($form->{"deliverydate_$i"}, SQL_DATE).qq|,
+ project_id = $project_id,
+ serialnumber = |.$dbh->quote($form->{"serialnumber_$i"}).qq|,
+ notes = |.$dbh->quote($form->{"notes_$i"}).qq|
+ WHERE id = $invoice_id|;
+ $dbh->do($query) || $form->dberror($query);
+
+
+ if ($form->{"inventory_accno_id_$i"}) {
+
+ # add purchase to inventory
+ push @{ $form->{acc_trans}{lineitems} }, {
+ chart_id => $form->{"inventory_accno_id_$i"},
+ amount => $amount,
+ fxgrossamount => $fxlinetotal + $form->round_amount($fxtax, 2),
+ grossamount => $grossamount,
+ project_id => $project_id,
+ invoice_id => $invoice_id };
+
+
+ $updparts{$form->{"id_$i"}} = 1;
+
+ # update parts table
+ $form->update_balance($dbh,
+ "parts",
+ "onhand",
+ qq|id = $form->{"id_$i"}|,
+ $form->{"qty_$i"}) unless $form->{shipped};
+
+
+ # check if we sold the item
+ $query = qq|SELECT i.id, i.qty, i.allocated, i.trans_id, i.project_id,
+ p.inventory_accno_id, p.expense_accno_id, a.transdate
+ FROM invoice i
+ JOIN parts p ON (p.id = i.parts_id)
+ JOIN ar a ON (a.id = i.trans_id)
+ WHERE i.parts_id = $form->{"id_$i"}
+ AND (i.qty + i.allocated) > 0
+ ORDER BY transdate|;
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ my $totalqty = $form->{"qty_$i"};
+
+ while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
+
+ my $qty = $ref->{qty} + $ref->{allocated};
+
+ if (($qty - $totalqty) > 0) {
+ $qty = $totalqty;
+ }
+
+ $linetotal = $form->round_amount($form->{"sellprice_$i"} * $qty, 2);
+ $ref->{project_id} ||= 'NULL';
+
+ # add entry for inventory, this one is for the sold item
+ if ($linetotal) {
+ $query = qq|INSERT INTO acc_trans (trans_id, chart_id, amount,
+ transdate, project_id, invoice_id)
+ VALUES ($ref->{trans_id}, $ref->{inventory_accno_id},
+ $linetotal, '$ref->{transdate}', $ref->{project_id},
+ $invoice_id)|;
+ $dbh->do($query) || $form->dberror($query);
+
+ # add expense
+ $query = qq|INSERT INTO acc_trans (trans_id, chart_id, amount,
+ transdate, project_id, invoice_id)
+ VALUES ($ref->{trans_id}, $ref->{expense_accno_id},
+ |. ($linetotal * -1) .qq|, '$ref->{transdate}',
+ $ref->{project_id}, $invoice_id)|;
+ $dbh->do($query) || $form->dberror($query);
+ }
+
+ # update allocated for sold item
+ $form->update_balance($dbh,
+ "invoice",
+ "allocated",
+ qq|id = $ref->{id}|,
+ $qty * -1);
+
+ $allocated += $qty;
+
+ last if (($totalqty -= $qty) <= 0);
+ }
+
+ $sth->finish;
+
+ } else {
+
+ # add purchase to expense
+ push @{ $form->{acc_trans}{lineitems} }, {
+ chart_id => $form->{"expense_accno_id_$i"},
+ amount => $amount,
+ fxgrossamount => $fxlinetotal + $form->round_amount($fxtax, 2),
+ grossamount => $grossamount,
+ project_id => $project_id,
+ invoice_id => $invoice_id };
+
+ }
+ }
+ }
+
+ $form->{paid} = 0;
+ for $i (1 .. $form->{paidaccounts}) {
+ $form->{"paid_$i"} = $form->parse_amount($myconfig, $form->{"paid_$i"});
+ $form->{paid} += $form->{"paid_$i"};
+ $form->{datepaid} = $form->{"datepaid_$i"} if ($form->{"datepaid_$i"});
+ }
+
+ # add lineitems + tax
+ $amount = 0;
+ $grossamount = 0;
+ $fxgrossamount = 0;
+ for (@{ $form->{acc_trans}{lineitems} }) {
+ $amount += $_->{amount};
+ $grossamount += $_->{grossamount};
+ $fxgrossamount += $_->{fxgrossamount};
+ }
+ $invnetamount = $amount;
+
+ $amount = 0;
+ for (split / /, $form->{taxaccounts}) {
+ $amount += $form->{acc_trans}{$form->{id}}{$_}{amount} = $form->round_amount($form->{acc_trans}{$form->{id}}{$_}{amount}, 2);
+ $form->{acc_trans}{$form->{id}}{$_}{amount} *= -1;
+ }
+ $invamount = $invnetamount + $amount;
+
+ $diff = 0;
+ if ($form->{taxincluded}) {
+ $diff = $form->round_amount($grossamount - $invamount, 2);
+ $invamount += $diff;
+ }
+ $fxdiff = $form->round_amount($fxdiff,2);
+ $invnetamount += $fxdiff;
+ $invamount += $fxdiff;
+
+ if ($form->round_amount($form->{paid} - $fxgrossamount,2) == 0) {
+ $form->{paid} = $invamount;
+ } else {
+ $form->{paid} = $form->round_amount($form->{paid} * $form->{exchangerate}, 2);
+ }
+
+ foreach $ref (sort { $b->{amount} <=> $a->{amount} } @ { $form->{acc_trans}{lineitems} }) {
+ $amount = $ref->{amount} + $diff + $fxdiff;
+ $query = qq|INSERT INTO acc_trans (trans_id, chart_id, amount,
+ transdate, project_id, invoice_id)
+ VALUES ($form->{id}, $ref->{chart_id}, $amount * -1,
+ '$form->{transdate}', $ref->{project_id}, $ref->{invoice_id})|;
+ $dbh->do($query) || $form->dberror($query);
+ $diff = 0;
+ $fxdiff = 0;
+ }
+
+ $form->{payables} = $invamount;
+
+ delete $form->{acc_trans}{lineitems};
+
+ # update exchangerate
+ if (($form->{currency} ne $form->{defaultcurrency}) && !$exchangerate) {
+ $form->update_exchangerate($dbh, $form->{currency}, $form->{transdate}, 0, $form->{exchangerate});
+ }
+
+ # record payable
+ if ($form->{payables}) {
+ ($accno) = split /--/, $form->{AP};
+
+ $query = qq|INSERT INTO acc_trans (trans_id, chart_id, amount,
+ transdate)
+ VALUES ($form->{id},
+ (SELECT id FROM chart
+ WHERE accno = '$accno'),
+ $form->{payables}, '$form->{transdate}')|;
+ $dbh->do($query) || $form->dberror($query);
+ }
+
+ foreach my $trans_id (keys %{$form->{acc_trans}}) {
+ foreach my $accno (keys %{ $form->{acc_trans}{$trans_id} }) {
+ $amount = $form->round_amount($form->{acc_trans}{$trans_id}{$accno}{amount}, 2);
+ if ($amount) {
+ $query = qq|INSERT INTO acc_trans (trans_id, chart_id, amount,
+ transdate)
+ VALUES ($trans_id, (SELECT id FROM chart
+ WHERE accno = '$accno'),
+ $amount, '$form->{transdate}')|;
+ $dbh->do($query) || $form->dberror($query);
+ }
+ }
+ }
+
+ # if there is no amount but a payment record payable
+ if ($invamount == 0) {
+ $form->{payables} = 1;
+ }
+
+ my $cleared = 0;
+
+ # record payments and offsetting AP
+ for my $i (1 .. $form->{paidaccounts}) {
+
+ if ($form->{"paid_$i"}) {
+ my ($accno) = split /--/, $form->{"AP_paid_$i"};
+ $form->{"datepaid_$i"} = $form->{transdate} unless ($form->{"datepaid_$i"});
+ $form->{datepaid} = $form->{"datepaid_$i"};
+
+ $exchangerate = 0;
+
+ if ($form->{currency} eq $form->{defaultcurrency}) {
+ $form->{"exchangerate_$i"} = 1;
+ } else {
+ $exchangerate = $form->check_exchangerate($myconfig, $form->{currency}, $form->{"datepaid_$i"}, 'sell');
+
+ $form->{"exchangerate_$i"} = ($exchangerate) ? $exchangerate : $form->parse_amount($myconfig, $form->{"exchangerate_$i"});
+ }
+
+
+ # record AP
+ $amount = ($form->round_amount($form->{"paid_$i"} * $form->{exchangerate}, 2)) * -1;
+
+ if ($form->{payables}) {
+ $query = qq|INSERT INTO acc_trans (trans_id, chart_id, amount,
+ transdate)
+ VALUES ($form->{id}, (SELECT id FROM chart
+ WHERE accno = '$form->{AP}'),
+ $amount, '$form->{"datepaid_$i"}')|;
+ $dbh->do($query) || $form->dberror($query);
+ }
+
+ if ($keepcleared) {
+ $cleared = ($form->{"cleared_$i"}) ? 1 : 0;
+ }
+
+ # record payment
+ $query = qq|INSERT INTO acc_trans (trans_id, chart_id, amount, transdate,
+ source, memo, cleared)
+ VALUES ($form->{id}, (SELECT id FROM chart
+ WHERE accno = '$accno'),
+ $form->{"paid_$i"}, '$form->{"datepaid_$i"}', |
+ .$dbh->quote($form->{"source_$i"}).qq|, |
+ .$dbh->quote($form->{"memo_$i"}).qq|, '$cleared')|;
+ $dbh->do($query) || $form->dberror($query);
+
+ # exchangerate difference
+ $amount = $form->round_amount($form->{"paid_$i"} * $form->{"exchangerate_$i"} - $form->{"paid_$i"}, 2);
+
+ if ($amount) {
+ $query = qq|INSERT INTO acc_trans (trans_id, chart_id, amount,
+ transdate, source, fx_transaction, cleared)
+ VALUES ($form->{id}, (SELECT id FROM chart
+ WHERE accno = '$accno'),
+ $amount, '$form->{"datepaid_$i"}', |
+ .$dbh->quote($form->{"source_$i"}).qq|, '1', '$cleared')|;
+ $dbh->do($query) || $form->dberror($query);
+ }
+
+ # gain/loss
+ $amount = $form->round_amount($form->round_amount($form->{"paid_$i"} * $form->{exchangerate},2) - $form->round_amount($form->{"paid_$i"} * $form->{"exchangerate_$i"},2), 2);
+
+ if ($amount) {
+ my $accno_id = ($amount > 0) ? $fxgain_accno_id : $fxloss_accno_id;
+ $query = qq|INSERT INTO acc_trans (trans_id, chart_id, amount,
+ transdate, fx_transaction, cleared)
+ VALUES ($form->{id}, $accno_id,
+ $amount, '$form->{"datepaid_$i"}', '1', '$cleared')|;
+ $dbh->do($query) || $form->dberror($query);
+ }
+
+ # update exchange rate
+ if (($form->{currency} ne $form->{defaultcurrency}) && !$exchangerate) {
+ $form->update_exchangerate($dbh, $form->{currency}, $form->{"datepaid_$i"}, 0, $form->{"exchangerate_$i"});
+ }
+ }
+ }
+
+ # set values which could be empty
+ $form->{taxincluded} *= 1;
+
+ $form->{invnumber} = $form->update_defaults($myconfig, "vinumber", $dbh) unless $form->{invnumber};
+
+ # save AP record
+ $query = qq|UPDATE ap set
+ invnumber = |.$dbh->quote($form->{invnumber}).qq|,
+ ordnumber = |.$dbh->quote($form->{ordnumber}).qq|,
+ quonumber = |.$dbh->quote($form->{quonumber}).qq|,
+ transdate = '$form->{transdate}',
+ vendor_id = $form->{vendor_id},
+ amount = $invamount,
+ netamount = $invnetamount,
+ paid = $form->{paid},
+ datepaid = |.$form->dbquote($form->{datepaid}, SQL_DATE).qq|,
+ duedate = |.$form->dbquote($form->{duedate}, SQL_DATE).qq|,
+ invoice = '1',
+ shippingpoint = |.$dbh->quote($form->{shippingpoint}).qq|,
+ shipvia = |.$dbh->quote($form->{shipvia}).qq|,
+ taxincluded = '$form->{taxincluded}',
+ notes = |.$dbh->quote($form->{notes}).qq|,
+ intnotes = |.$dbh->quote($form->{intnotes}).qq|,
+ curr = '$form->{currency}',
+ department_id = $form->{department_id},
+ employee_id = $form->{employee_id},
+ language_code = '$form->{language_code}',
+ ponumber = |.$dbh->quote($form->{ponumber}).qq|
+ WHERE id = $form->{id}|;
+ $dbh->do($query) || $form->dberror($query);
+
+ # add shipto
+ $form->{name} = $form->{vendor};
+ $form->{name} =~ s/--$form->{vendor_id}//;
+ $form->add_shipto($dbh, $form->{id});
+
+ my %audittrail = ( tablename => 'ap',
+ reference => $form->{invnumber},
+ formname => $form->{type},
+ action => 'posted',
+ id => $form->{id} );
+
+ $form->audittrail($dbh, "", \%audittrail);
+
+ my $rc = $dbh->commit;
+
+ foreach $item (keys %updparts) {
+ $query = qq|UPDATE parts SET
+ avgcost = avgcost($item),
+ lastcost = lastcost($item)
+ WHERE id = $item|;
+ $dbh->do($query) || $form->dberror($query);
+ $dbh->commit;
+ }
+
+ $dbh->disconnect;
+ $rc;
+
+}
+
+
+
+sub reverse_invoice {
+ my ($dbh, $form) = @_;
+
+ my $query = qq|SELECT id FROM ap
+ WHERE id = $form->{id}|;
+ my ($id) = $dbh->selectrow_array($query);
+
+ return unless $id;
+
+ # reverse inventory items
+ $query = qq|SELECT i.parts_id, p.inventory_accno_id, p.expense_accno_id,
+ i.qty, i.allocated, i.sellprice, i.project_id
+ FROM invoice i, parts p
+ WHERE i.parts_id = p.id
+ AND i.trans_id = $form->{id}|;
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ my $netamount = 0;
+
+ while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
+ $netamount += $form->round_amount($ref->{sellprice} * $ref->{qty} * -1, 2);
+
+ if ($ref->{inventory_accno_id}) {
+ # update onhand
+ $form->update_balance($dbh,
+ "parts",
+ "onhand",
+ qq|id = $ref->{parts_id}|,
+ $ref->{qty});
+
+ # if $ref->{allocated} > 0 than we sold that many items
+ if ($ref->{allocated} > 0) {
+
+ # get references for sold items
+ $query = qq|SELECT i.id, i.trans_id, i.allocated, a.transdate
+ FROM invoice i, ar a
+ WHERE i.parts_id = $ref->{parts_id}
+ AND i.allocated < 0
+ AND i.trans_id = a.id
+ ORDER BY transdate DESC|;
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while (my $pthref = $sth->fetchrow_hashref(NAME_lc)) {
+ my $qty = $ref->{allocated};
+
+ if (($ref->{allocated} + $pthref->{allocated}) > 0) {
+ $qty = $pthref->{allocated} * -1;
+ }
+
+ my $amount = $form->round_amount($ref->{sellprice} * $qty, 2);
+
+ #adjust allocated
+ $form->update_balance($dbh,
+ "invoice",
+ "allocated",
+ qq|id = $pthref->{id}|,
+ $qty);
+
+ # add reversal for sale
+ $ref->{project_id} *= 1;
+ $query = qq|INSERT INTO acc_trans (trans_id, chart_id, amount,
+ transdate, project_id)
+ VALUES ($pthref->{trans_id}, $ref->{expense_accno_id},
+ $amount, '$form->{transdate}', $ref->{project_id})|;
+ $dbh->do($query) || $form->dberror($query);
+
+ $query = qq|INSERT INTO acc_trans (trans_id, chart_id, amount,
+ transdate, project_id)
+ VALUES ($pthref->{trans_id}, $ref->{inventory_accno_id},
+ $amount * -1, '$form->{transdate}', $ref->{project_id})|;
+ $dbh->do($query) || $form->dberror($query);
+
+ last if (($ref->{allocated} -= $qty) <= 0);
+ }
+ $sth->finish;
+ }
+ }
+ }
+ $sth->finish;
+
+ # delete acc_trans
+ $query = qq|DELETE FROM acc_trans
+ WHERE trans_id = $form->{id}|;
+ $dbh->do($query) || $form->dberror($query);
+
+ # delete invoice entries
+ $query = qq|DELETE FROM invoice
+ WHERE trans_id = $form->{id}|;
+ $dbh->do($query) || $form->dberror($query);
+
+ $query = qq|DELETE FROM shipto
+ WHERE trans_id = $form->{id}|;
+ $dbh->do($query) || $form->dberror($query);
+
+ $dbh->commit;
+
+}
+
+
+
+sub delete_invoice {
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect_noauto($myconfig);
+
+ my %audittrail = ( tablename => 'ap',
+ reference => $form->{invnumber},
+ formname => $form->{type},
+ action => 'deleted',
+ id => $form->{id} );
+
+ $form->audittrail($dbh, "", \%audittrail);
+
+ my $query = qq|SELECT parts_id FROM invoice
+ WHERE trans_id = $form->{id}|;
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ my $item;
+ my %updparts = ();
+ while (($item) = $sth->fetchrow_array) {
+ $updparts{$item} = 1;
+ }
+ $sth->finish;
+
+ &reverse_invoice($dbh, $form);
+
+ # delete AP record
+ $query = qq|DELETE FROM ap
+ WHERE id = $form->{id}|;
+ $dbh->do($query) || $form->dberror($query);
+
+ # delete spool files
+ $query = qq|SELECT spoolfile FROM status
+ WHERE trans_id = $form->{id}
+ AND spoolfile IS NOT NULL|;
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ my $spoolfile;
+ my @spoolfiles = ();
+
+ while (($spoolfile) = $sth->fetchrow_array) {
+ push @spoolfiles, $spoolfile;
+ }
+ $sth->finish;
+
+ # delete status entries
+ $query = qq|DELETE FROM status
+ WHERE trans_id = $form->{id}|;
+ $dbh->do($query) || $form->dberror($query);
+
+ my $rc = $dbh->commit;
+
+ if ($rc) {
+ foreach $item (keys %updparts) {
+ $query = qq|UPDATE parts SET
+ avgcost = avgcost($item),
+ lastcost = lastcost($item)
+ WHERE id = $item|;
+ $dbh->do($query) || $form->dberror($query);
+ $dbh->commit;
+ }
+
+ foreach $spoolfile (@spoolfiles) {
+ unlink "$spool/$spoolfile" if $spoolfile;
+ }
+ }
+
+ $dbh->disconnect;
+
+ $rc;
+
+}
+
+
+
+sub retrieve_invoice {
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect_noauto($myconfig);
+
+ my $query;
+
+ if ($form->{id}) {
+ # get default accounts and last invoice number
+ $query = qq|SELECT (SELECT c.accno FROM chart c
+ WHERE d.inventory_accno_id = c.id) AS inventory_accno,
+ (SELECT c.accno FROM chart c
+ WHERE d.income_accno_id = c.id) AS income_accno,
+ (SELECT c.accno FROM chart c
+ WHERE d.expense_accno_id = c.id) AS expense_accno,
+ (SELECT c.accno FROM chart c
+ WHERE d.fxgain_accno_id = c.id) AS fxgain_accno,
+ (SELECT c.accno FROM chart c
+ WHERE d.fxloss_accno_id = c.id) AS fxloss_accno,
+ d.curr AS currencies
+ FROM defaults d|;
+ } else {
+ $query = qq|SELECT (SELECT c.accno FROM chart c
+ WHERE d.inventory_accno_id = c.id) AS inventory_accno,
+ (SELECT c.accno FROM chart c
+ WHERE d.income_accno_id = c.id) AS income_accno,
+ (SELECT c.accno FROM chart c
+ WHERE d.expense_accno_id = c.id) AS expense_accno,
+ (SELECT c.accno FROM chart c
+ WHERE d.fxgain_accno_id = c.id) AS fxgain_accno,
+ (SELECT c.accno FROM chart c
+ WHERE d.fxloss_accno_id = c.id) AS fxloss_accno,
+ d.curr AS currencies,
+ current_date AS transdate
+ FROM defaults d|;
+ }
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ my $ref = $sth->fetchrow_hashref(NAME_lc);
+ for (keys %$ref) {
+ $form->{$_} = $ref->{$_};
+ }
+ $sth->finish;
+
+
+ if ($form->{id}) {
+
+ # retrieve invoice
+ $query = qq|SELECT a.invnumber, a.transdate, a.duedate,
+ a.ordnumber, a.quonumber, a.paid, a.taxincluded, a.notes,
+ a.intnotes, a.curr AS currency, a.vendor_id, a.language_code,
+ a.ponumber
+ FROM ap a
+ WHERE id = $form->{id}|;
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ $ref = $sth->fetchrow_hashref(NAME_lc);
+ for (keys %$ref) {
+ $form->{$_} = $ref->{$_};
+ }
+ $sth->finish;
+
+ # get shipto
+ $query = qq|SELECT * FROM shipto
+ WHERE trans_id = $form->{id}|;
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ $ref = $sth->fetchrow_hashref(NAME_lc);
+ for (keys %$ref) {
+ $form->{$_} = $ref->{$_};
+ }
+ $sth->finish;
+
+ # retrieve individual items
+ $query = qq|SELECT
+ p.partnumber, i.description, i.qty, i.fxsellprice, i.sellprice,
+ i.parts_id AS id, i.unit, p.bin, i.deliverydate,
+ pr.projectnumber,
+ i.project_id, i.serialnumber, i.discount, i.notes,
+ pg.partsgroup, p.partsgroup_id, p.partnumber AS sku,
+ p.weight, p.onhand,
+ p.inventory_accno_id, p.income_accno_id, p.expense_accno_id,
+ t.description AS partsgrouptranslation
+ FROM invoice i
+ JOIN parts p ON (i.parts_id = p.id)
+ LEFT JOIN project pr ON (i.project_id = pr.id)
+ LEFT JOIN partsgroup pg ON (pg.id = p.partsgroup_id)
+ LEFT JOIN translation t ON (t.trans_id = p.partsgroup_id AND t.language_code = '$form->{language_code}')
+ WHERE i.trans_id = $form->{id}
+ ORDER BY i.id|;
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ # exchangerate defaults
+ &exchangerate_defaults($dbh, $form);
+
+ # price matrix and vendor partnumber
+ $query = qq|SELECT partnumber
+ FROM partsvendor
+ WHERE parts_id = ?
+ AND vendor_id = $form->{vendor_id}|;
+ my $pmh = $dbh->prepare($query) || $form->dberror($query);
+
+ # tax rates for part
+ $query = qq|SELECT c.accno
+ FROM chart c
+ JOIN partstax pt ON (pt.chart_id = c.id)
+ WHERE pt.parts_id = ?|;
+ my $tth = $dbh->prepare($query);
+
+ my $ptref;
+
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+
+ my ($dec) = ($ref->{fxsellprice} =~ /\.(\d+)/);
+ $dec = length $dec;
+ my $decimalplaces = ($dec > 2) ? $dec : 2;
+
+ $tth->execute($ref->{id});
+ $ref->{taxaccounts} = "";
+ my $taxrate = 0;
+
+ while ($ptref = $tth->fetchrow_hashref(NAME_lc)) {
+ $ref->{taxaccounts} .= "$ptref->{accno} ";
+ $taxrate += $form->{"$ptref->{accno}_rate"};
+ }
+
+ $tth->finish;
+ chop $ref->{taxaccounts};
+
+ # price matrix
+ $ref->{sellprice} = $form->round_amount($ref->{fxsellprice} * $form->{$form->{currency}}, $decimalplaces);
+ &price_matrix($pmh, $ref, $decimalplaces, $form);
+
+ $ref->{sellprice} = $ref->{fxsellprice};
+ $ref->{qty} *= -1;
+
+ $ref->{partsgroup} = $ref->{partsgrouptranslation} if $ref->{partsgrouptranslation};
+
+ push @{ $form->{invoice_details} }, $ref;
+
+ }
+
+ $sth->finish;
+
+ }
+
+
+ my $rc = $dbh->commit;
+ $dbh->disconnect;
+
+ $rc;
+
+}
+
+
+sub retrieve_item {
+ my ($self, $myconfig, $form) = @_;
+
+ my $i = $form->{rowcount};
+ my $null;
+ my $var;
+
+ # don't include assemblies or obsolete parts
+ my $where = "WHERE p.assembly = '0' AND p.obsolete = '0'";
+
+ if ($form->{"partnumber_$i"} ne "") {
+ $var = $form->like(lc $form->{"partnumber_$i"});
+ $where .= " AND lower(p.partnumber) LIKE '$var'";
+ }
+
+ if ($form->{"description_$i"} ne "") {
+ $var = $form->like(lc $form->{"description_$i"});
+ if ($form->{language_code} ne "") {
+ $where .= " AND lower(t1.description) LIKE '$var'";
+ } else {
+ $where .= " AND lower(p.description) LIKE '$var'";
+ }
+ }
+
+ if ($form->{"partsgroup_$i"} ne "") {
+ ($null, $var) = split /--/, $form->{"partsgroup_$i"};
+ $var *= 1;
+ $where .= qq| AND p.partsgroup_id = $var|;
+ }
+
+ if ($form->{"description_$i"} ne "") {
+ $where .= " ORDER BY 3";
+ } else {
+ $where .= " ORDER BY 2";
+ }
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ my $query = qq|SELECT p.id, p.partnumber, p.description,
+ pg.partsgroup, p.partsgroup_id,
+ p.lastcost AS sellprice, p.unit, p.bin, p.onhand, p.notes,
+ p.inventory_accno_id, p.income_accno_id, p.expense_accno_id,
+ p.partnumber AS sku, p.weight,
+ t1.description AS translation,
+ t2.description AS grouptranslation
+ FROM parts p
+ LEFT JOIN partsgroup pg ON (pg.id = p.partsgroup_id)
+ LEFT JOIN translation t1 ON (t1.trans_id = p.id AND t1.language_code = '$form->{language_code}')
+ LEFT JOIN translation t2 ON (t2.trans_id = p.partsgroup_id AND t2.language_code = '$form->{language_code}')
+ $where|;
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ # foreign currency
+ &exchangerate_defaults($dbh, $form);
+
+ # taxes
+ $query = qq|SELECT c.accno
+ FROM chart c
+ JOIN partstax pt ON (pt.chart_id = c.id)
+ WHERE pt.parts_id = ?|;
+ my $tth = $dbh->prepare($query) || $form->dberror($query);
+
+ # price matrix
+ $query = qq|SELECT p.*
+ FROM partsvendor p
+ WHERE p.parts_id = ?
+ AND vendor_id = $form->{vendor_id}|;
+ my $pmh = $dbh->prepare($query) || $form->dberror($query);
+
+ my $ref;
+ my $ptref;
+
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+
+ my ($dec) = ($ref->{sellprice} =~ /\.(\d+)/);
+ $dec = length $dec;
+ my $decimalplaces = ($dec > 2) ? $dec : 2;
+
+ # get taxes for part
+ $tth->execute($ref->{id});
+
+ $ref->{taxaccounts} = "";
+ while ($ptref = $tth->fetchrow_hashref(NAME_lc)) {
+ $ref->{taxaccounts} .= "$ptref->{accno} ";
+ }
+ $tth->finish;
+ chop $ref->{taxaccounts};
+
+ # get vendor price and partnumber
+ &price_matrix($pmh, $ref, $decimalplaces, $form);
+
+ $ref->{description} = $ref->{translation} if $ref->{translation};
+ $ref->{partsgroup} = $ref->{grouptranslation} if $ref->{grouptranslation};
+
+ push @{ $form->{item_list} }, $ref;
+
+ }
+
+ $sth->finish;
+ $dbh->disconnect;
+
+}
+
+
+sub exchangerate_defaults {
+ my ($dbh, $form) = @_;
+
+ my $var;
+
+ # get default currencies
+ my $query = qq|SELECT substr(curr,1,3), curr FROM defaults|;
+ my $eth = $dbh->prepare($query) || $form->dberror($query);
+ $eth->execute;
+ ($form->{defaultcurrency}, $form->{currencies}) = $eth->fetchrow_array;
+ $eth->finish;
+
+ $query = qq|SELECT sell
+ FROM exchangerate
+ WHERE curr = ?
+ AND transdate = ?|;
+ my $eth1 = $dbh->prepare($query) || $form->dberror($query);
+
+ $query = qq~SELECT max(transdate || ' ' || sell || ' ' || curr)
+ FROM exchangerate
+ WHERE curr = ?~;
+ my $eth2 = $dbh->prepare($query) || $form->dberror($query);
+
+ # get exchange rates for transdate or max
+ foreach $var (split /:/, substr($form->{currencies},4)) {
+ $eth1->execute($var, $form->{transdate});
+ ($form->{$var}) = $eth1->fetchrow_array;
+ if (! $form->{$var} ) {
+ $eth2->execute($var);
+
+ ($form->{$var}) = $eth2->fetchrow_array;
+ ($null, $form->{$var}) = split / /, $form->{$var};
+ $form->{$var} = 1 unless $form->{$var};
+ $eth2->finish;
+ }
+ $eth1->finish;
+ }
+
+ $form->{$form->{currency}} = $form->{exchangerate} if $form->{exchangerate};
+ $form->{$form->{currency}} ||= 1;
+ $form->{$form->{defaultcurrency}} = 1;
+
+}
+
+
+sub price_matrix {
+ my ($pmh, $ref, $decimalplaces, $form) = @_;
+
+ $pmh->execute($ref->{id});
+ my $mref = $pmh->fetchrow_hashref(NAME_lc);
+
+ if ($mref->{partnumber} ne "") {
+ $ref->{partnumber} = $mref->{partnumber};
+ }
+
+ if ($mref->{lastcost}) {
+ # do a conversion
+ $ref->{sellprice} = $form->round_amount($mref->{lastcost} * $form->{$mref->{curr}}, $decimalplaces);
+ }
+ $pmh->finish;
+
+ $ref->{sellprice} *= 1;
+
+ # add 0:price to matrix
+ $ref->{pricematrix} = "0:$ref->{sellprice}";
+
+}
+
+
+sub vendor_details {
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ # get rest for the vendor
+ my $query = qq|SELECT vendornumber, name, address1, address2, city, state,
+ zipcode, country,
+ contact, phone as vendorphone, fax as vendorfax, vendornumber,
+ taxnumber AS vendortaxnumber, sic_code AS sic, iban, bic,
+ gifi_accno AS gifi, startdate, enddate
+ FROM vendor
+ WHERE id = $form->{vendor_id}|;
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ $ref = $sth->fetchrow_hashref(NAME_lc);
+ for (keys %$ref) {
+ $form->{$_} = $ref->{$_};
+ }
+
+ $sth->finish;
+ $dbh->disconnect;
+
+}
+
+
+sub item_links {
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ my $query = qq|SELECT accno, description, link
+ FROM chart
+ WHERE link LIKE '%IC%'
+ ORDER BY accno|;
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
+ foreach my $key (split(/:/, $ref->{link})) {
+ if ($key =~ /IC/) {
+ push @{ $form->{IC_links}{$key} }, { accno => $ref->{accno},
+ description => $ref->{description} };
+ }
+ }
+ }
+
+ $sth->finish;
+}
+
+1;
+
diff --git a/LedgerSMB/IS.pm b/LedgerSMB/IS.pm
new file mode 100755
index 00000000..5bb7ef63
--- /dev/null
+++ b/LedgerSMB/IS.pm
@@ -0,0 +1,1684 @@
+#=====================================================================
+# LedgerSMB
+# Small Medium Business Accounting software
+#
+# See COPYRIGHT file for copyright information
+#======================================================================
+#
+# This file has NOT undergone whitespace cleanup.
+#
+#======================================================================
+#
+# Inventory invoicing module
+#
+#======================================================================
+
+package IS;
+
+
+sub invoice_details {
+ my ($self, $myconfig, $form) = @_;
+
+ $form->{duedate} = $form->{transdate} unless ($form->{duedate});
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ my $query = qq|SELECT date '$form->{duedate}' - date '$form->{transdate}'
+ AS terms, weightunit
+ FROM defaults|;
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ ($form->{terms}, $form->{weightunit}) = $sth->fetchrow_array;
+ $sth->finish;
+
+ # this is for the template
+ $form->{invdate} = $form->{transdate};
+
+ my $tax = 0;
+ my $item;
+ my $i;
+ my @sortlist = ();
+ my $projectnumber;
+ my $projectdescription;
+ my $projectnumber_id;
+ my $translation;
+ my $partsgroup;
+
+ my %oid = ( 'Pg' => 'oid',
+ 'PgPP' => 'oid',
+ 'Oracle' => 'rowid',
+ 'DB2' => '1=1'
+ );
+
+ my @taxaccounts;
+ my %taxaccounts;
+ my $tax;
+ my $taxrate;
+ my $taxamount;
+
+ my %translations;
+
+ $query = qq|SELECT p.description, t.description
+ FROM project p
+ LEFT JOIN translation t ON (t.trans_id = p.id AND t.language_code = '$form->{language_code}')
+ WHERE id = ?|;
+ my $prh = $dbh->prepare($query) || $form->dberror($query);
+
+ $query = qq|SELECT inventory_accno_id, income_accno_id,
+ expense_accno_id, assembly, weight FROM parts
+ WHERE id = ?|;
+ my $pth = $dbh->prepare($query) || $form->dberror($query);
+
+ my $sortby;
+
+ # sort items by project and partsgroup
+ for $i (1 .. $form->{rowcount} - 1) {
+
+ # account numbers
+ $pth->execute($form->{"id_$i"});
+ $ref = $pth->fetchrow_hashref(NAME_lc);
+ for (keys %$ref) { $form->{"${_}_$i"} = $ref->{$_} }
+ $pth->finish;
+
+ $projectnumber_id = 0;
+ $projectnumber = "";
+ $form->{partsgroup} = "";
+ $form->{projectnumber} = "";
+
+ if ($form->{groupprojectnumber} || $form->{grouppartsgroup}) {
+
+ $inventory_accno_id = ($form->{"inventory_accno_id_$i"} || $form->{"assembly_$i"}) ? "1" : "";
+
+ if ($form->{groupprojectnumber}) {
+ ($projectnumber, $projectnumber_id) = split /--/, $form->{"projectnumber_$i"};
+ }
+ if ($form->{grouppartsgroup}) {
+ ($form->{partsgroup}) = split /--/, $form->{"partsgroup_$i"};
+ }
+
+ if ($projectnumber_id && $form->{groupprojectnumber}) {
+ if ($translation{$projectnumber_id}) {
+ $form->{projectnumber} = $translation{$projectnumber_id};
+ } else {
+ # get project description
+ $prh->execute($projectnumber_id);
+ ($projectdescription, $translation) = $prh->fetchrow_array;
+ $prh->finish;
+
+ $form->{projectnumber} = ($translation) ? "$projectnumber, $translation" : "$projectnumber, $projectdescription";
+
+ $translation{$projectnumber_id} = $form->{projectnumber};
+ }
+ }
+
+ if ($form->{grouppartsgroup} && $form->{partsgroup}) {
+ $form->{projectnumber} .= " / " if $projectnumber_id;
+ $form->{projectnumber} .= $form->{partsgroup};
+ }
+
+ $form->format_string(projectnumber);
+
+ }
+
+ $sortby = qq|$projectnumber$form->{partsgroup}|;
+ if ($form->{sortby} ne 'runningnumber') {
+ for (qw(partnumber description bin)) {
+ $sortby .= $form->{"${_}_$i"} if $form->{sortby} eq $_;
+ }
+ }
+
+ push @sortlist, [ $i, qq|$projectnumber$form->{partsgroup}$inventory_accno_id|, $form->{projectnumber}, $projectnumber_id, $form->{partsgroup}, $sortby ];
+
+ }
+
+ # sort the whole thing by project and group
+ @sortlist = sort { $a->[5] cmp $b->[5] } @sortlist;
+
+ my $runningnumber = 1;
+ my $sameitem = "";
+ my $subtotal;
+ my $k = scalar @sortlist;
+ my $j = 0;
+
+ foreach $item (@sortlist) {
+
+ $i = $item->[0];
+ $j++;
+
+ # heading
+ if ($form->{groupprojectnumber} || $form->{grouppartsgroup}) {
+ if ($item->[1] ne $sameitem) {
+ $sameitem = $item->[1];
+
+ $ok = 0;
+
+ if ($form->{groupprojectnumber}) {
+ $ok = $form->{"projectnumber_$i"};
+ }
+ if ($form->{grouppartsgroup}) {
+ $ok = $form->{"partsgroup_$i"} unless $ok;
+ }
+
+ if ($ok) {
+
+ if ($form->{"inventory_accno_id_$i"} || $form->{"assembly_$i"}) {
+ push(@{ $form->{part} }, "");
+ push(@{ $form->{service} }, NULL);
+ } else {
+ push(@{ $form->{part} }, NULL);
+ push(@{ $form->{service} }, "");
+ }
+
+ push(@{ $form->{description} }, $item->[2]);
+ for (qw(taxrates runningnumber number sku serialnumber bin qty ship unit deliverydate projectnumber sellprice listprice netprice discount discountrate linetotal weight itemnotes)) { push(@{ $form->{$_} }, "") }
+ push(@{ $form->{lineitems} }, { amount => 0, tax => 0 });
+ }
+ }
+ }
+
+ $form->{"qty_$i"} = $form->parse_amount($myconfig, $form->{"qty_$i"});
+
+ if ($form->{"qty_$i"}) {
+
+ $form->{totalqty} += $form->{"qty_$i"};
+ $form->{totalship} += $form->{"qty_$i"};
+ $form->{totalweight} += ($form->{"qty_$i"} * $form->{"weight_$i"});
+ $form->{totalweightship} += ($form->{"qty_$i"} * $form->{"weight_$i"});
+
+ # add number, description and qty to $form->{number}, ....
+ push(@{ $form->{runningnumber} }, $runningnumber++);
+ push(@{ $form->{number} }, $form->{"partnumber_$i"});
+ push(@{ $form->{sku} }, $form->{"sku_$i"});
+ push(@{ $form->{serialnumber} }, $form->{"serialnumber_$i"});
+ push(@{ $form->{bin} }, $form->{"bin_$i"});
+ push(@{ $form->{description} }, $form->{"description_$i"});
+ push(@{ $form->{itemnotes} }, $form->{"notes_$i"});
+ push(@{ $form->{qty} }, $form->format_amount($myconfig, $form->{"qty_$i"}));
+ push(@{ $form->{ship} }, $form->format_amount($myconfig, $form->{"qty_$i"}));
+ push(@{ $form->{unit} }, $form->{"unit_$i"});
+ push(@{ $form->{deliverydate} }, $form->{"deliverydate_$i"});
+ push(@{ $form->{projectnumber} }, $form->{"projectnumber_$i"});
+
+ push(@{ $form->{sellprice} }, $form->{"sellprice_$i"});
+
+ # listprice
+ push(@{ $form->{listprice} }, $form->{"listprice_$i"});
+
+ push(@{ $form->{weight} }, $form->format_amount($myconfig, $form->{"weight_$i"} * $form->{"qty_$i"}));
+
+ my $sellprice = $form->parse_amount($myconfig, $form->{"sellprice_$i"});
+ my ($dec) = ($sellprice =~ /\.(\d+)/);
+ $dec = length $dec;
+ my $decimalplaces = ($dec > 2) ? $dec : 2;
+
+ my $discount = $form->round_amount($sellprice * $form->parse_amount($myconfig, $form->{"discount_$i"})/100, $decimalplaces);
+
+ # keep a netprice as well, (sellprice - discount)
+ $form->{"netprice_$i"} = $sellprice - $discount;
+
+ my $linetotal = $form->round_amount($form->{"qty_$i"} * $form->{"netprice_$i"}, 2);
+
+ if ($form->{"inventory_accno_id_$i"} || $form->{"assembly_$i"}) {
+ push(@{ $form->{part} }, $form->{"sku_$i"});
+ push(@{ $form->{service} }, NULL);
+ $form->{totalparts} += $linetotal;
+ } else {
+ push(@{ $form->{service} }, $form->{"sku_$i"});
+ push(@{ $form->{part} }, NULL);
+ $form->{totalservices} += $linetotal;
+ }
+
+ push(@{ $form->{netprice} }, ($form->{"netprice_$i"}) ? $form->format_amount($myconfig, $form->{"netprice_$i"}, $decimalplaces) : " ");
+
+ $discount = ($discount) ? $form->format_amount($myconfig, $discount * -1, $decimalplaces) : " ";
+ $linetotal = ($linetotal) ? $linetotal : " ";
+
+ push(@{ $form->{discount} }, $discount);
+ push(@{ $form->{discountrate} }, $form->format_amount($myconfig, $form->{"discount_$i"}));
+
+ $form->{total} += $linetotal;
+
+ # this is for the subtotals for grouping
+ $subtotal += $linetotal;
+
+ $form->{"linetotal_$i"} = $form->format_amount($myconfig, $linetotal, 2);
+ push(@{ $form->{linetotal} }, $form->{"linetotal_$i"});
+
+ @taxaccounts = split / /, $form->{"taxaccounts_$i"};
+
+ my $ml = 1;
+ my @taxrates = ();
+
+ $tax = 0;
+
+ for (0 .. 1) {
+ $taxrate = 0;
+
+ for (@taxaccounts) { $taxrate += $form->{"${_}_rate"} if ($form->{"${_}_rate"} * $ml) > 0 }
+
+ $taxrate *= $ml;
+ $taxamount = $linetotal * $taxrate / (1 + $taxrate);
+ $taxbase = ($linetotal - $taxamount);
+
+ foreach $item (@taxaccounts) {
+ if (($form->{"${item}_rate"} * $ml) > 0) {
+
+ push @taxrates, $form->{"${item}_rate"} * 100;
+
+ if ($form->{taxincluded}) {
+ $taxaccounts{$item} += $linetotal * $form->{"${item}_rate"} / (1 + $taxrate);
+ $taxbase{$item} += $taxbase;
+ } else {
+ $taxbase{$item} += $linetotal;
+ $taxaccounts{$item} += $linetotal * $form->{"${item}_rate"};
+ }
+ }
+ }
+
+ if ($form->{taxincluded}) {
+ $tax += $linetotal * ($taxrate / (1 + ($taxrate * $ml)));
+ } else {
+ $tax += $linetotal * $taxrate;
+ }
+
+ $ml *= -1;
+ }
+
+ push(@{ $form->{lineitems} }, { amount => $linetotal, tax => $form->round_amount($tax, 2) });
+ push(@{ $form->{taxrates} }, join ' ', sort { $a <=> $b } @taxrates);
+
+ if ($form->{"assembly_$i"}) {
+ $form->{stagger} = -1;
+ &assembly_details($myconfig, $form, $dbh, $form->{"id_$i"}, $oid{$myconfig->{dbdriver}}, $form->{"qty_$i"});
+ }
+
+ }
+
+ # add subtotal
+ if ($form->{groupprojectnumber} || $form->{grouppartsgroup}) {
+ if ($subtotal) {
+ if ($j < $k) {
+ # look at next item
+ if ($sortlist[$j]->[1] ne $sameitem) {
+
+ if ($form->{"inventory_accno_id_$j"} || $form->{"assembly_$i"}) {
+ push(@{ $form->{part} }, "");
+ push(@{ $form->{service} }, NULL);
+ } else {
+ push(@{ $form->{service} }, "");
+ push(@{ $form->{part} }, NULL);
+ }
+
+ for (qw(taxrates runningnumber number sku serialnumber bin qty ship unit deliverydate projectnumber sellprice listprice netprice discount discountrate weight itemnotes)) { push(@{ $form->{$_} }, "") }
+
+ push(@{ $form->{description} }, $form->{groupsubtotaldescription});
+
+ push(@{ $form->{lineitems} }, { amount => 0, tax => 0 });
+
+ if ($form->{groupsubtotaldescription} ne "") {
+ push(@{ $form->{linetotal} }, $form->format_amount($myconfig, $subtotal, 2));
+ } else {
+ push(@{ $form->{linetotal} }, "");
+ }
+ $subtotal = 0;
+ }
+
+ } else {
+
+ # got last item
+ if ($form->{groupsubtotaldescription} ne "") {
+
+ if ($form->{"inventory_accno_id_$j"} || $form->{"assembly_$i"}) {
+ push(@{ $form->{part} }, "");
+ push(@{ $form->{service} }, NULL);
+ } else {
+ push(@{ $form->{service} }, "");
+ push(@{ $form->{part} }, NULL);
+ }
+
+ for (qw(taxrates runningnumber number sku serialnumber bin qty ship unit deliverydate projectnumber sellprice listprice netprice discount discountrate weight itemnotes)) { push(@{ $form->{$_} }, "") }
+
+ push(@{ $form->{description} }, $form->{groupsubtotaldescription});
+ push(@{ $form->{linetotal} }, $form->format_amount($myconfig, $subtotal, 2));
+ push(@{ $form->{lineitems} }, { amount => 0, tax => 0 });
+ }
+ }
+ }
+ }
+ }
+
+
+ $tax = 0;
+ foreach my $item (sort keys %taxaccounts) {
+ if ($form->round_amount($taxaccounts{$item}, 2)) {
+ $tax += $taxamount = $form->round_amount($taxaccounts{$item}, 2);
+
+ push(@{ $form->{taxbaseinclusive} }, $form->{"${item}_taxbaseinclusive"} = $form->format_amount($myconfig, $taxbase{$item} + $tax, 2));
+ push(@{ $form->{taxbase} }, $form->{"${item}_taxbase"} = $form->format_amount($myconfig, $taxbase{$item}, 2));
+ push(@{ $form->{tax} }, $form->{"${item}_tax"} = $form->format_amount($myconfig, $taxamount, 2));
+
+ push(@{ $form->{taxdescription} }, $form->{"${item}_description"});
+
+ $form->{"${item}_taxrate"} = $form->format_amount($myconfig, $form->{"${item}_rate"} * 100);
+ push(@{ $form->{taxrate} }, $form->{"${item}_taxrate"});
+ push(@{ $form->{taxnumber} }, $form->{"${item}_taxnumber"});
+ }
+ }
+
+ # adjust taxes for lineitems
+ my $total = 0;
+ for (@{ $form->{lineitems} }) {
+ $total += $_->{tax};
+ }
+ if ($form->round_amount($total,2) != $form->round_amount($tax,2)) {
+ # get largest amount
+ for (reverse sort { $a->{tax} <=> $b->{tax} } @{ $form->{lineitems} }) {
+ $_->{tax} -= $total - $tax;
+ last;
+ }
+ }
+ $i = 1;
+ for (@{ $form->{lineitems} }) {
+ push(@{ $form->{linetax} }, $form->format_amount($myconfig, $_->{tax}, 2, ""));
+ }
+
+
+ for $i (1 .. $form->{paidaccounts}) {
+ if ($form->{"paid_$i"}) {
+ push(@{ $form->{payment} }, $form->{"paid_$i"});
+ my ($accno, $description) = split /--/, $form->{"AR_paid_$i"};
+ push(@{ $form->{paymentaccount} }, $description);
+ push(@{ $form->{paymentdate} }, $form->{"datepaid_$i"});
+ push(@{ $form->{paymentsource} }, $form->{"source_$i"});
+ push(@{ $form->{paymentmemo} }, $form->{"memo_$i"});
+
+ $form->{paid} += $form->parse_amount($myconfig, $form->{"paid_$i"});
+ }
+ }
+
+ for (qw(totalparts totalservices)) { $form->{$_} = $form->format_amount($myconfig, $form->{$_}, 2) }
+ for (qw(totalqty totalship totalweight)) { $form->{$_} = $form->format_amount($myconfig, $form->{$_}) }
+ $form->{subtotal} = $form->format_amount($myconfig, $form->{total}, 2);
+ $form->{invtotal} = ($form->{taxincluded}) ? $form->{total} : $form->{total} + $tax;
+
+ use LedgerSMB::CP;
+ my $c;
+ if ($form->{language_code} ne "") {
+ $c = new CP $form->{language_code};
+ } else {
+ $c = new CP $myconfig->{countrycode};
+ }
+ $c->init;
+ my $whole;
+ ($whole, $form->{decimal}) = split /\./, $form->{invtotal};
+ $form->{decimal} .= "00";
+ $form->{decimal} = substr($form->{decimal}, 0, 2);
+ $form->{text_decimal} = $c->num2text($form->{decimal} * 1);
+ $form->{text_amount} = $c->num2text($whole);
+ $form->{integer_amount} = $form->format_amount($myconfig, $whole);
+
+ $form->format_string(qw(text_amount text_decimal));
+
+ $form->{total} = $form->format_amount($myconfig, $form->{invtotal} - $form->{paid}, 2);
+ $form->{invtotal} = $form->format_amount($myconfig, $form->{invtotal}, 2);
+
+ $form->{paid} = $form->format_amount($myconfig, $form->{paid}, 2);
+
+ $dbh->disconnect;
+
+}
+
+
+sub assembly_details {
+ my ($myconfig, $form, $dbh, $id, $oid, $qty) = @_;
+
+ my $sm = "";
+ my $spacer;
+
+ $form->{stagger}++;
+ if ($form->{format} eq 'html') {
+ $spacer = "&nbsp;" x (3 * ($form->{stagger} - 1)) if $form->{stagger} > 1;
+ }
+ if ($form->{format} =~ /(postscript|pdf)/) {
+ if ($form->{stagger} > 1) {
+ $spacer = ($form->{stagger} - 1) * 3;
+ $spacer = '\rule{'.$spacer.'mm}{0mm}';
+ }
+ }
+
+ # get parts and push them onto the stack
+ my $sortorder = "";
+
+ if ($form->{grouppartsgroup}) {
+ $sortorder = qq|ORDER BY pg.partsgroup, a.$oid|;
+ } else {
+ $sortorder = qq|ORDER BY a.$oid|;
+ }
+
+ my $query = qq|SELECT p.partnumber, p.description, p.unit, a.qty,
+ pg.partsgroup, p.partnumber AS sku
+ FROM assembly a
+ JOIN parts p ON (a.parts_id = p.id)
+ LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
+ WHERE a.bom = '1'
+ AND a.id = '$id'
+ $sortorder|;
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
+
+ for (qw(partnumber description partsgroup)) {
+ $form->{"a_$_"} = $ref->{$_};
+ $form->format_string("a_$_");
+ }
+
+ if ($form->{grouppartsgroup} && $ref->{partsgroup} ne $sm) {
+ for (qw(taxrates runningnumber number sku serialnumber unit qty ship bin deliverydate projectnumber sellprice listprice netprice discount discountrate linetotal weight itemnotes)) { push(@{ $form->{$_} }, "") }
+ $sm = ($form->{"a_partsgroup"}) ? $form->{"a_partsgroup"} : "--";
+ push(@{ $form->{description} }, "$spacer$sm");
+ push(@{ $form->{lineitems} }, { amount => 0, tax => 0 });
+ }
+
+ if ($form->{stagger}) {
+
+ push(@{ $form->{description} }, $form->format_amount($myconfig, $ref->{qty} * $form->{"qty_$i"}) . qq| -- $form->{"a_partnumber"}, $form->{"a_description"}|);
+ for (qw(taxrates runningnumber number sku serialnumber unit qty ship bin deliverydate projectnumber sellprice listprice netprice discount discountrate linetotal weight itemnotes)) { push(@{ $form->{$_} }, "") }
+
+ } else {
+
+ push(@{ $form->{description} }, qq|$form->{"a_description"}|);
+ push(@{ $form->{number} }, $form->{"a_partnumber"});
+ push(@{ $form->{sku} }, $form->{"a_partnumber"});
+
+ for (qw(taxrates runningnumber ship serialnumber reqdate projectnumber sellprice listprice netprice discount discountrate linetotal weight itemnotes)) { push(@{ $form->{$_} }, "") }
+
+ }
+
+ push(@{ $form->{lineitems} }, { amount => 0, tax => 0 });
+
+ push(@{ $form->{qty} }, $form->format_amount($myconfig, $ref->{qty} * $qty));
+
+ for (qw(unit bin)) {
+ $form->{"a_$_"} = $ref->{$_};
+ $form->format_string("a_$_");
+ push(@{ $form->{$_} }, $form->{"a_$_"});
+ }
+
+ }
+ $sth->finish;
+
+ $form->{stagger}--;
+
+}
+
+
+sub project_description {
+ my ($self, $dbh, $id) = @_;
+
+ my $query = qq|SELECT description
+ FROM project
+ WHERE id = $id|;
+ ($_) = $dbh->selectrow_array($query);
+
+ $_;
+
+}
+
+
+sub customer_details {
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ # get rest for the customer
+ my $query = qq|SELECT customernumber, name, address1, address2, city,
+ state, zipcode, country,
+ contact, phone as customerphone, fax as customerfax,
+ taxnumber AS customertaxnumber, sic_code AS sic, iban, bic,
+ startdate, enddate
+ FROM customer
+ WHERE id = $form->{customer_id}|;
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ $ref = $sth->fetchrow_hashref(NAME_lc);
+ for (keys %$ref) { $form->{$_} = $ref->{$_} }
+
+ $sth->finish;
+ $dbh->disconnect;
+
+}
+
+
+sub post_invoice {
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database, turn off autocommit
+ my $dbh = $form->dbconnect_noauto($myconfig);
+
+ my $query;
+ my $sth;
+ my $null;
+ my $project_id;
+ my $exchangerate = 0;
+ my $keepcleared = 0;
+
+ %$form->{acc_trans} = ();
+
+ ($null, $form->{employee_id}) = split /--/, $form->{employee};
+ unless ($form->{employee_id}) {
+ ($form->{employee}, $form->{employee_id}) = $form->get_employee($dbh);
+ }
+
+ ($null, $form->{department_id}) = split(/--/, $form->{department});
+ $form->{department_id} *= 1;
+
+ $query = qq|SELECT fxgain_accno_id, fxloss_accno_id
+ FROM defaults|;
+ my ($fxgain_accno_id, $fxloss_accno_id) = $dbh->selectrow_array($query);
+
+ $query = qq|SELECT p.assembly, p.inventory_accno_id,
+ p.income_accno_id, p.expense_accno_id, p.project_id
+ FROM parts p
+ WHERE p.id = ?|;
+ my $pth = $dbh->prepare($query) || $form->dberror($query);
+
+ if ($form->{id}) {
+ $keepcleared = 1;
+ $query = qq|SELECT id FROM ar
+ WHERE id = $form->{id}|;
+
+ if ($dbh->selectrow_array($query)) {
+ &reverse_invoice($dbh, $form);
+ } else {
+ $query = qq|INSERT INTO ar (id)
+ VALUES ($form->{id})|;
+ $dbh->do($query) || $form->dberror($query);
+ }
+
+ }
+
+ my $uid = localtime;
+ $uid .= "$$";
+
+ if (! $form->{id}) {
+
+ $query = qq|INSERT INTO ar (invnumber, employee_id)
+ VALUES ('$uid', $form->{employee_id})|;
+ $dbh->do($query) || $form->dberror($query);
+
+ $query = qq|SELECT id FROM ar
+ WHERE invnumber = '$uid'|;
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ ($form->{id}) = $sth->fetchrow_array;
+ $sth->finish;
+ }
+
+
+ if ($form->{currency} eq $form->{defaultcurrency}) {
+ $form->{exchangerate} = 1;
+ } else {
+ $exchangerate = $form->check_exchangerate($myconfig, $form->{currency}, $form->{transdate}, 'buy');
+ }
+
+ $form->{exchangerate} = ($exchangerate) ? $exchangerate : $form->parse_amount($myconfig, $form->{exchangerate});
+
+ my $i;
+ my $item;
+ my $allocated = 0;
+ my $taxrate;
+ my $tax;
+ my $fxtax;
+ my @taxaccounts;
+ my $amount;
+ my $grossamount;
+ my $invamount = 0;
+ my $invnetamount = 0;
+ my $diff = 0;
+ my $ml;
+ my $invoice_id;
+ my $ndx;
+
+ foreach $i (1 .. $form->{rowcount}) {
+ $form->{"qty_$i"} = $form->parse_amount($myconfig, $form->{"qty_$i"});
+
+ if ($form->{"qty_$i"}) {
+
+ $pth->execute($form->{"id_$i"});
+ $ref = $pth->fetchrow_hashref(NAME_lc);
+ for (keys %$ref) { $form->{"${_}_$i"} = $ref->{$_} }
+ $pth->finish;
+
+ # project
+ $project_id = 'NULL';
+ if ($form->{"projectnumber_$i"}) {
+ ($null, $project_id) = split /--/, $form->{"projectnumber_$i"};
+ }
+ $project_id = $form->{"project_id_$i"} if $form->{"project_id_$i"};
+
+ # keep entered selling price
+ my $fxsellprice = $form->parse_amount($myconfig, $form->{"sellprice_$i"});
+
+ my ($dec) = ($fxsellprice =~ /\.(\d+)/);
+ $dec = length $dec;
+ my $decimalplaces = ($dec > 2) ? $dec : 2;
+
+ # undo discount formatting
+ $form->{"discount_$i"} = $form->parse_amount($myconfig, $form->{"discount_$i"})/100;
+
+ # deduct discount
+ $form->{"sellprice_$i"} = $fxsellprice - $form->round_amount($fxsellprice * $form->{"discount_$i"}, $decimalplaces);
+
+ # linetotal
+ my $fxlinetotal = $form->round_amount($form->{"sellprice_$i"} * $form->{"qty_$i"}, 2);
+
+ $amount = $fxlinetotal * $form->{exchangerate};
+ my $linetotal = $form->round_amount($amount, 2);
+ $fxdiff += $amount - $linetotal;
+
+ @taxaccounts = split / /, $form->{"taxaccounts_$i"};
+ $ml = 1;
+ $tax = 0;
+ $fxtax = 0;
+
+ for (0 .. 1) {
+ $taxrate = 0;
+
+ # add tax rates
+ for (@taxaccounts) { $taxrate += $form->{"${_}_rate"} if ($form->{"${_}_rate"} * $ml) > 0 }
+
+ if ($form->{taxincluded}) {
+ $tax += $amount = $linetotal * ($taxrate / (1 + ($taxrate * $ml)));
+ $form->{"sellprice_$i"} -= $amount / $form->{"qty_$i"};
+ $fxtax += $fxlinetotal * ($taxrate / (1 + ($taxrate * $ml)));
+ } else {
+ $tax += $amount = $linetotal * $taxrate;
+ $fxtax += $fxlinetotal * $taxrate;
+ }
+
+ for (@taxaccounts) {
+ $form->{acc_trans}{$form->{id}}{$_}{amount} += $amount * $form->{"${_}_rate"} / $taxrate if ($form->{"${_}_rate"} * $ml) > 0;
+ }
+
+ $ml = -1;
+ }
+
+ $grossamount = $form->round_amount($linetotal, 2);
+
+ if ($form->{taxincluded}) {
+ $amount = $form->round_amount($tax, 2);
+ $linetotal -= $form->round_amount($tax - $diff, 2);
+ $diff = ($amount - $tax);
+ }
+
+ # add linetotal to income
+ $amount = $form->round_amount($linetotal, 2);
+
+ push @{ $form->{acc_trans}{lineitems} }, {
+ chart_id => $form->{"income_accno_id_$i"},
+ amount => $amount,
+ fxgrossamount => $fxlinetotal + $fxtax,
+ grossamount => $grossamount,
+ project_id => $project_id };
+ $ndx = $#{@{$form->{acc_trans}{lineitems}}};
+
+ $form->{"sellprice_$i"} = $form->round_amount($form->{"sellprice_$i"} * $form->{exchangerate}, $decimalplaces);
+
+ if ($form->{"inventory_accno_id_$i"} || $form->{"assembly_$i"}) {
+
+ if ($form->{"assembly_$i"}) {
+ # do not update if assembly consists of all services
+ $query = qq|SELECT sum(p.inventory_accno_id), p.assembly
+ FROM parts p
+ JOIN assembly a ON (a.parts_id = p.id)
+ WHERE a.id = $form->{"id_$i"}
+ GROUP BY p.assembly|;
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+ my ($inv, $assembly) = $sth->fetchrow_array;
+ $sth->finish;
+
+ if ($inv || $assembly) {
+ $form->update_balance($dbh,
+ "parts",
+ "onhand",
+ qq|id = $form->{"id_$i"}|,
+ $form->{"qty_$i"} * -1) unless $form->{shipped};
+ }
+
+ &process_assembly($dbh, $form, $form->{"id_$i"}, $form->{"qty_$i"}, $project_id);
+ } else {
+ $form->update_balance($dbh,
+ "parts",
+ "onhand",
+ qq|id = $form->{"id_$i"}|,
+ $form->{"qty_$i"} * -1) unless $form->{shipped};
+
+ $allocated = &cogs($dbh, $form, $form->{"id_$i"}, $form->{"qty_$i"}, $project_id);
+ }
+ }
+
+ # save detail record in invoice table
+ $query = qq|INSERT INTO invoice (description)
+ VALUES ('$uid')|;
+ $dbh->do($query) || $form->dberror($query);
+
+ $query = qq|SELECT id FROM invoice
+ WHERE description = '$uid'|;
+ ($invoice_id) = $dbh->selectrow_array($query);
+
+ $query = qq|UPDATE invoice SET
+ trans_id = $form->{id},
+ parts_id = $form->{"id_$i"},
+ description = |.$dbh->quote($form->{"description_$i"}).qq|,
+ qty = $form->{"qty_$i"},
+ sellprice = $form->{"sellprice_$i"},
+ fxsellprice = $fxsellprice,
+ discount = $form->{"discount_$i"},
+ allocated = $allocated,
+ unit = |.$dbh->quote($form->{"unit_$i"}).qq|,
+ deliverydate = |.$form->dbquote($form->{"deliverydate_$i"}, SQL_DATE).qq|,
+ project_id = $project_id,
+ serialnumber = |.$dbh->quote($form->{"serialnumber_$i"}).qq|,
+ notes = |.$dbh->quote($form->{"notes_$i"}).qq|
+ WHERE id = $invoice_id|;
+ $dbh->do($query) || $form->dberror($query);
+
+ # add invoice_id
+ $form->{acc_trans}{lineitems}[$ndx]->{invoice_id} = $invoice_id;
+
+ }
+ }
+
+ $form->{paid} = 0;
+ for $i (1 .. $form->{paidaccounts}) {
+ $form->{"paid_$i"} = $form->parse_amount($myconfig, $form->{"paid_$i"});
+ $form->{paid} += $form->{"paid_$i"};
+ $form->{datepaid} = $form->{"datepaid_$i"} if ($form->{"paid_$i"});
+ }
+
+ # add lineitems + tax
+ $amount = 0;
+ $grossamount = 0;
+ $fxgrossamount = 0;
+ for (@{ $form->{acc_trans}{lineitems} }) {
+ $amount += $_->{amount};
+ $grossamount += $_->{grossamount};
+ $fxgrossamount += $_->{fxgrossamount};
+ }
+ $invnetamount = $amount;
+
+ $amount = 0;
+ for (split / /, $form->{taxaccounts}) { $amount += $form->{acc_trans}{$form->{id}}{$_}{amount} = $form->round_amount($form->{acc_trans}{$form->{id}}{$_}{amount}, 2) }
+ $invamount = $invnetamount + $amount;
+
+ $diff = 0;
+ if ($form->{taxincluded}) {
+ $diff = $form->round_amount($grossamount - $invamount, 2);
+ $invamount += $diff;
+ }
+ $fxdiff = $form->round_amount($fxdiff,2);
+ $invnetamount += $fxdiff;
+ $invamount += $fxdiff;
+
+ if ($form->round_amount($form->{paid} - $fxgrossamount,2) == 0) {
+ $form->{paid} = $invamount;
+ } else {
+ $form->{paid} = $form->round_amount($form->{paid} * $form->{exchangerate}, 2);
+ }
+
+ foreach $ref (sort { $b->{amount} <=> $a->{amount} } @ { $form->{acc_trans}{lineitems} }) {
+ $amount = $ref->{amount} + $diff + $fxdiff;
+ $query = qq|INSERT INTO acc_trans (trans_id, chart_id, amount,
+ transdate, project_id, invoice_id)
+ VALUES ($form->{id}, $ref->{chart_id}, $amount,
+ '$form->{transdate}', $ref->{project_id}, $ref->{invoice_id})|;
+ $dbh->do($query) || $form->dberror($query);
+ $diff = 0;
+ $fxdiff = 0;
+ }
+
+ $form->{receivables} = $invamount * -1;
+
+ delete $form->{acc_trans}{lineitems};
+
+ # update exchangerate
+ if (($form->{currency} ne $form->{defaultcurrency}) && !$exchangerate) {
+ $form->update_exchangerate($dbh, $form->{currency}, $form->{transdate}, $form->{exchangerate}, 0);
+ }
+
+ # record receivable
+ if ($form->{receivables}) {
+ ($accno) = split /--/, $form->{AR};
+
+ $query = qq|INSERT INTO acc_trans (trans_id, chart_id, amount,
+ transdate)
+ VALUES ($form->{id},
+ (SELECT id FROM chart
+ WHERE accno = '$accno'),
+ $form->{receivables}, '$form->{transdate}')|;
+ $dbh->do($query) || $form->dberror($query);
+ }
+
+ foreach my $trans_id (keys %{$form->{acc_trans}}) {
+ foreach my $accno (keys %{$form->{acc_trans}{$trans_id}}) {
+ $amount = $form->round_amount($form->{acc_trans}{$trans_id}{$accno}{amount}, 2);
+ if ($amount) {
+ $query = qq|INSERT INTO acc_trans (trans_id, chart_id, amount,
+ transdate)
+ VALUES ($trans_id, (SELECT id FROM chart
+ WHERE accno = '$accno'),
+ $amount, '$form->{transdate}')|;
+ $dbh->do($query) || $form->dberror($query);
+ }
+ }
+ }
+
+
+ # if there is no amount but a payment record receivable
+ if ($invamount == 0) {
+ $form->{receivables} = 1;
+ }
+
+ my $cleared = 0;
+
+ # record payments and offsetting AR
+ for $i (1 .. $form->{paidaccounts}) {
+
+ if ($form->{"paid_$i"}) {
+ my ($accno) = split /--/, $form->{"AR_paid_$i"};
+ $form->{"datepaid_$i"} = $form->{transdate} unless ($form->{"datepaid_$i"});
+ $form->{datepaid} = $form->{"datepaid_$i"};
+
+ $exchangerate = 0;
+
+ if ($form->{currency} eq $form->{defaultcurrency}) {
+ $form->{"exchangerate_$i"} = 1;
+ } else {
+ $exchangerate = $form->check_exchangerate($myconfig, $form->{currency}, $form->{"datepaid_$i"}, 'buy');
+
+ $form->{"exchangerate_$i"} = ($exchangerate) ? $exchangerate : $form->parse_amount($myconfig, $form->{"exchangerate_$i"});
+ }
+
+
+ # record AR
+ $amount = $form->round_amount($form->{"paid_$i"} * $form->{exchangerate}, 2);
+
+ if ($form->{receivables}) {
+ $query = qq|INSERT INTO acc_trans (trans_id, chart_id, amount,
+ transdate)
+ VALUES ($form->{id}, (SELECT id FROM chart
+ WHERE accno = '$form->{AR}'),
+ $amount, '$form->{"datepaid_$i"}')|;
+ $dbh->do($query) || $form->dberror($query);
+ }
+
+ # record payment
+ $amount = $form->{"paid_$i"} * -1;
+ if ($keepcleared) {
+ $cleared = ($form->{"cleared_$i"}) ? 1 : 0;
+ }
+
+ $query = qq|INSERT INTO acc_trans (trans_id, chart_id, amount, transdate,
+ source, memo, cleared)
+ VALUES ($form->{id}, (SELECT id FROM chart
+ WHERE accno = '$accno'),
+ $amount, '$form->{"datepaid_$i"}', |
+ .$dbh->quote($form->{"source_$i"}).qq|, |
+ .$dbh->quote($form->{"memo_$i"}).qq|, '$cleared')|;
+ $dbh->do($query) || $form->dberror($query);
+
+ # exchangerate difference
+ $amount = $form->round_amount(($form->round_amount($form->{"paid_$i"} * $form->{"exchangerate_$i"} - $form->{"paid_$i"}, 2)) * -1, 2);
+
+ if ($amount) {
+ $query = qq|INSERT INTO acc_trans (trans_id, chart_id, amount,
+ transdate, source, fx_transaction, cleared)
+ VALUES ($form->{id}, (SELECT id FROM chart
+ WHERE accno = '$accno'),
+ $amount, '$form->{"datepaid_$i"}', |
+ .$dbh->quote($form->{"source_$i"}).qq|, '1', '$cleared')|;
+ $dbh->do($query) || $form->dberror($query);
+ }
+
+ # gain/loss
+ $amount = $form->round_amount(($form->round_amount($form->{"paid_$i"} * $form->{exchangerate},2) - $form->round_amount($form->{"paid_$i"} * $form->{"exchangerate_$i"},2)) * -1, 2);
+
+ if ($amount) {
+ my $accno_id = ($amount > 0) ? $fxgain_accno_id : $fxloss_accno_id;
+ $query = qq|INSERT INTO acc_trans (trans_id, chart_id, amount,
+ transdate, fx_transaction, cleared)
+ VALUES ($form->{id}, $accno_id,
+ $amount, '$form->{"datepaid_$i"}', '1', '$cleared')|;
+ $dbh->do($query) || $form->dberror($query);
+ }
+
+ # update exchange rate
+ if (($form->{currency} ne $form->{defaultcurrency}) && !$exchangerate) {
+ $form->update_exchangerate($dbh, $form->{currency}, $form->{"datepaid_$i"}, $form->{"exchangerate_$i"}, 0);
+ }
+ }
+ }
+
+ # set values which could be empty to 0
+ $form->{terms} *= 1;
+ $form->{taxincluded} *= 1;
+
+ # if this is from a till
+ my $till = ($form->{till}) ? qq|'$form->{till}'| : "NULL";
+
+ $form->{invnumber} = $form->update_defaults($myconfig, "sinumber", $dbh) unless $form->{invnumber};
+
+ # save AR record
+ $query = qq|UPDATE ar set
+ invnumber = |.$dbh->quote($form->{invnumber}).qq|,
+ ordnumber = |.$dbh->quote($form->{ordnumber}).qq|,
+ quonumber = |.$dbh->quote($form->{quonumber}).qq|,
+ transdate = '$form->{transdate}',
+ customer_id = $form->{customer_id},
+ amount = $invamount,
+ netamount = $invnetamount,
+ paid = $form->{paid},
+ datepaid = |.$form->dbquote($form->{datepaid}, SQL_DATE).qq|,
+ duedate = |.$form->dbquote($form->{duedate}, SQL_DATE).qq|,
+ invoice = '1',
+ shippingpoint = |.$dbh->quote($form->{shippingpoint}).qq|,
+ shipvia = |.$dbh->quote($form->{shipvia}).qq|,
+ terms = $form->{terms},
+ notes = |.$dbh->quote($form->{notes}).qq|,
+ intnotes = |.$dbh->quote($form->{intnotes}).qq|,
+ taxincluded = '$form->{taxincluded}',
+ curr = '$form->{currency}',
+ department_id = $form->{department_id},
+ employee_id = $form->{employee_id},
+ till = $till,
+ language_code = '$form->{language_code}',
+ ponumber = |.$dbh->quote($form->{ponumber}).qq|
+ WHERE id = $form->{id}
+ |;
+ $dbh->do($query) || $form->dberror($query);
+
+ # add shipto
+ $form->{name} = $form->{customer};
+ $form->{name} =~ s/--$form->{customer_id}//;
+ $form->add_shipto($dbh, $form->{id});
+
+ # save printed, emailed and queued
+ $form->save_status($dbh);
+
+ my %audittrail = ( tablename => 'ar',
+ reference => $form->{invnumber},
+ formname => $form->{type},
+ action => 'posted',
+ id => $form->{id} );
+
+ $form->audittrail($dbh, "", \%audittrail);
+
+ $form->save_recurring($dbh, $myconfig);
+
+ my $rc = $dbh->commit;
+
+ $dbh->disconnect;
+
+ $rc;
+
+}
+
+
+sub process_assembly {
+ my ($dbh, $form, $id, $totalqty, $project_id) = @_;
+
+ my $query = qq|SELECT a.parts_id, a.qty, p.assembly,
+ p.partnumber, p.description, p.unit,
+ p.inventory_accno_id, p.income_accno_id,
+ p.expense_accno_id
+ FROM assembly a
+ JOIN parts p ON (a.parts_id = p.id)
+ WHERE a.id = $id|;
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ my $allocated;
+
+ while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
+
+ $allocated = 0;
+
+ $ref->{inventory_accno_id} *= 1;
+ $ref->{expense_accno_id} *= 1;
+
+ # multiply by number of assemblies
+ $ref->{qty} *= $totalqty;
+
+ if ($ref->{assembly}) {
+ &process_assembly($dbh, $form, $ref->{parts_id}, $ref->{qty}, $project_id);
+ next;
+ } else {
+ if ($ref->{inventory_accno_id}) {
+ $allocated = &cogs($dbh, $form, $ref->{parts_id}, $ref->{qty}, $project_id);
+ }
+ }
+
+ # save detail record for individual assembly item in invoice table
+ $query = qq|INSERT INTO invoice (trans_id, description, parts_id, qty,
+ sellprice, fxsellprice, allocated, assemblyitem, unit)
+ VALUES
+ ($form->{id}, |
+ .$dbh->quote($ref->{description}).qq|,
+ $ref->{parts_id}, $ref->{qty}, 0, 0, $allocated, 't', |
+ .$dbh->quote($ref->{unit}).qq|)|;
+ $dbh->do($query) || $form->dberror($query);
+
+ }
+
+ $sth->finish;
+
+}
+
+
+sub cogs {
+ my ($dbh, $form, $id, $totalqty, $project_id) = @_;
+
+ my $query = qq|SELECT i.id, i.trans_id, i.qty, i.allocated, i.sellprice,
+ i.fxsellprice, p.inventory_accno_id, p.expense_accno_id
+ FROM invoice i, parts p
+ WHERE i.parts_id = p.id
+ AND i.parts_id = $id
+ AND (i.qty + i.allocated) < 0
+ ORDER BY trans_id|;
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ my $allocated = 0;
+ my $qty;
+
+ while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
+ if (($qty = (($ref->{qty} * -1) - $ref->{allocated})) > $totalqty) {
+ $qty = $totalqty;
+ }
+
+ $form->update_balance($dbh,
+ "invoice",
+ "allocated",
+ qq|id = $ref->{id}|,
+ $qty);
+
+ # total expenses and inventory
+ # sellprice is the cost of the item
+ my $linetotal = $form->round_amount($ref->{sellprice} * $qty, 2);
+
+ # add expense
+ push @{ $form->{acc_trans}{lineitems} }, {
+ chart_id => $ref->{expense_accno_id},
+ amount => $linetotal * -1,
+ project_id => $project_id,
+ invoice_id => $ref->{id} };
+
+ # deduct inventory
+ push @{ $form->{acc_trans}{lineitems} }, {
+ chart_id => $ref->{inventory_accno_id},
+ amount => $linetotal,
+ project_id => $project_id,
+ invoice_id => $ref->{id} };
+
+ # add allocated
+ $allocated += -$qty;
+
+ last if (($totalqty -= $qty) <= 0);
+ }
+
+ $sth->finish;
+
+ $allocated;
+
+}
+
+
+
+sub reverse_invoice {
+ my ($dbh, $form) = @_;
+
+ my $query = qq|SELECT id FROM ar
+ WHERE id = $form->{id}|;
+ my ($id) = $dbh->selectrow_array($query);
+
+ return unless $id;
+
+ # reverse inventory items
+ my $query = qq|SELECT i.id, i.parts_id, i.qty, i.assemblyitem, p.assembly,
+ p.inventory_accno_id
+ FROM invoice i
+ JOIN parts p ON (i.parts_id = p.id)
+ WHERE i.trans_id = $form->{id}|;
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
+
+ if ($ref->{inventory_accno_id} || $ref->{assembly}) {
+
+ # if the invoice item is not an assemblyitem adjust parts onhand
+ if (!$ref->{assemblyitem}) {
+ # adjust onhand in parts table
+ $form->update_balance($dbh,
+ "parts",
+ "onhand",
+ qq|id = $ref->{parts_id}|,
+ $ref->{qty});
+ }
+
+ # loop if it is an assembly
+ next if ($ref->{assembly});
+
+ # de-allocated purchases
+ $query = qq|SELECT id, trans_id, allocated
+ FROM invoice
+ WHERE parts_id = $ref->{parts_id}
+ AND allocated > 0
+ ORDER BY trans_id DESC|;
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while (my $inhref = $sth->fetchrow_hashref(NAME_lc)) {
+ $qty = $ref->{qty};
+ if (($ref->{qty} - $inhref->{allocated}) > 0) {
+ $qty = $inhref->{allocated};
+ }
+
+ # update invoice
+ $form->update_balance($dbh,
+ "invoice",
+ "allocated",
+ qq|id = $inhref->{id}|,
+ $qty * -1);
+
+ last if (($ref->{qty} -= $qty) <= 0);
+ }
+ $sth->finish;
+ }
+ }
+
+ $sth->finish;
+
+ # delete acc_trans
+ $query = qq|DELETE FROM acc_trans
+ WHERE trans_id = $form->{id}|;
+ $dbh->do($query) || $form->dberror($query);
+
+ # delete invoice entries
+ $query = qq|DELETE FROM invoice
+ WHERE trans_id = $form->{id}|;
+ $dbh->do($query) || $form->dberror($query);
+
+ $query = qq|DELETE FROM shipto
+ WHERE trans_id = $form->{id}|;
+ $dbh->do($query) || $form->dberror($query);
+
+ $dbh->commit;
+
+}
+
+
+
+sub delete_invoice {
+ my ($self, $myconfig, $form, $spool) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect_noauto($myconfig);
+
+ &reverse_invoice($dbh, $form);
+
+ my %audittrail = ( tablename => 'ar',
+ reference => $form->{invnumber},
+ formname => $form->{type},
+ action => 'deleted',
+ id => $form->{id} );
+
+ $form->audittrail($dbh, "", \%audittrail);
+
+ # delete AR record
+ my $query = qq|DELETE FROM ar
+ WHERE id = $form->{id}|;
+ $dbh->do($query) || $form->dberror($query);
+
+ # delete spool files
+ $query = qq|SELECT spoolfile FROM status
+ WHERE trans_id = $form->{id}
+ AND spoolfile IS NOT NULL|;
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ my $spoolfile;
+ my @spoolfiles = ();
+
+ while (($spoolfile) = $sth->fetchrow_array) {
+ push @spoolfiles, $spoolfile;
+ }
+ $sth->finish;
+
+ # delete status entries
+ $query = qq|DELETE FROM status
+ WHERE trans_id = $form->{id}|;
+ $dbh->do($query) || $form->dberror($query);
+
+ my $rc = $dbh->commit;
+
+ if ($rc) {
+ foreach $spoolfile (@spoolfiles) {
+ unlink "$spool/$spoolfile" if $spoolfile;
+ }
+ }
+
+ $dbh->disconnect;
+
+ $rc;
+
+}
+
+
+
+sub retrieve_invoice {
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect_noauto($myconfig);
+
+ my $query;
+
+ if ($form->{id}) {
+ # get default accounts and last invoice number
+ $query = qq|SELECT d.curr AS currencies
+ FROM defaults d|;
+ } else {
+ $query = qq|SELECT d.curr AS currencies, current_date AS transdate
+ FROM defaults d|;
+ }
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ my $ref = $sth->fetchrow_hashref(NAME_lc);
+ for (keys %$ref) { $form->{$_} = $ref->{$_} }
+ $sth->finish;
+
+
+ if ($form->{id}) {
+
+ # retrieve invoice
+ $query = qq|SELECT a.invnumber, a.ordnumber, a.quonumber,
+ a.transdate, a.paid,
+ a.shippingpoint, a.shipvia, a.terms, a.notes, a.intnotes,
+ a.duedate, a.taxincluded, a.curr AS currency,
+ a.employee_id, e.name AS employee, a.till, a.customer_id,
+ a.language_code, a.ponumber
+ FROM ar a
+ LEFT JOIN employee e ON (e.id = a.employee_id)
+ WHERE a.id = $form->{id}|;
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ $ref = $sth->fetchrow_hashref(NAME_lc);
+ for (keys %$ref) { $form->{$_} = $ref->{$_} }
+ $sth->finish;
+
+ # get shipto
+ $query = qq|SELECT * FROM shipto
+ WHERE trans_id = $form->{id}|;
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ $ref = $sth->fetchrow_hashref(NAME_lc);
+ for (keys %$ref) { $form->{$_} = $ref->{$_} }
+ $sth->finish;
+
+ # retrieve individual items
+ $query = qq|SELECT i.description, i.qty, i.fxsellprice, i.sellprice,
+ i.discount, i.parts_id AS id, i.unit, i.deliverydate,
+ i.project_id, pr.projectnumber, i.serialnumber, i.notes,
+ p.partnumber, p.assembly, p.bin,
+ pg.partsgroup, p.partsgroup_id, p.partnumber AS sku,
+ p.listprice, p.lastcost, p.weight, p.onhand,
+ p.inventory_accno_id, p.income_accno_id, p.expense_accno_id,
+ t.description AS partsgrouptranslation
+ FROM invoice i
+ JOIN parts p ON (i.parts_id = p.id)
+ LEFT JOIN project pr ON (i.project_id = pr.id)
+ LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
+ LEFT JOIN translation t ON (t.trans_id = p.partsgroup_id AND t.language_code = '$form->{language_code}')
+ WHERE i.trans_id = $form->{id}
+ AND NOT i.assemblyitem = '1'
+ ORDER BY i.id|;
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ # foreign currency
+ &exchangerate_defaults($dbh, $form);
+
+ # query for price matrix
+ my $pmh = &price_matrix_query($dbh, $form);
+
+ # taxes
+ $query = qq|SELECT c.accno
+ FROM chart c
+ JOIN partstax pt ON (pt.chart_id = c.id)
+ WHERE pt.parts_id = ?|;
+ my $tth = $dbh->prepare($query) || $form->dberror($query);
+
+ my $taxrate;
+ my $ptref;
+
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+
+ my ($dec) = ($ref->{fxsellprice} =~ /\.(\d+)/);
+ $dec = length $dec;
+ my $decimalplaces = ($dec > 2) ? $dec : 2;
+
+ $tth->execute($ref->{id});
+
+ $ref->{taxaccounts} = "";
+ $taxrate = 0;
+
+ while ($ptref = $tth->fetchrow_hashref(NAME_lc)) {
+ $ref->{taxaccounts} .= "$ptref->{accno} ";
+ $taxrate += $form->{"$ptref->{accno}_rate"};
+ }
+ $tth->finish;
+ chop $ref->{taxaccounts};
+
+ # price matrix
+ $ref->{sellprice} = ($ref->{fxsellprice} * $form->{$form->{currency}});
+ &price_matrix($pmh, $ref, $form->{transdate}, $decimalplaces, $form, $myconfig);
+ $ref->{sellprice} = $ref->{fxsellprice};
+
+ $ref->{partsgroup} = $ref->{partsgrouptranslation} if $ref->{partsgrouptranslation};
+
+ push @{ $form->{invoice_details} }, $ref;
+ }
+ $sth->finish;
+
+ }
+
+ my $rc = $dbh->commit;
+ $dbh->disconnect;
+
+ $rc;
+
+}
+
+
+sub retrieve_item {
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ my $i = $form->{rowcount};
+ my $null;
+ my $var;
+
+ my $where = "WHERE p.obsolete = '0' AND NOT p.income_accno_id IS NULL";
+
+ if ($form->{"partnumber_$i"} ne "") {
+ $var = $form->like(lc $form->{"partnumber_$i"});
+ $where .= " AND lower(p.partnumber) LIKE '$var'";
+ }
+ if ($form->{"description_$i"} ne "") {
+ $var = $form->like(lc $form->{"description_$i"});
+ if ($form->{language_code} ne "") {
+ $where .= " AND lower(t1.description) LIKE '$var'";
+ } else {
+ $where .= " AND lower(p.description) LIKE '$var'";
+ }
+ }
+
+ if ($form->{"partsgroup_$i"} ne "") {
+ ($null, $var) = split /--/, $form->{"partsgroup_$i"};
+ $var *= 1;
+ if ($var == 0) {
+ # search by partsgroup, this is for the POS
+ $where .= qq| AND pg.partsgroup = '$form->{"partsgroup_$i"}'|;
+ } else {
+ $where .= qq| AND p.partsgroup_id = $var|;
+ }
+ }
+
+ if ($form->{"description_$i"} ne "") {
+ $where .= " ORDER BY 3";
+ } else {
+ $where .= " ORDER BY 2";
+ }
+
+ my $query = qq|SELECT p.id, p.partnumber, p.description, p.sellprice,
+ p.listprice, p.lastcost,
+ p.unit, p.assembly, p.bin, p.onhand, p.notes,
+ p.inventory_accno_id, p.income_accno_id, p.expense_accno_id,
+ pg.partsgroup, p.partsgroup_id, p.partnumber AS sku,
+ p.weight,
+ t1.description AS translation,
+ t2.description AS grouptranslation
+ FROM parts p
+ LEFT JOIN partsgroup pg ON (pg.id = p.partsgroup_id)
+ LEFT JOIN translation t1 ON (t1.trans_id = p.id AND t1.language_code = '$form->{language_code}')
+ LEFT JOIN translation t2 ON (t2.trans_id = p.partsgroup_id AND t2.language_code = '$form->{language_code}')
+ $where|;
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ my $ref;
+ my $ptref;
+
+ # setup exchange rates
+ &exchangerate_defaults($dbh, $form);
+
+ # taxes
+ $query = qq|SELECT c.accno
+ FROM chart c
+ JOIN partstax pt ON (c.id = pt.chart_id)
+ WHERE pt.parts_id = ?|;
+ my $tth = $dbh->prepare($query) || $form->dberror($query);
+
+
+ # price matrix
+ my $pmh = &price_matrix_query($dbh, $form);
+
+ my $transdate = $form->datetonum($myconfig, $form->{transdate});
+
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+
+ my ($dec) = ($ref->{sellprice} =~ /\.(\d+)/);
+ $dec = length $dec;
+ my $decimalplaces = ($dec > 2) ? $dec : 2;
+
+ # get taxes for part
+ $tth->execute($ref->{id});
+
+ $ref->{taxaccounts} = "";
+ while ($ptref = $tth->fetchrow_hashref(NAME_lc)) {
+ $ref->{taxaccounts} .= "$ptref->{accno} ";
+ }
+ $tth->finish;
+ chop $ref->{taxaccounts};
+
+ # get matrix
+ &price_matrix($pmh, $ref, $transdate, $decimalplaces, $form, $myconfig);
+
+ $ref->{description} = $ref->{translation} if $ref->{translation};
+ $ref->{partsgroup} = $ref->{grouptranslation} if $ref->{grouptranslation};
+
+ push @{ $form->{item_list} }, $ref;
+
+ }
+
+ $sth->finish;
+ $dbh->disconnect;
+
+}
+
+
+sub price_matrix_query {
+ my ($dbh, $form) = @_;
+
+ my $query = qq|SELECT p.id AS parts_id, 0 AS customer_id, 0 AS pricegroup_id,
+ 0 AS pricebreak, p.sellprice, NULL AS validfrom, NULL AS validto,
+ '$form->{defaultcurrency}' AS curr, '' AS pricegroup
+ FROM parts p
+ WHERE p.id = ?
+
+ UNION
+
+ SELECT p.*, g.pricegroup
+ FROM partscustomer p
+ LEFT JOIN pricegroup g ON (g.id = p.pricegroup_id)
+ WHERE p.parts_id = ?
+ AND p.customer_id = $form->{customer_id}
+
+ UNION
+
+ SELECT p.*, g.pricegroup
+ FROM partscustomer p
+ LEFT JOIN pricegroup g ON (g.id = p.pricegroup_id)
+ JOIN customer c ON (c.pricegroup_id = g.id)
+ WHERE p.parts_id = ?
+ AND c.id = $form->{customer_id}
+
+ UNION
+
+ SELECT p.*, '' AS pricegroup
+ FROM partscustomer p
+ WHERE p.customer_id = 0
+ AND p.pricegroup_id = 0
+ AND p.parts_id = ?
+
+ ORDER BY customer_id DESC, pricegroup_id DESC, pricebreak
+
+ |;
+ my $sth = $dbh->prepare($query) || $form->dberror($query);
+
+ $sth;
+
+}
+
+
+sub price_matrix {
+ my ($pmh, $ref, $transdate, $decimalplaces, $form, $myconfig) = @_;
+
+ $pmh->execute($ref->{id}, $ref->{id}, $ref->{id}, $ref->{id});
+
+ $ref->{pricematrix} = "";
+
+ my $customerprice;
+ my $pricegroupprice;
+ my $sellprice;
+ my $baseprice;
+ my $mref;
+ my %p = ();
+ my $i = 0;
+
+ while ($mref = $pmh->fetchrow_hashref(NAME_lc)) {
+
+ # check date
+ if ($mref->{validfrom}) {
+ next if $transdate < $form->datetonum($myconfig, $mref->{validfrom});
+ }
+ if ($mref->{validto}) {
+ next if $transdate > $form->datetonum($myconfig, $mref->{validto});
+ }
+
+ # convert price
+ $sellprice = $form->round_amount($mref->{sellprice} * $form->{$mref->{curr}}, $decimalplaces);
+
+ $mref->{pricebreak} *= 1;
+
+ if ($mref->{customer_id}) {
+ $p{$mref->{pricebreak}} = $sellprice;
+ $customerprice = 1;
+ }
+
+ if ($mref->{pricegroup_id}) {
+ if (!$customerprice) {
+ $p{$mref->{pricebreak}} = $sellprice;
+ $pricegroupprice = 1;
+ }
+ }
+
+ if (!$customerprice && !$pricegroupprice) {
+ $p{$mref->{pricebreak}} = $sellprice;
+ }
+
+ if (($mref->{pricebreak} + $mref->{customer_id} + $mref->{pricegroup_id}) == 0) {
+ $baseprice = $sellprice;
+ }
+
+ $i++;
+
+ }
+ $pmh->finish;
+
+ if (! exists $p{0}) {
+ $p{0} = $baseprice;
+ }
+
+ if ($i > 1) {
+ $ref->{sellprice} = $p{0};
+ for (sort { $a <=> $b } keys %p) { $ref->{pricematrix} .= "${_}:$p{$_} " }
+ } else {
+ $ref->{sellprice} = $form->round_amount($p{0} * (1 - $form->{tradediscount}), $decimalplaces);
+ $ref->{pricematrix} = "0:$ref->{sellprice} " if $ref->{sellprice};
+ }
+ chop $ref->{pricematrix};
+
+}
+
+
+sub exchangerate_defaults {
+ my ($dbh, $form) = @_;
+
+ my $var;
+
+ # get default currencies
+ my $query = qq|SELECT substr(curr,1,3), curr FROM defaults|;
+ my $eth = $dbh->prepare($query) || $form->dberror($query);
+ $eth->execute;
+ ($form->{defaultcurrency}, $form->{currencies}) = $eth->fetchrow_array;
+ $eth->finish;
+
+ $query = qq|SELECT buy
+ FROM exchangerate
+ WHERE curr = ?
+ AND transdate = ?|;
+ my $eth1 = $dbh->prepare($query) || $form->dberror($query);
+
+ $query = qq~SELECT max(transdate || ' ' || buy || ' ' || curr)
+ FROM exchangerate
+ WHERE curr = ?~;
+ my $eth2 = $dbh->prepare($query) || $form->dberror($query);
+
+ # get exchange rates for transdate or max
+ foreach $var (split /:/, substr($form->{currencies},4)) {
+ $eth1->execute($var, $form->{transdate});
+ ($form->{$var}) = $eth1->fetchrow_array;
+ if (! $form->{$var} ) {
+ $eth2->execute($var);
+
+ ($form->{$var}) = $eth2->fetchrow_array;
+ ($null, $form->{$var}) = split / /, $form->{$var};
+ $form->{$var} = 1 unless $form->{$var};
+ $eth2->finish;
+ }
+ $eth1->finish;
+ }
+
+ $form->{$form->{currency}} = $form->{exchangerate} if $form->{exchangerate};
+ $form->{$form->{currency}} ||= 1;
+ $form->{$form->{defaultcurrency}} = 1;
+
+}
+
+
+1;
+
diff --git a/LedgerSMB/Inifile.pm b/LedgerSMB/Inifile.pm
new file mode 100755
index 00000000..0b5055df
--- /dev/null
+++ b/LedgerSMB/Inifile.pm
@@ -0,0 +1,74 @@
+#=====================================================================
+# LedgerSMB
+# Small Medium Business Accounting software
+#
+# See COPYRIGHT file for copyright information
+#======================================================================
+#
+# This file has NOT undergone whitespace cleanup.
+#
+#======================================================================
+#
+# routines to retrieve / manipulate win ini style files
+# ORDER is used to keep the elements in the order they appear in .ini
+#
+#=====================================================================
+
+package Inifile;
+
+
+sub new {
+ my ($type, $file) = @_;
+
+ warn "$type has no copy constructor! creating a new object." if ref($type);
+ $type = ref($type) || $type;
+ my $self = bless {}, $type;
+ $self->add_file($file) if defined $file;
+
+ return $self;
+}
+
+
+sub add_file {
+ my ($self, $file) = @_;
+
+ my $id = "";
+ my %menuorder = ();
+
+ for (@{$self->{ORDER}}) { $menuorder{$_} = 1 }
+
+ open FH, "$file" or Form->error("$file : $!");
+
+ while (<FH>) {
+ next if /^(#|;|\s)/;
+ last if /^\./;
+
+ chop;
+
+ # strip comments
+ s/\s*(#|;).*//g;
+
+ # remove any trailing whitespace
+ s/^\s*(.*?)\s*$/$1/;
+
+ if (/^\[/) {
+ s/(\[|\])//g;
+ $id = $_;
+ push @{$self->{ORDER}}, $_ if ! $menuorder{$_};
+ $menuorder{$_} = 1;
+ next;
+ }
+
+ # add key=value to $id
+ my ($key, $value) = split /=/, $_, 2;
+
+ $self->{$id}{$key} = $value;
+
+ }
+ close FH;
+
+}
+
+
+1;
+
diff --git a/LedgerSMB/JC.pm b/LedgerSMB/JC.pm
new file mode 100755
index 00000000..af1ffd40
--- /dev/null
+++ b/LedgerSMB/JC.pm
@@ -0,0 +1,582 @@
+#=====================================================================
+# LedgerSMB
+# Small Medium Business Accounting software
+#
+# See COPYRIGHT file for copyright information
+#======================================================================
+#
+# This file has NOT undergone whitespace cleanup.
+#
+#======================================================================
+#
+# Job Costing
+#
+#======================================================================
+
+
+package JC;
+
+use LedgerSMB::IS;
+
+sub get_jcitems {
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ my $query = qq|SELECT current_date FROM defaults|;
+ ($form->{transdate}) = $dbh->selectrow_array($query);
+
+ ($form->{employee}, $form->{employee_id}) = $form->get_employee($dbh);
+
+ my $dateformat = $myconfig->{dateformat};
+ $dateformat =~ s/yy/yyyy/;
+ $dateformat =~ s/yyyyyy/yyyy/;
+
+ if ($form->{id}) {
+ # retrieve timecard/storescard
+ $query = qq|SELECT j.*, to_char(j.checkedin, 'HH24:MI:SS') AS checkedina,
+ to_char(j.checkedout, 'HH24:MI:SS') AS checkedouta,
+ to_char(j.checkedin, '$dateformat') AS transdate,
+ e.name AS employee, p.partnumber,
+ pr.projectnumber, pr.description AS projectdescription,
+ pr.production, pr.completed, pr.parts_id AS project
+ FROM jcitems j
+ JOIN employee e ON (e.id = j.employee_id)
+ JOIN parts p ON (p.id = j.parts_id)
+ JOIN project pr ON (pr.id = j.project_id)
+ WHERE j.id = $form->{id}|;
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ $ref = $sth->fetchrow_hashref(NAME_lc);
+
+ for (keys %$ref) { $form->{$_} = $ref->{$_} }
+ $sth->finish;
+ $form->{project} = ($form->{project}) ? "job" : "project";
+ for (qw(checkedin checkedout)) {
+ $form->{$_} = $form->{"${_}a"};
+ delete $form->{"${_}a"};
+ }
+
+ $query = qq|SELECT s.printed, s.spoolfile, s.formname
+ FROM status s
+ WHERE s.formname = '$form->{type}'
+ AND s.trans_id = $form->{id}|;
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+ $form->{printed} .= "$ref->{formname} " if $ref->{printed};
+ $form->{queued} .= "$ref->{formname} $ref->{spoolfile} " if $ref->{spoolfile};
+ }
+ $sth->finish;
+ for (qw(printed queued)) { $form->{$_} =~ s/ +$//g }
+ }
+
+ JC->jcitems_links($myconfig, $form, $dbh);
+
+ # get language codes
+ $query = qq|SELECT *
+ FROM language
+ ORDER BY 2|;
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ $form->{all_language} = ();
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+ push @{ $form->{all_language} }, $ref;
+ }
+ $sth->finish;
+
+ $dbh->disconnect;
+
+}
+
+
+sub jcitems_links {
+ my ($self, $myconfig, $form, $dbh) = @_;
+
+ my $disconnect = 0;
+
+ if (! $dbh) {
+ $dbh = $form->dbconnect($myconfig);
+ $disconnect = 1;
+ }
+
+ my $query;
+
+ if ($form->{project_id}) {
+ $form->{orphaned} = 1;
+ $query = qq|SELECT parts_id
+ FROM project
+ WHERE id = $form->{project_id}|;
+ if ($dbh->selectrow_array($query)) {
+ $form->{project} = 'job';
+ $query = qq|SELECT id
+ FROM project
+ WHERE parts_id > 0
+ AND production > completed
+ AND id = $form->{project_id}|;
+ ($form->{orphaned}) = $dbh->selectrow_array($q);
+ } else {
+ $form->{project} = 'project';
+ }
+ }
+
+ JC->jcparts($myconfig, $form, $dbh);
+
+ $form->all_employees($myconfig, $dbh, $form->{transdate});
+
+ my $where;
+
+ if ($form->{transdate}) {
+ $where .= qq| AND (enddate IS NULL
+ OR enddate >= '$form->{transdate}')
+ AND (startdate <= '$form->{transdate}'
+ OR startdate IS NULL)|;
+ }
+
+ if ($form->{project} eq 'job') {
+ $query = qq|
+ SELECT pr.*
+ FROM project pr
+ WHERE pr.parts_id > 0
+ AND pr.production > pr.completed
+ $where|;
+ } elsif ($form->{project} eq 'project') {
+ $query = qq|
+ SELECT pr.*
+ FROM project pr
+ WHERE pr.parts_id IS NULL
+ $where|;
+ } else {
+ $query = qq|
+ SELECT pr.*
+ FROM project pr
+ WHERE 1=1
+ $where
+ EXCEPT
+ SELECT pr.*
+ FROM project pr
+ WHERE pr.parts_id > 0
+ AND pr.production = pr.completed|;
+ }
+
+ if ($form->{project_id}) {
+ $query .= qq|
+ UNION
+ SELECT *
+ FROM project
+ WHERE id = $form->{project_id}|;
+ }
+
+ $query .= qq|
+ ORDER BY projectnumber|;
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
+ push @{ $form->{all_project} }, $ref;
+ }
+ $sth->finish;
+
+ $dbh->disconnect if $disconnect;
+
+}
+
+
+sub jcparts {
+ my ($self, $myconfig, $form, $dbh) = @_;
+
+ my ($null, $project_id) = split /--/, $form->{projectnumber};
+ $project_id *= 1;
+
+ my $query = qq|SELECT customer_id
+ FROM project
+ WHERE id = $project_id|;
+ my ($customer_id) = $dbh->selectrow_array($query);
+ $customer_id *= 1;
+
+ my $where;
+
+ if ($form->{project} eq 'job') {
+ $where = " AND p.income_accno_id IS NULL";
+ if ($form->{type} eq 'storescard') {
+ $where = " AND p.inventory_accno_id > 0
+ AND p.income_accno_id > 0";
+ }
+
+ $query = qq|SELECT p.id, p.partnumber, p.description, p.sellprice,
+ p.unit, t.description AS translation
+ FROM parts p
+ LEFT JOIN translation t ON (t.trans_id = p.id AND t.language_code = '$form->{language_code}')
+ WHERE p.obsolete = '0'
+ $where|;
+ } elsif ($form->{project} eq 'project') {
+ $where = " AND p.inventory_accno_id IS NULL";
+ if ($form->{type} eq 'storescard') {
+ $where = " AND p.inventory_accno_id > 0";
+ }
+
+ $query = qq|SELECT p.id, p.partnumber, p.description, p.sellprice,
+ p.unit, t.description AS translation
+ FROM parts p
+ LEFT JOIN translation t ON (t.trans_id = p.id AND t.language_code = '$form->{language_code}')
+ WHERE p.obsolete = '0'
+ AND p.assembly = '0'
+ $where|;
+ } else {
+
+ $query = qq|SELECT p.id, p.partnumber, p.description, p.sellprice,
+ p.unit, t.description AS translation
+ FROM parts p
+ LEFT JOIN translation t ON (t.trans_id = p.id AND t.language_code = '$form->{language_code}')
+ WHERE p.obsolete = '0'
+ AND p.income_accno_id IS NULL
+ UNION
+ SELECT p.id, p.partnumber, p.description, p.sellprice,
+ p.unit, t.description AS translation
+ FROM parts p
+ LEFT JOIN translation t ON (t.trans_id = p.id AND t.language_code = '$form->{language_code}')
+ WHERE p.obsolete = '0'
+ AND p.assembly = '0'
+ AND p.inventory_accno_id IS NULL|;
+ }
+
+ $query .= qq|
+ ORDER BY 2|;
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ my $pmh = price_matrix_query($dbh, $project_id, $customer_id);
+ IS::exchangerate_defaults($dbh, $form);
+
+ while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
+ $ref->{description} = $ref->{translation} if $ref->{translation};
+ IS::price_matrix($pmh, $ref, $form->datetonum($form->{transdate}), 4, $form, $myconfig);
+ push @{ $form->{all_parts} }, $ref;
+ }
+ $sth->finish;
+
+}
+
+
+sub delete_timecard {
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect_noauto($myconfig);
+
+ my %audittrail = ( tablename => 'jcitems',
+ reference => $form->{id},
+ formname => $form->{type},
+ action => 'deleted',
+ id => $form->{id} );
+
+ $form->audittrail($dbh, "", \%audittrail);
+
+ my $query = qq|DELETE FROM jcitems
+ WHERE id = $form->{id}|;
+ $dbh->do($query) || $form->dberror($query);
+
+ # delete spool files
+ $query = qq|SELECT spoolfile FROM status
+ WHERE formname = '$form->{type}'
+ AND trans_id = $form->{id}
+ AND spoolfile IS NOT NULL|;
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ my $spoolfile;
+ my @spoolfiles = ();
+
+ while (($spoolfile) = $sth->fetchrow_array) {
+ push @spoolfiles, $spoolfile;
+ }
+ $sth->finish;
+
+ # delete status entries
+ $query = qq|DELETE FROM status
+ WHERE formname = '$form->{type}'
+ AND trans_id = $form->{id}|;
+ $dbh->do($query) || $form->dberror($query);
+
+ my $rc = $dbh->commit;
+
+ if ($rc) {
+ foreach $spoolfile (@spoolfiles) {
+ unlink "$spool/$spoolfile" if $spoolfile;
+ }
+ }
+
+ $dbh->disconnect;
+
+ $rc;
+
+}
+
+
+sub jcitems {
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ my $query;
+ my $where = "1 = 1";
+ my $null;
+ my $var;
+
+ if ($form->{projectnumber}) {
+ ($null, $var) = split /--/, $form->{projectnumber};
+ $where .= " AND j.project_id = $var";
+
+ $query = qq|SELECT parts_id
+ FROM project
+ WHERE id = $var|;
+ my ($job) = $dbh->selectrow_array($query);
+ $form->{project} = ($job) ? "job" : "project";
+
+ }
+ if ($form->{partnumber}) {
+ ($null, $var) = split /--/, $form->{partnumber};
+ $where .= " AND j.parts_id = $var";
+
+ $query = qq|SELECT inventory_accno_id
+ FROM parts
+ WHERE id = $var|;
+ my ($job) = $dbh->selectrow_array($query);
+ $form->{project} = ($job) ? "job" : "project";
+
+ }
+ if ($form->{employee}) {
+ ($null, $var) = split /--/, $form->{employee};
+ $where .= " AND j.employee_id = $var";
+ }
+ if ($form->{open} || $form->{closed}) {
+ unless ($form->{open} && $form->{closed}) {
+ $where .= " AND j.qty != j.allocated" if $form->{open};
+ $where .= " AND j.qty = j.allocated" if $form->{closed};
+ }
+ }
+
+ ($form->{startdatefrom}, $form->{startdateto}) = $form->from_to($form->{year}, $form->{month}, $form->{interval}) if $form->{year} && $form->{month};
+
+ $where .= " AND j.checkedin >= '$form->{startdatefrom}'" if $form->{startdatefrom};
+ $where .= " AND j.checkedout < date '$form->{startdateto}' + 1" if $form->{startdateto};
+
+ my %ordinal = ( id => 1,
+ description => 2,
+ transdate => 7,
+ partnumber => 9,
+ projectnumber => 10,
+ projectdescription => 11,
+ );
+
+ my @a = (transdate, projectnumber);
+ my $sortorder = $form->sort_order(\@a, \%ordinal);
+
+ my $dateformat = $myconfig->{dateformat};
+ $dateformat =~ s/yy$/yyyy/;
+ $dateformat =~ s/yyyyyy/yyyy/;
+
+ if ($form->{project} eq 'job') {
+ if ($form->{type} eq 'timecard') {
+ $where .= " AND pr.parts_id > 0
+ AND p.income_accno_id IS NULL";
+ }
+
+ if ($form->{type} eq 'storescard') {
+ $where .= " AND pr.parts_id > 0
+ AND p.income_accno_id > 0";
+ }
+ }
+ if ($form->{project} eq 'project') {
+ $where .= " AND pr.parts_id IS NULL";
+ }
+
+ $query = qq|SELECT j.id, j.description, j.qty, j.allocated,
+ to_char(j.checkedin, 'HH24:MI') AS checkedin,
+ to_char(j.checkedout, 'HH24:MI') AS checkedout,
+ to_char(j.checkedin, 'yyyymmdd') AS transdate,
+ to_char(j.checkedin, '$dateformat') AS transdatea,
+ to_char(j.checkedin, 'D') AS weekday,
+ p.partnumber,
+ pr.projectnumber, pr.description AS projectdescription,
+ e.employeenumber, e.name AS employee,
+ to_char(j.checkedin, 'WW') AS workweek, pr.parts_id,
+ j.sellprice
+ FROM jcitems j
+ JOIN parts p ON (p.id = j.parts_id)
+ JOIN project pr ON (pr.id = j.project_id)
+ JOIN employee e ON (e.id = j.employee_id)
+ WHERE $where
+ ORDER BY employee, employeenumber, $sortorder|;
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+ $ref->{project} = ($ref->{parts_id}) ? "job" : "project";
+ $ref->{transdate} = $ref->{transdatea};
+ delete $ref->{transdatea};
+ push @{ $form->{transactions} }, $ref;
+ }
+ $sth->finish;
+
+ $dbh->disconnect;
+
+}
+
+
+sub save {
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect_noauto($myconfig);
+
+ my $query;
+ my $sth;
+
+ my ($null, $project_id) = split /--/, $form->{projectnumber};
+
+ if ($form->{id}) {
+ # check if it was a job
+ $query = qq|SELECT pr.parts_id, pr.production - pr.completed
+ FROM project pr
+ JOIN jcitems j ON (j.project_id = pr.id)
+ WHERE j.id = $form->{id}|;
+ my ($job_id, $qty) = $dbh->selectrow_array($query);
+
+ if ($job_id && $qty == 0) {
+ $dbh->disconnect;
+ return -1;
+ }
+
+ # check if new one belongs to a job
+ if ($project_id) {
+ $query = qq|SELECT pr.parts_id, pr.production - pr.completed
+ FROM project pr
+ WHERE pr.id = $project_id|;
+ my ($job_id, $qty) = $dbh->selectrow_array($query);
+
+ if ($job_id && $qty == 0) {
+ $dbh->disconnect;
+ return -2;
+ }
+ }
+
+ } else {
+ my $uid = localtime;
+ $uid .= "$$";
+
+ $query = qq|INSERT INTO jcitems (description)
+ VALUES ('$uid')|;
+ $dbh->do($query) || $form->dberror($query);
+
+ $query = qq|SELECT id FROM jcitems
+ WHERE description = '$uid'|;
+ ($form->{id}) = $dbh->selectrow_array($query);
+ }
+
+ for (qw(inhour inmin insec outhour outmin outsec)) { $form->{$_} = substr("00$form->{$_}", -2) }
+ for (qw(qty sellprice allocated)) { $form->{$_} = $form->parse_amount($myconfig, $form->{$_}) }
+
+ my $checkedin = "$form->{inhour}$form->{inmin}$form->{insec}";
+ my $checkedout = "$form->{outhour}$form->{outmin}$form->{outsec}";
+
+ my $outdate = $form->{transdate};
+ if ($checkedout < $checkedin) {
+ $outdate = $form->add_date($myconfig, $form->{transdate}, 1, 'days');
+ }
+
+ ($null, $form->{employee_id}) = split /--/, $form->{employee};
+ unless ($form->{employee_id}) {
+ ($form->{employee}, $form->{employee_id}) = $form->get_employee($dbh);
+ }
+
+ my $parts_id;
+ ($null, $parts_id) = split /--/, $form->{partnumber};
+
+ $query = qq|UPDATE jcitems SET
+ project_id = $project_id,
+ parts_id = $parts_id,
+ description = |.$dbh->quote($form->{description}).qq|,
+ qty = $form->{qty},
+ allocated = $form->{allocated},
+ sellprice = $form->{sellprice},
+ fxsellprice = $form->{sellprice},
+ serialnumber = |.$dbh->quote($form->{serialnumber}).qq|,
+ checkedin = timestamp '$form->{transdate} $form->{inhour}:$form->{inmin}:$form->{insec}',
+ checkedout = timestamp '$outdate $form->{outhour}:$form->{outmin}:$form->{outsec}',
+ employee_id = $form->{employee_id},
+ notes = |.$dbh->quote($form->{notes}).qq|
+ WHERE id = $form->{id}|;
+ $dbh->do($query) || $form->dberror($query);
+
+ # save printed, queued
+ $form->save_status($dbh);
+
+ my %audittrail = ( tablename => 'jcitems',
+ reference => $form->{id},
+ formname => $form->{type},
+ action => 'saved',
+ id => $form->{id} );
+
+ $form->audittrail($dbh, "", \%audittrail);
+
+ my $rc = $dbh->commit;
+
+ $rc;
+
+}
+
+
+sub price_matrix_query {
+ my ($dbh, $project_id, $customer_id) = @_;
+
+ my $query = qq|SELECT p.id AS parts_id, 0 AS customer_id, 0 AS pricegroup_id,
+ 0 AS pricebreak, p.sellprice, NULL AS validfrom, NULL AS validto,
+ (SELECT substr(curr,1,3) FROM defaults) AS curr, '' AS pricegroup
+ FROM parts p
+ WHERE p.id = ?
+
+ UNION
+
+ SELECT p.*, g.pricegroup
+ FROM partscustomer p
+ LEFT JOIN pricegroup g ON (g.id = p.pricegroup_id)
+ WHERE p.parts_id = ?
+ AND p.customer_id = $customer_id
+
+ UNION
+
+ SELECT p.*, g.pricegroup
+ FROM partscustomer p
+ LEFT JOIN pricegroup g ON (g.id = p.pricegroup_id)
+ JOIN customer c ON (c.pricegroup_id = g.id)
+ WHERE p.parts_id = ?
+ AND c.id = $customer_id
+
+ UNION
+
+ SELECT p.*, '' AS pricegroup
+ FROM partscustomer p
+ WHERE p.customer_id = 0
+ AND p.pricegroup_id = 0
+ AND p.parts_id = ?
+
+ ORDER BY customer_id DESC, pricegroup_id DESC, pricebreak
+
+ |;
+ my $sth = $dbh->prepare($query) || $form->dberror($query);
+
+ $sth;
+
+}
+
+
+1;
+
diff --git a/LedgerSMB/Mailer.pm b/LedgerSMB/Mailer.pm
new file mode 100755
index 00000000..db44cff0
--- /dev/null
+++ b/LedgerSMB/Mailer.pm
@@ -0,0 +1,149 @@
+#=====================================================================
+# LedgerSMB
+# Small Medium Business Accounting software
+#
+# See COPYRIGHT file for copyright information
+#======================================================================
+#
+# This file has NOT undergone whitespace cleanup.
+#
+#======================================================================
+#
+# mailer package
+#
+#======================================================================
+
+package Mailer;
+
+sub new {
+ my ($type) = @_;
+ my $self = {};
+
+ bless $self, $type;
+}
+
+
+sub send {
+ my ($self, $out) = @_;
+
+ my $boundary = time;
+ $boundary = "LedgerSMB-$self->{version}-$boundary";
+ my $domain = $self->{from};
+ $domain =~ s/(.*?\@|>)//g;
+ my $msgid = "$boundary\@$domain";
+
+ $self->{charset} = "ISO-8859-1" unless $self->{charset};
+
+ if ($out) {
+ open(OUT, $out) or return "$out : $!";
+ } else {
+ open(OUT, ">-") or return "STDOUT : $!";
+ }
+
+ $self->{contenttype} = "text/plain" unless $self->{contenttype};
+
+ my %h;
+ for (qw(from to cc bcc)) {
+ $self->{$_} =~ s/\&lt;/</g;
+ $self->{$_} =~ s/\&gt;/>/g;
+ $self->{$_} =~ s/(\/|\\|\$)//g;
+ $h{$_} = $self->{$_};
+ }
+
+ $h{cc} = "Cc: $h{cc}\n" if $self->{cc};
+ $h{bcc} = "Bcc: $h{bcc}\n" if $self->{bcc};
+ $h{notify} = "Disposition-Notification-To: $h{from}\n" if $self->{notify};
+ $h{subject} = ($self->{subject} =~ /([\x00-\x1F]|[\x7B-\xFFFF])/) ? "Subject: =?$self->{charset}?B?".&encode_base64($self->{subject},"")."?=" : "Subject: $self->{subject}";
+
+ print OUT qq|From: $h{from}
+To: $h{to}
+$h{cc}$h{bcc}$h{subject}
+Message-ID: <$msgid>
+$h{notify}X-Mailer: LedgerSMB $self->{version}
+MIME-Version: 1.0
+|;
+
+
+ if (@{ $self->{attachments} }) {
+ print OUT qq|Content-Type: multipart/mixed; boundary="$boundary"
+
+|;
+ if ($self->{message} ne "") {
+ print OUT qq|--${boundary}
+Content-Type: $self->{contenttype}; charset="$self->{charset}"
+
+$self->{message}
+
+|;
+ }
+
+ foreach my $attachment (@{ $self->{attachments} }) {
+
+ my $application = ($attachment =~ /(^\w+$)|\.(html|text|txt|sql)$/) ? "text" : "application";
+
+ unless (open IN, $attachment) {
+ close(OUT);
+ return "$attachment : $!";
+ }
+
+ my $filename = $attachment;
+ # strip path
+ $filename =~ s/(.*\/|$self->{fileid})//g;
+
+ print OUT qq|--${boundary}
+Content-Type: $application/$self->{format}; name="$filename"; charset="$self->{charset}"
+Content-Transfer-Encoding: BASE64
+Content-Disposition: attachment; filename="$filename"\n\n|;
+
+ my $msg = "";
+ while (<IN>) {;
+ $msg .= $_;
+ }
+ print OUT &encode_base64($msg);
+
+ close(IN);
+
+ }
+ print OUT qq|--${boundary}--\n|;
+
+ } else {
+ print OUT qq|Content-Type: $self->{contenttype}; charset="$self->{charset}"
+
+$self->{message}
+|;
+ }
+
+ close(OUT);
+
+ return "";
+
+}
+
+
+sub encode_base64 ($;$) {
+
+ # this code is from the MIME-Base64-2.12 package
+ # Copyright 1995-1999,2001 Gisle Aas <gisle@ActiveState.com>
+
+ my $res = "";
+ my $eol = $_[1];
+ $eol = "\n" unless defined $eol;
+ pos($_[0]) = 0; # ensure start at the beginning
+
+ $res = join '', map( pack('u',$_)=~ /^.(\S*)/, ($_[0]=~/(.{1,45})/gs));
+
+ $res =~ tr|` -_|AA-Za-z0-9+/|; # `# help emacs
+ # fix padding at the end
+ my $padding = (3 - length($_[0]) % 3) % 3;
+ $res =~ s/.{$padding}$/'=' x $padding/e if $padding;
+ # break encoded string into lines of no more than 60 characters each
+ if (length $eol) {
+ $res =~ s/(.{1,60})/$1$eol/g;
+ }
+ return $res;
+
+}
+
+
+1;
+
diff --git a/LedgerSMB/Menu.pm b/LedgerSMB/Menu.pm
new file mode 100755
index 00000000..417f6cba
--- /dev/null
+++ b/LedgerSMB/Menu.pm
@@ -0,0 +1,91 @@
+#=====================================================================
+# LedgerSMB
+# Small Medium Business Accounting software
+#
+# See COPYRIGHT file for copyright information
+#======================================================================
+#
+# This file has NOT undergone whitespace cleanup.
+#
+#======================================================================
+#
+# routines for menu items
+#
+#=====================================================================
+
+package Menu;
+
+use LedgerSMB::Inifile;
+@ISA = qw/Inifile/;
+
+
+sub menuitem {
+ my ($self, $myconfig, $form, $item) = @_;
+
+ my $module = ($self->{$item}{module}) ? $self->{$item}{module} : $form->{script};
+ my $action = ($self->{$item}{action}) ? $self->{$item}{action} : "section_menu";
+ my $target = ($self->{$item}{target}) ? $self->{$item}{target} : "";
+
+ my $level = $form->escape($item);
+ my $str = qq|<a href="$module?path=$form->{path}&action=$action&level=$level&login=$form->{login}&timeout=$form->{timeout}&sessionid=$form->{sessionid}&js=$form->{js}|;
+
+ my @vars = qw(module action target href);
+
+ if ($self->{$item}{href}) {
+ $str = qq|<a href="$self->{$item}{href}|;
+ @vars = qw(module target href);
+ }
+
+ for (@vars) { delete $self->{$item}{$_} }
+
+ delete $self->{$item}{submenu};
+
+ # add other params
+ foreach my $key (keys %{ $self->{$item} }) {
+ $str .= "&".$form->escape($key)."=";
+ ($value, $conf) = split /=/, $self->{$item}{$key}, 2;
+ $value = "$myconfig->{$value}$conf" if $self->{$item}{$key} =~ /=/;
+
+ $str .= $form->escape($value);
+ }
+
+ $str .= qq|#id$form->{tag}| if $target eq 'acc_menu';
+
+ if ($target) {
+ $str .= qq|" target="$target"|;
+ }
+
+ $str .= qq|>|;
+
+}
+
+
+sub access_control {
+ my ($self, $myconfig, $menulevel) = @_;
+
+ my @menu = ();
+
+ if ($menulevel eq "") {
+ @menu = grep { !/--/ } @{ $self->{ORDER} };
+ } else {
+ @menu = grep { /^${menulevel}--/; } @{ $self->{ORDER} };
+ }
+
+ my @a = split /;/, $myconfig->{acs};
+ my $excl = ();
+
+ # remove --AR, --AP from array
+ grep { ($a, $b) = split /--/; s/--$a$//; } @a;
+
+ for (@a) { $excl{$_} = 1 }
+
+ @a = ();
+ for (@menu) { push @a, $_ unless $excl{$_} }
+
+ @a;
+
+}
+
+
+1;
+
diff --git a/LedgerSMB/Num2text.pm b/LedgerSMB/Num2text.pm
new file mode 100755
index 00000000..d8ecdef3
--- /dev/null
+++ b/LedgerSMB/Num2text.pm
@@ -0,0 +1,149 @@
+#=====================================================================
+# LedgerSMB
+# Small Medium Business Accounting software
+#
+# See COPYRIGHT file for copyright information
+#======================================================================
+#
+# This file has NOT undergone whitespace cleanup.
+#
+#======================================================================
+#
+# this is the default code for the Check package
+#
+#=====================================================================
+
+
+sub init {
+ my $self = shift;
+
+ %{ $self->{numbername} } =
+ (0 => 'Zero',
+ 1 => 'One',
+ 2 => 'Two',
+ 3 => 'Three',
+ 4 => 'Four',
+ 5 => 'Five',
+ 6 => 'Six',
+ 7 => 'Seven',
+ 8 => 'Eight',
+ 9 => 'Nine',
+ 10 => 'Ten',
+ 11 => 'Eleven',
+ 12 => 'Twelve',
+ 13 => 'Thirteen',
+ 14 => 'Fourteen',
+ 15 => 'Fifteen',
+ 16 => 'Sixteen',
+ 17 => 'Seventeen',
+ 18 => 'Eighteen',
+ 19 => 'Nineteen',
+ 20 => 'Twenty',
+ 30 => 'Thirty',
+ 40 => 'Forty',
+ 50 => 'Fifty',
+ 60 => 'Sixty',
+ 70 => 'Seventy',
+ 80 => 'Eighty',
+ 90 => 'Ninety',
+ 10**2 => 'Hundred',
+ 10**3 => 'Thousand',
+ 10**6 => 'Million',
+ 10**9 => 'Billion',
+ 10**12 => 'Trillion',
+ );
+
+}
+
+
+sub num2text {
+ my ($self, $amount) = @_;
+
+ return $self->{numbername}{0} unless $amount;
+
+ my @textnumber = ();
+
+ # split amount into chunks of 3
+ my @num = reverse split //, abs($amount);
+ my @numblock = ();
+ my @a;
+ my $i;
+
+ while (@num) {
+ @a = ();
+ for (1 .. 3) {
+ push @a, shift @num;
+ }
+ push @numblock, join / /, reverse @a;
+ }
+
+ while (@numblock) {
+
+ $i = $#numblock;
+ @num = split //, $numblock[$i];
+
+ if ($numblock[$i] == 0) {
+ pop @numblock;
+ next;
+ }
+
+ if ($numblock[$i] > 99) {
+ # the one from hundreds
+ push @textnumber, $self->{numbername}{$num[0]};
+
+ # add hundred designation
+ push @textnumber, $self->{numbername}{10**2};
+
+ # reduce numblock
+ $numblock[$i] -= $num[0] * 100;
+
+ }
+
+ $numblock[$i] *= 1;
+
+ if ($numblock[$i] > 9) {
+ # tens
+ push @textnumber, $self->format_ten($numblock[$i]);
+ } elsif ($numblock[$i] > 0) {
+ # ones
+ push @textnumber, $self->{numbername}{$numblock[$i]};
+ }
+
+ # add thousand, million
+ if ($i) {
+ $num = 10**($i * 3);
+ push @textnumber, $self->{numbername}{$num};
+ }
+
+ pop @numblock;
+
+ }
+
+ join ' ', @textnumber;
+
+}
+
+
+sub format_ten {
+ my ($self, $amount) = @_;
+
+ my $textnumber = "";
+ my @num = split //, $amount;
+
+ if ($amount > 20) {
+ $textnumber = $self->{numbername}{$num[0]*10};
+ $amount = $num[1];
+ } else {
+ $textnumber = $self->{numbername}{$amount};
+ $amount = 0;
+ }
+
+ $textnumber .= " ".$self->{numbername}{$amount} if $amount;
+
+ $textnumber;
+
+}
+
+
+1;
+
diff --git a/LedgerSMB/OE.pm b/LedgerSMB/OE.pm
new file mode 100755
index 00000000..25bcecd3
--- /dev/null
+++ b/LedgerSMB/OE.pm
@@ -0,0 +1,2238 @@
+#=====================================================================
+# LedgerSMB
+# Small Medium Business Accounting software
+#
+# See COPYRIGHT file for copyright information
+#======================================================================
+#
+# This file has NOT undergone whitespace cleanup.
+#
+#======================================================================
+#
+# Order entry module
+# Quotation
+#
+#======================================================================
+
+package OE;
+
+
+sub transactions {
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ my $query;
+ my $null;
+ my $var;
+ my $ordnumber = 'ordnumber';
+ my $quotation = '0';
+ my $department;
+
+ my $rate = ($form->{vc} eq 'customer') ? 'buy' : 'sell';
+
+ ($form->{transdatefrom}, $form->{transdateto}) = $form->from_to($form->{year}, $form->{month}, $form->{interval}) if $form->{year} && $form->{month};
+
+ if ($form->{type} =~ /_quotation$/) {
+ $quotation = '1';
+ $ordnumber = 'quonumber';
+ }
+
+ my $number = $form->like(lc $form->{$ordnumber});
+ my $name = $form->like(lc $form->{$form->{vc}});
+
+ for (qw(department employee)) {
+ if ($form->{$_}) {
+ ($null, $var) = split /--/, $form->{$_};
+ $department .= " AND o.${_}_id = $var";
+ }
+ }
+
+ my $query = qq|SELECT o.id, o.ordnumber, o.transdate, o.reqdate,
+ o.amount, ct.name, o.netamount, o.$form->{vc}_id,
+ ex.$rate AS exchangerate,
+ o.closed, o.quonumber, o.shippingpoint, o.shipvia,
+ e.name AS employee, m.name AS manager, o.curr, o.ponumber
+ FROM oe o
+ JOIN $form->{vc} ct ON (o.$form->{vc}_id = ct.id)
+ LEFT JOIN employee e ON (o.employee_id = e.id)
+ LEFT JOIN employee m ON (e.managerid = m.id)
+ LEFT JOIN exchangerate ex ON (ex.curr = o.curr
+ AND ex.transdate = o.transdate)
+ WHERE o.quotation = '$quotation'
+ $department|;
+
+ my %ordinal = ( id => 1,
+ ordnumber => 2,
+ transdate => 3,
+ reqdate => 4,
+ name => 6,
+ quonumber => 11,
+ shipvia => 13,
+ employee => 14,
+ manager => 15,
+ curr => 16,
+ ponumber => 17
+ );
+
+ my @a = (transdate, $ordnumber, name);
+ push @a, "employee" if $form->{l_employee};
+ if ($form->{type} !~ /(ship|receive)_order/) {
+ push @a, "manager" if $form->{l_manager};
+ }
+ my $sortorder = $form->sort_order(\@a, \%ordinal);
+
+
+ # build query if type eq (ship|receive)_order
+ if ($form->{type} =~ /(ship|receive)_order/) {
+
+ my ($warehouse, $warehouse_id) = split /--/, $form->{warehouse};
+
+ $query = qq|SELECT DISTINCT o.id, o.ordnumber, o.transdate,
+ o.reqdate, o.amount, ct.name, o.netamount, o.$form->{vc}_id,
+ ex.$rate AS exchangerate,
+ o.closed, o.quonumber, o.shippingpoint, o.shipvia,
+ e.name AS employee, o.curr, o.ponumber
+ FROM oe o
+ JOIN $form->{vc} ct ON (o.$form->{vc}_id = ct.id)
+ JOIN orderitems oi ON (oi.trans_id = o.id)
+ JOIN parts p ON (p.id = oi.parts_id)|;
+
+ if ($warehouse_id && $form->{type} eq 'ship_order') {
+ $query .= qq|
+ JOIN inventory i ON (oi.parts_id = i.parts_id)
+ |;
+ }
+
+ $query .= qq|
+ LEFT JOIN employee e ON (o.employee_id = e.id)
+ LEFT JOIN exchangerate ex ON (ex.curr = o.curr
+ AND ex.transdate = o.transdate)
+ WHERE o.quotation = '0'
+ AND (p.inventory_accno_id > 0 OR p.assembly = '1')
+ AND oi.qty != oi.ship
+ $department|;
+
+ if ($warehouse_id && $form->{type} eq 'ship_order') {
+ $query .= qq|
+ AND i.warehouse_id = $warehouse_id
+ AND ( SELECT SUM(i.qty)
+ FROM inventory i
+ WHERE oi.parts_id = i.parts_id
+ AND i.warehouse_id = $warehouse_id ) > 0
+ |;
+ }
+
+ }
+
+ if ($form->{"$form->{vc}_id"}) {
+ $query .= qq| AND o.$form->{vc}_id = $form->{"$form->{vc}_id"}|;
+ } else {
+ if ($form->{$form->{vc}} ne "") {
+ $query .= " AND lower(ct.name) LIKE '$name'";
+ }
+ }
+
+ if ($form->{$ordnumber} ne "") {
+ $query .= " AND lower($ordnumber) LIKE '$number'";
+ $form->{open} = 1;
+ $form->{closed} = 1;
+ }
+ if ($form->{ponumber} ne "") {
+ $query .= " AND lower(ponumber) LIKE '$form->{ponumber}'";
+ }
+
+ if (!$form->{open} && !$form->{closed}) {
+ $query .= " AND o.id = 0";
+ } elsif (!($form->{open} && $form->{closed})) {
+ $query .= ($form->{open}) ? " AND o.closed = '0'" : " AND o.closed = '1'";
+ }
+
+ if ($form->{shipvia} ne "") {
+ $var = $form->like(lc $form->{shipvia});
+ $query .= " AND lower(o.shipvia) LIKE '$var'";
+ }
+ if ($form->{description} ne "") {
+ $var = $form->like(lc $form->{description});
+ $query .= " AND o.id IN (SELECT DISTINCT trans_id
+ FROM orderitems
+ WHERE lower(description) LIKE '$var')";
+ }
+
+ if ($form->{transdatefrom}) {
+ $query .= " AND o.transdate >= '$form->{transdatefrom}'";
+ }
+ if ($form->{transdateto}) {
+ $query .= " AND o.transdate <= '$form->{transdateto}'";
+ }
+
+ $query .= " ORDER by $sortorder";
+
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ my %oid = ();
+ while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
+ $ref->{exchangerate} = 1 unless $ref->{exchangerate};
+ if ($ref->{id} != $oid{id}{$ref->{id}}) {
+ push @{ $form->{OE} }, $ref;
+ $oid{vc}{$ref->{curr}}{$ref->{"$form->{vc}_id"}}++;
+ }
+ $oid{id}{$ref->{id}} = $ref->{id};
+ }
+ $sth->finish;
+
+ $dbh->disconnect;
+
+ if ($form->{type} =~ /^consolidate_/) {
+ @a = ();
+ foreach $ref (@{ $form->{OE} }) { push @a, $ref if $oid{vc}{$ref->{curr}}{$ref->{"$form->{vc}_id"}} > 1 }
+
+ @{ $form->{OE} } = @a;
+ }
+
+}
+
+
+
+sub save {
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database, turn off autocommit
+ my $dbh = $form->dbconnect_noauto($myconfig);
+
+ my $query;
+ my $sth;
+ my $null;
+ my $exchangerate = 0;
+
+ ($null, $form->{employee_id}) = split /--/, $form->{employee};
+ if (! $form->{employee_id}) {
+ ($form->{employee}, $form->{employee_id}) = $form->get_employee($dbh);
+ $form->{employee} = "$form->{employee}--$form->{employee_id}";
+ }
+
+ my $ml = ($form->{type} eq 'sales_order') ? 1 : -1;
+
+ $query = qq|SELECT p.assembly, p.project_id
+ FROM parts p
+ WHERE p.id = ?|;
+ my $pth = $dbh->prepare($query) || $form->dberror($query);
+
+
+ if ($form->{id}) {
+ $query = qq|SELECT id FROM oe
+ WHERE id = $form->{id}|;
+
+ if ($dbh->selectrow_array($query)) {
+ &adj_onhand($dbh, $form, $ml) if $form->{type} =~ /_order$/;
+
+ $query = qq|DELETE FROM orderitems
+ WHERE trans_id = $form->{id}|;
+ $dbh->do($query) || $form->dberror($query);
+
+ $query = qq|DELETE FROM shipto
+ WHERE trans_id = $form->{id}|;
+ $dbh->do($query) || $form->dberror($query);
+
+ } else {
+ $query = qq|INSERT INTO oe (id)
+ VALUES ($form->{id})|;
+ $dbh->do($query) || $form->dberror($query);
+ }
+ }
+
+ if (! $form->{id}) {
+
+ my $uid = localtime;
+ $uid .= "$$";
+
+ $query = qq|INSERT INTO oe (ordnumber, employee_id)
+ VALUES ('$uid', $form->{employee_id})|;
+ $dbh->do($query) || $form->dberror($query);
+
+ $query = qq|SELECT id FROM oe
+ WHERE ordnumber = '$uid'|;
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+ ($form->{id}) = $sth->fetchrow_array;
+ $sth->finish;
+
+ }
+
+ my $amount;
+ my $linetotal;
+ my $discount;
+ my $project_id;
+ my $taxrate;
+ my $taxamount;
+ my $fxsellprice;
+ my %taxbase;
+ my @taxaccounts;
+ my %taxaccounts;
+ my $netamount = 0;
+
+
+ for my $i (1 .. $form->{rowcount}) {
+
+ for (qw(qty ship)) { $form->{"${_}_$i"} = $form->parse_amount($myconfig, $form->{"${_}_$i"}) }
+
+ $form->{"discount_$i"} = $form->parse_amount($myconfig, $form->{"discount_$i"}) / 100;
+ $form->{"sellprice_$i"} = $form->parse_amount($myconfig, $form->{"sellprice_$i"});
+
+ if ($form->{"qty_$i"}) {
+
+ $pth->execute($form->{"id_$i"});
+ $ref = $pth->fetchrow_hashref(NAME_lc);
+ for (keys %$ref) { $form->{"${_}_$i"} = $ref->{$_} }
+ $pth->finish;
+
+ $fxsellprice = $form->{"sellprice_$i"};
+
+ my ($dec) = ($form->{"sellprice_$i"} =~ /\.(\d+)/);
+ $dec = length $dec;
+ my $decimalplaces = ($dec > 2) ? $dec : 2;
+
+ $discount = $form->round_amount($form->{"sellprice_$i"} * $form->{"discount_$i"}, $decimalplaces);
+ $form->{"sellprice_$i"} = $form->round_amount($form->{"sellprice_$i"} - $discount, $decimalplaces);
+
+ $linetotal = $form->round_amount($form->{"sellprice_$i"} * $form->{"qty_$i"}, 2);
+
+ @taxaccounts = split / /, $form->{"taxaccounts_$i"};
+ $taxrate = 0;
+ $taxdiff = 0;
+
+ for (@taxaccounts) { $taxrate += $form->{"${_}_rate"} }
+
+ if ($form->{taxincluded}) {
+ $taxamount = $linetotal * $taxrate / (1 + $taxrate);
+ $taxbase = $linetotal - $taxamount;
+ # we are not keeping a natural price, do not round
+ $form->{"sellprice_$i"} = $form->{"sellprice_$i"} * (1 / (1 + $taxrate));
+ } else {
+ $taxamount = $linetotal * $taxrate;
+ $taxbase = $linetotal;
+ }
+
+ if (@taxaccounts && $form->round_amount($taxamount, 2) == 0) {
+ if ($form->{taxincluded}) {
+ foreach $item (@taxaccounts) {
+ $taxamount = $form->round_amount($linetotal * $form->{"${item}_rate"} / (1 + abs($form->{"${item}_rate"})), 2);
+
+ $taxaccounts{$item} += $taxamount;
+ $taxdiff += $taxamount;
+
+ $taxbase{$item} += $taxbase;
+ }
+ $taxaccounts{$taxaccounts[0]} += $taxdiff;
+ } else {
+ foreach $item (@taxaccounts) {
+ $taxaccounts{$item} += $linetotal * $form->{"${item}_rate"};
+ $taxbase{$item} += $taxbase;
+ }
+ }
+ } else {
+ foreach $item (@taxaccounts) {
+ $taxaccounts{$item} += $taxamount * $form->{"${item}_rate"} / $taxrate;
+ $taxbase{$item} += $taxbase;
+ }
+ }
+
+
+ $netamount += $form->{"sellprice_$i"} * $form->{"qty_$i"};
+
+ $project_id = 'NULL';
+ if ($form->{"projectnumber_$i"} ne "") {
+ ($null, $project_id) = split /--/, $form->{"projectnumber_$i"};
+ }
+ $project_id = $form->{"project_id_$i"} if $form->{"project_id_$i"};
+
+ # save detail record in orderitems table
+ $query = qq|INSERT INTO orderitems (|;
+ $query .= "id, " if $form->{"orderitems_id_$i"};
+ $query .= qq|trans_id, parts_id, description, qty, sellprice, discount,
+ unit, reqdate, project_id, ship, serialnumber, notes)
+ VALUES (|;
+ $query .= qq|$form->{"orderitems_id_$i"},| if $form->{"orderitems_id_$i"};
+ $query .= qq|$form->{id}, $form->{"id_$i"}, |
+ .$dbh->quote($form->{"description_$i"}).qq|,
+ $form->{"qty_$i"}, $fxsellprice, $form->{"discount_$i"}, |
+ .$dbh->quote($form->{"unit_$i"}).qq|, |
+ .$form->dbquote($form->{"reqdate_$i"}, SQL_DATE).qq|,
+ $project_id, $form->{"ship_$i"}, |
+ .$dbh->quote($form->{"serialnumber_$i"}).qq|, |
+ .$dbh->quote($form->{"notes_$i"}).qq|)|;
+ $dbh->do($query) || $form->dberror($query);
+
+ $form->{"sellprice_$i"} = $fxsellprice;
+ }
+ $form->{"discount_$i"} *= 100;
+ }
+
+
+ # set values which could be empty
+ for (qw(vendor_id customer_id taxincluded closed quotation)) { $form->{$_} *= 1 }
+
+ # add up the tax
+ my $tax = 0;
+ for (keys %taxaccounts) { $tax += $taxaccounts{$_} }
+
+ $amount = $form->round_amount($netamount + $tax, 2);
+ $netamount = $form->round_amount($netamount, 2);
+
+ if ($form->{currency} eq $form->{defaultcurrency}) {
+ $form->{exchangerate} = 1;
+ } else {
+ $exchangerate = $form->check_exchangerate($myconfig, $form->{currency}, $form->{transdate}, ($form->{vc} eq 'customer') ? 'buy' : 'sell');
+ }
+
+ $form->{exchangerate} = ($exchangerate) ? $exchangerate : $form->parse_amount($myconfig, $form->{exchangerate});
+
+ my $quotation;
+ my $ordnumber;
+ my $numberfld;
+ if ($form->{type} =~ /_order$/) {
+ $quotation = "0";
+ $ordnumber = "ordnumber";
+ $numberfld = ($form->{vc} eq 'customer') ? "sonumber" : "ponumber";
+ } else {
+ $quotation = "1";
+ $ordnumber = "quonumber";
+ $numberfld = ($form->{vc} eq 'customer') ? "sqnumber" : "rfqnumber";
+ }
+
+ $form->{$ordnumber} = $form->update_defaults($myconfig, $numberfld, $dbh) unless $form->{$ordnumber};
+
+ ($null, $form->{department_id}) = split(/--/, $form->{department});
+ for (qw(department_id terms)) { $form->{$_} *= 1 }
+
+ # save OE record
+ $query = qq|UPDATE oe set
+ ordnumber = |.$dbh->quote($form->{ordnumber}).qq|,
+ quonumber = |.$dbh->quote($form->{quonumber}).qq|,
+ transdate = '$form->{transdate}',
+ vendor_id = $form->{vendor_id},
+ customer_id = $form->{customer_id},
+ amount = $amount,
+ netamount = $netamount,
+ reqdate = |.$form->dbquote($form->{reqdate}, SQL_DATE).qq|,
+ taxincluded = '$form->{taxincluded}',
+ shippingpoint = |.$dbh->quote($form->{shippingpoint}).qq|,
+ shipvia = |.$dbh->quote($form->{shipvia}).qq|,
+ notes = |.$dbh->quote($form->{notes}).qq|,
+ intnotes = |.$dbh->quote($form->{intnotes}).qq|,
+ curr = '$form->{currency}',
+ closed = '$form->{closed}',
+ quotation = '$quotation',
+ department_id = $form->{department_id},
+ employee_id = $form->{employee_id},
+ language_code = '$form->{language_code}',
+ ponumber = |.$dbh->quote($form->{ponumber}).qq|,
+ terms = $form->{terms}
+ WHERE id = $form->{id}|;
+ $dbh->do($query) || $form->dberror($query);
+
+ $form->{ordtotal} = $amount;
+
+ # add shipto
+ $form->{name} = $form->{$form->{vc}};
+ $form->{name} =~ s/--$form->{"$form->{vc}_id"}//;
+ $form->add_shipto($dbh, $form->{id});
+
+ # save printed, emailed, queued
+ $form->save_status($dbh);
+
+ if (($form->{currency} ne $form->{defaultcurrency}) && !$exchangerate) {
+ if ($form->{vc} eq 'customer') {
+ $form->update_exchangerate($dbh, $form->{currency}, $form->{transdate}, $form->{exchangerate}, 0);
+ }
+ if ($form->{vc} eq 'vendor') {
+ $form->update_exchangerate($dbh, $form->{currency}, $form->{transdate}, 0, $form->{exchangerate});
+ }
+ }
+
+
+ if ($form->{type} =~ /_order$/) {
+ # adjust onhand
+ &adj_onhand($dbh, $form, $ml * -1);
+ &adj_inventory($dbh, $myconfig, $form);
+ }
+
+ my %audittrail = ( tablename => 'oe',
+ reference => ($form->{type} =~ /_order$/) ? $form->{ordnumber} : $form->{quonumber},
+ formname => $form->{type},
+ action => 'saved',
+ id => $form->{id} );
+
+ $form->audittrail($dbh, "", \%audittrail);
+
+ $form->save_recurring($dbh, $myconfig);
+
+ my $rc = $dbh->commit;
+ $dbh->disconnect;
+
+ $rc;
+
+}
+
+
+
+sub delete {
+ my ($self, $myconfig, $form, $spool) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect_noauto($myconfig);
+
+ # delete spool files
+ my $query = qq|SELECT spoolfile FROM status
+ WHERE trans_id = $form->{id}
+ AND spoolfile IS NOT NULL|;
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ my $spoolfile;
+ my @spoolfiles = ();
+
+ while (($spoolfile) = $sth->fetchrow_array) {
+ push @spoolfiles, $spoolfile;
+ }
+ $sth->finish;
+
+
+ $query = qq|SELECT o.parts_id, o.ship, p.inventory_accno_id, p.assembly
+ FROM orderitems o
+ JOIN parts p ON (p.id = o.parts_id)
+ WHERE trans_id = $form->{id}|;
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ if ($form->{type} =~ /_order$/) {
+ $ml = ($form->{type} eq 'purchase_order') ? -1 : 1;
+ while (my ($id, $ship, $inv, $assembly) = $sth->fetchrow_array) {
+ $form->update_balance($dbh,
+ "parts",
+ "onhand",
+ qq|id = $id|,
+ $ship * $ml) if ($inv || $assembly);
+ }
+ }
+ $sth->finish;
+
+ # delete inventory
+ $query = qq|DELETE FROM inventory
+ WHERE trans_id = $form->{id}|;
+ $dbh->do($query) || $form->dberror($query);
+
+ # delete status entries
+ $query = qq|DELETE FROM status
+ WHERE trans_id = $form->{id}|;
+ $dbh->do($query) || $form->dberror($query);
+
+ # delete OE record
+ $query = qq|DELETE FROM oe
+ WHERE id = $form->{id}|;
+ $dbh->do($query) || $form->dberror($query);
+
+ # delete individual entries
+ $query = qq|DELETE FROM orderitems
+ WHERE trans_id = $form->{id}|;
+ $dbh->do($query) || $form->dberror($query);
+
+ $query = qq|DELETE FROM shipto
+ WHERE trans_id = $form->{id}|;
+ $dbh->do($query) || $form->dberror($query);
+
+ my %audittrail = ( tablename => 'oe',
+ reference => ($form->{type} =~ /_order$/) ? $form->{ordnumber} : $form->{quonumber},
+ formname => $form->{type},
+ action => 'deleted',
+ id => $form->{id} );
+
+ $form->audittrail($dbh, "", \%audittrail);
+
+ my $rc = $dbh->commit;
+ $dbh->disconnect;
+
+ if ($rc) {
+ foreach $spoolfile (@spoolfiles) {
+ unlink "$spool/$spoolfile" if $spoolfile;
+ }
+ }
+
+ $rc;
+
+}
+
+
+
+sub retrieve {
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ my $query;
+ my $sth;
+ my $var;
+ my $ref;
+
+ $query = qq|SELECT curr, current_date
+ FROM defaults|;
+ ($form->{currencies}, $form->{transdate}) = $dbh->selectrow_array($query);
+
+ if ($form->{id}) {
+
+ # retrieve order
+ $query = qq|SELECT o.ordnumber, o.transdate, o.reqdate, o.terms,
+ o.taxincluded, o.shippingpoint, o.shipvia, o.notes, o.intnotes,
+ o.curr AS currency, e.name AS employee, o.employee_id,
+ o.$form->{vc}_id, vc.name AS $form->{vc}, o.amount AS invtotal,
+ o.closed, o.reqdate, o.quonumber, o.department_id,
+ d.description AS department, o.language_code, o.ponumber
+ FROM oe o
+ JOIN $form->{vc} vc ON (o.$form->{vc}_id = vc.id)
+ LEFT JOIN employee e ON (o.employee_id = e.id)
+ LEFT JOIN department d ON (o.department_id = d.id)
+ WHERE o.id = $form->{id}|;
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ $ref = $sth->fetchrow_hashref(NAME_lc);
+ for (keys %$ref) { $form->{$_} = $ref->{$_} }
+ $sth->finish;
+
+ $query = qq|SELECT * FROM shipto
+ WHERE trans_id = $form->{id}|;
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ $ref = $sth->fetchrow_hashref(NAME_lc);
+ for (keys %$ref) { $form->{$_} = $ref->{$_} }
+ $sth->finish;
+
+ # get printed, emailed and queued
+ $query = qq|SELECT s.printed, s.emailed, s.spoolfile, s.formname
+ FROM status s
+ WHERE s.trans_id = $form->{id}|;
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+ $form->{printed} .= "$ref->{formname} " if $ref->{printed};
+ $form->{emailed} .= "$ref->{formname} " if $ref->{emailed};
+ $form->{queued} .= "$ref->{formname} $ref->{spoolfile} " if $ref->{spoolfile};
+ }
+ $sth->finish;
+ for (qw(printed emailed queued)) { $form->{$_} =~ s/ +$//g }
+
+ my %oid = ( 'Pg' => 'oid',
+ 'PgPP' => 'oid',
+ 'Oracle' => 'rowid',
+ 'DB2' => '1=1'
+ );
+
+ # retrieve individual items
+ $query = qq|SELECT o.id AS orderitems_id,
+ p.partnumber, p.assembly, o.description, o.qty,
+ o.sellprice, o.parts_id AS id, o.unit, o.discount, p.bin,
+ o.reqdate, o.project_id, o.ship, o.serialnumber, o.notes,
+ pr.projectnumber,
+ pg.partsgroup, p.partsgroup_id, p.partnumber AS sku,
+ p.listprice, p.lastcost, p.weight, p.onhand,
+ p.inventory_accno_id, p.income_accno_id, p.expense_accno_id,
+ t.description AS partsgrouptranslation
+ FROM orderitems o
+ JOIN parts p ON (o.parts_id = p.id)
+ LEFT JOIN project pr ON (o.project_id = pr.id)
+ LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
+ LEFT JOIN translation t ON (t.trans_id = p.partsgroup_id AND t.language_code = '$form->{language_code}')
+ WHERE o.trans_id = $form->{id}
+ ORDER BY o.$oid{$myconfig->{dbdriver}}|;
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ # foreign exchange rates
+ &exchangerate_defaults($dbh, $form);
+
+ # query for price matrix
+ my $pmh = &price_matrix_query($dbh, $form);
+
+ # taxes
+ $query = qq|SELECT c.accno
+ FROM chart c
+ JOIN partstax pt ON (pt.chart_id = c.id)
+ WHERE pt.parts_id = ?|;
+ my $tth = $dbh->prepare($query) || $form->dberror($query);
+
+ my $taxrate;
+ my $ptref;
+ my $sellprice;
+ my $listprice;
+
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+
+ ($decimalplaces) = ($ref->{sellprice} =~ /\.(\d+)/);
+ $decimalplaces = length $decimalplaces;
+ $decimalplaces = ($decimalplaces > 2) ? $decimalplaces : 2;
+
+ $tth->execute($ref->{id});
+ $ref->{taxaccounts} = "";
+ $taxrate = 0;
+
+ while ($ptref = $tth->fetchrow_hashref(NAME_lc)) {
+ $ref->{taxaccounts} .= "$ptref->{accno} ";
+ $taxrate += $form->{"$ptref->{accno}_rate"};
+ }
+ $tth->finish;
+ chop $ref->{taxaccounts};
+
+ # preserve price
+ $sellprice = $ref->{sellprice};
+
+ # multiply by exchangerate
+ $ref->{sellprice} = $form->round_amount($ref->{sellprice} * $form->{$form->{currency}}, $decimalplaces);
+
+ for (qw(listprice lastcost)) { $ref->{$_} = $form->round_amount($ref->{$_} / $form->{$form->{currency}}, $decimalplaces) }
+
+ # partnumber and price matrix
+ &price_matrix($pmh, $ref, $form->{transdate}, $decimalplaces, $form, $myconfig);
+
+ $ref->{sellprice} = $sellprice;
+
+ $ref->{partsgroup} = $ref->{partsgrouptranslation} if $ref->{partsgrouptranslation};
+
+ push @{ $form->{form_details} }, $ref;
+
+ }
+ $sth->finish;
+
+ # get recurring transaction
+ $form->get_recurring($dbh);
+
+ } else {
+
+ # get last name used
+ $form->lastname_used($myconfig, $dbh, $form->{vc}) unless $form->{"$form->{vc}_id"};
+ delete $form->{notes};
+
+ }
+
+ $dbh->disconnect;
+
+}
+
+
+sub price_matrix_query {
+ my ($dbh, $form) = @_;
+
+ my $query;
+ my $sth;
+
+ if ($form->{customer_id}) {
+ $query = qq|SELECT p.id AS parts_id, 0 AS customer_id, 0 AS pricegroup_id,
+ 0 AS pricebreak, p.sellprice, NULL AS validfrom, NULL AS validto,
+ '$form->{defaultcurrency}' AS curr, '' AS pricegroup
+ FROM parts p
+ WHERE p.id = ?
+
+ UNION
+
+ SELECT p.*, g.pricegroup
+ FROM partscustomer p
+ LEFT JOIN pricegroup g ON (g.id = p.pricegroup_id)
+ WHERE p.parts_id = ?
+ AND p.customer_id = $form->{customer_id}
+
+ UNION
+
+ SELECT p.*, g.pricegroup
+ FROM partscustomer p
+ LEFT JOIN pricegroup g ON (g.id = p.pricegroup_id)
+ JOIN customer c ON (c.pricegroup_id = g.id)
+ WHERE p.parts_id = ?
+ AND c.id = $form->{customer_id}
+
+ UNION
+
+ SELECT p.*, '' AS pricegroup
+ FROM partscustomer p
+ WHERE p.customer_id = 0
+ AND p.pricegroup_id = 0
+ AND p.parts_id = ?
+
+ ORDER BY customer_id DESC, pricegroup_id DESC, pricebreak
+ |;
+ $sth = $dbh->prepare($query) || $form->dberror($query);
+ }
+
+ if ($form->{vendor_id}) {
+ # price matrix and vendor's partnumber
+ $query = qq|SELECT partnumber
+ FROM partsvendor
+ WHERE parts_id = ?
+ AND vendor_id = $form->{vendor_id}|;
+ $sth = $dbh->prepare($query) || $form->dberror($query);
+ }
+
+ $sth;
+
+}
+
+
+sub price_matrix {
+ my ($pmh, $ref, $transdate, $decimalplaces, $form, $myconfig) = @_;
+
+ $ref->{pricematrix} = "";
+ my $customerprice;
+ my $pricegroupprice;
+ my $sellprice;
+ my $mref;
+ my %p = ();
+
+ # depends if this is a customer or vendor
+ if ($form->{customer_id}) {
+ $pmh->execute($ref->{id}, $ref->{id}, $ref->{id}, $ref->{id});
+
+ while ($mref = $pmh->fetchrow_hashref(NAME_lc)) {
+
+ # check date
+ if ($mref->{validfrom}) {
+ next if $transdate < $form->datetonum($myconfig, $mref->{validfrom});
+ }
+ if ($mref->{validto}) {
+ next if $transdate > $form->datetonum($myconfig, $mref->{validto});
+ }
+
+ # convert price
+ $sellprice = $form->round_amount($mref->{sellprice} * $form->{$mref->{curr}}, $decimalplaces);
+
+ if ($mref->{customer_id}) {
+ $ref->{sellprice} = $sellprice if !$mref->{pricebreak};
+ $p{$mref->{pricebreak}} = $sellprice;
+ $customerprice = 1;
+ }
+
+ if ($mref->{pricegroup_id}) {
+ if (! $customerprice) {
+ $ref->{sellprice} = $sellprice if !$mref->{pricebreak};
+ $p{$mref->{pricebreak}} = $sellprice;
+ }
+ $pricegroupprice = 1;
+ }
+
+ if (!$customerprice && !$pricegroupprice) {
+ $p{$mref->{pricebreak}} = $sellprice;
+ }
+
+ }
+ $pmh->finish;
+
+ if (%p) {
+ if ($ref->{sellprice}) {
+ $p{0} = $ref->{sellprice};
+ }
+ for (sort { $a <=> $b } keys %p) { $ref->{pricematrix} .= "${_}:$p{$_} " }
+ } else {
+ if ($init) {
+ $ref->{sellprice} = $form->round_amount($ref->{sellprice}, $decimalplaces);
+ } else {
+ $ref->{sellprice} = $form->round_amount($ref->{sellprice} * (1 - $form->{tradediscount}), $decimalplaces);
+ }
+ $ref->{pricematrix} = "0:$ref->{sellprice} " if $ref->{sellprice};
+ }
+ chop $ref->{pricematrix};
+
+ }
+
+
+ if ($form->{vendor_id}) {
+ $pmh->execute($ref->{id});
+
+ $mref = $pmh->fetchrow_hashref(NAME_lc);
+
+ if ($mref->{partnumber} ne "") {
+ $ref->{partnumber} = $mref->{partnumber};
+ }
+
+ if ($mref->{lastcost}) {
+ # do a conversion
+ $ref->{sellprice} = $form->round_amount($mref->{lastcost} * $form->{$mref->{curr}}, $decimalplaces);
+ }
+ $pmh->finish;
+
+ $ref->{sellprice} *= 1;
+
+ # add 0:price to matrix
+ $ref->{pricematrix} = "0:$ref->{sellprice}";
+
+ }
+
+}
+
+
+sub exchangerate_defaults {
+ my ($dbh, $form) = @_;
+
+ my $var;
+ my $buysell = ($form->{vc} eq "customer") ? "buy" : "sell";
+
+ # get default currencies
+ my $query = qq|SELECT substr(curr,1,3), curr FROM defaults|;
+ ($form->{defaultcurrency}, $form->{currencies}) = $dbh->selectrow_array($query);
+
+ $query = qq|SELECT $buysell
+ FROM exchangerate
+ WHERE curr = ?
+ AND transdate = ?|;
+ my $eth1 = $dbh->prepare($query) || $form->dberror($query);
+ $query = qq~SELECT max(transdate || ' ' || $buysell || ' ' || curr)
+ FROM exchangerate
+ WHERE curr = ?~;
+ my $eth2 = $dbh->prepare($query) || $form->dberror($query);
+
+ # get exchange rates for transdate or max
+ foreach $var (split /:/, substr($form->{currencies},4)) {
+ $eth1->execute($var, $form->{transdate});
+ ($form->{$var}) = $eth1->fetchrow_array;
+ if (! $form->{$var} ) {
+ $eth2->execute($var);
+
+ ($form->{$var}) = $eth2->fetchrow_array;
+ ($null, $form->{$var}) = split / /, $form->{$var};
+ $form->{$var} = 1 unless $form->{$var};
+ $eth2->finish;
+ }
+ $eth1->finish;
+ }
+
+ $form->{$form->{currency}} = $form->{exchangerate} if $form->{exchangerate};
+ $form->{$form->{currency}} ||= 1;
+ $form->{$form->{defaultcurrency}} = 1;
+
+}
+
+
+sub order_details {
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+ my $query;
+ my $sth;
+
+ my $item;
+ my $i;
+ my @sortlist = ();
+ my $projectnumber;
+ my $projectdescription;
+ my $projectnumber_id;
+ my $translation;
+ my $partsgroup;
+
+ my %oid = ( 'Pg' => 'oid',
+ 'PgPP' => 'oid',
+ 'Oracle' => 'rowid',
+ 'DB2' => '1=1'
+ );
+
+ my @taxaccounts;
+ my %taxaccounts;
+ my $tax;
+ my $taxrate;
+ my $taxamount;
+
+ my %translations;
+
+ $query = qq|SELECT p.description, t.description
+ FROM project p
+ LEFT JOIN translation t ON (t.trans_id = p.id AND t.language_code = '$form->{language_code}')
+ WHERE id = ?|;
+ my $prh = $dbh->prepare($query) || $form->dberror($query);
+
+ $query = qq|SELECT inventory_accno_id, income_accno_id,
+ expense_accno_id, assembly FROM parts
+ WHERE id = ?|;
+ my $pth = $dbh->prepare($query) || $form->dberror($query);
+
+ my $sortby;
+
+ # sort items by project and partsgroup
+ for $i (1 .. $form->{rowcount}) {
+
+ if ($form->{"id_$i"}) {
+ # account numbers
+ $pth->execute($form->{"id_$i"});
+ $ref = $pth->fetchrow_hashref(NAME_lc);
+ for (keys %$ref) { $form->{"${_}_$i"} = $ref->{$_} }
+ $pth->finish;
+
+ $projectnumber_id = 0;
+ $projectnumber = "";
+ $form->{partsgroup} = "";
+ $form->{projectnumber} = "";
+
+ if ($form->{groupprojectnumber} || $form->{grouppartsgroup}) {
+
+ $inventory_accno_id = ($form->{"inventory_accno_id_$i"} || $form->{"assembly_$i"}) ? "1" : "";
+
+ if ($form->{groupprojectnumber}) {
+ ($projectnumber, $projectnumber_id) = split /--/, $form->{"projectnumber_$i"};
+ }
+ if ($form->{grouppartsgroup}) {
+ ($form->{partsgroup}) = split /--/, $form->{"partsgroup_$i"};
+ }
+
+ if ($projectnumber_id && $form->{groupprojectnumber}) {
+ if ($translation{$projectnumber_id}) {
+ $form->{projectnumber} = $translation{$projectnumber_id};
+ } else {
+ # get project description
+ $prh->execute($projectnumber_id);
+ ($projectdescription, $translation) = $prh->fetchrow_array;
+ $prh->finish;
+
+ $form->{projectnumber} = ($translation) ? "$projectnumber, $translation" : "$projectnumber, $projectdescription";
+
+ $translation{$projectnumber_id} = $form->{projectnumber};
+ }
+ }
+
+ if ($form->{grouppartsgroup} && $form->{partsgroup}) {
+ $form->{projectnumber} .= " / " if $projectnumber_id;
+ $form->{projectnumber} .= $form->{partsgroup};
+ }
+
+ $form->format_string(projectnumber);
+
+ }
+
+ $sortby = qq|$projectnumber$form->{partsgroup}|;
+ if ($form->{sortby} ne 'runningnumber') {
+ for (qw(partnumber description bin)) {
+ $sortby .= $form->{"${_}_$i"} if $form->{sortby} eq $_;
+ }
+ }
+
+ push @sortlist, [ $i, qq|$projectnumber$form->{partsgroup}$inventory_accno_id|, $form->{projectnumber}, $projectnumber_id, $form->{partsgroup}, $sortby ];
+ }
+
+ }
+
+ delete $form->{projectnumber};
+
+ # sort the whole thing by project and group
+ @sortlist = sort { $a->[5] cmp $b->[5] } @sortlist;
+
+
+ # if there is a warehouse limit picking
+ if ($form->{warehouse_id} && $form->{formname} =~ /(pick|packing)_list/) {
+ # run query to check for inventory
+ $query = qq|SELECT sum(qty) AS qty
+ FROM inventory
+ WHERE parts_id = ?
+ AND warehouse_id = ?|;
+ $sth = $dbh->prepare($query) || $form->dberror($query);
+
+ for $i (1 .. $form->{rowcount}) {
+ $sth->execute($form->{"id_$i"}, $form->{warehouse_id}) || $form->dberror;
+
+ ($qty) = $sth->fetchrow_array;
+ $sth->finish;
+
+ $form->{"qty_$i"} = 0 if $qty == 0;
+
+ if ($form->parse_amount($myconfig, $form->{"ship_$i"}) > $qty) {
+ $form->{"ship_$i"} = $form->format_amount($myconfig, $qty);
+ }
+ }
+ }
+
+
+ my $runningnumber = 1;
+ my $sameitem = "";
+ my $subtotal;
+ my $k = scalar @sortlist;
+ my $j = 0;
+
+ foreach $item (@sortlist) {
+ $i = $item->[0];
+ $j++;
+
+ if ($form->{groupprojectnumber} || $form->{grouppartsgroup}) {
+ if ($item->[1] ne $sameitem) {
+ $sameitem = $item->[1];
+
+ $ok = 0;
+
+ if ($form->{groupprojectnumber}) {
+ $ok = $form->{"projectnumber_$i"};
+ }
+ if ($form->{grouppartsgroup}) {
+ $ok = $form->{"partsgroup_$i"} unless $ok;
+ }
+
+ if ($ok) {
+ if ($form->{"inventory_accno_id_$i"} || $form->{"assembly_$i"}) {
+ push(@{ $form->{part} }, "");
+ push(@{ $form->{service} }, NULL);
+ } else {
+ push(@{ $form->{part} }, NULL);
+ push(@{ $form->{service} }, "");
+ }
+
+ push(@{ $form->{description} }, $item->[2]);
+ for (qw(taxrates runningnumber number sku qty ship unit bin serialnumber requiredate projectnumber sellprice listprice netprice discount discountrate linetotal weight itemnotes)) { push(@{ $form->{$_} }, "") }
+ push(@{ $form->{lineitems} }, { amount => 0, tax => 0 });
+ }
+ }
+ }
+
+ $form->{"qty_$i"} = $form->parse_amount($myconfig, $form->{"qty_$i"});
+ $form->{"ship_$i"} = $form->parse_amount($myconfig, $form->{"ship_$i"});
+
+ if ($form->{"qty_$i"}) {
+
+ $form->{totalqty} += $form->{"qty_$i"};
+ $form->{totalship} += $form->{"ship_$i"};
+ $form->{totalweight} += ($form->{"weight_$i"} * $form->{"qty_$i"});
+ $form->{totalweightship} += ($form->{"weight_$i"} * $form->{"ship_$i"});
+
+ # add number, description and qty to $form->{number}, ....
+ push(@{ $form->{runningnumber} }, $runningnumber++);
+ push(@{ $form->{number} }, qq|$form->{"partnumber_$i"}|);
+ push(@{ $form->{sku} }, qq|$form->{"sku_$i"}|);
+ push(@{ $form->{description} }, qq|$form->{"description_$i"}|);
+ push(@{ $form->{itemnotes} }, $form->{"notes_$i"});
+ push(@{ $form->{qty} }, $form->format_amount($myconfig, $form->{"qty_$i"}));
+ push(@{ $form->{ship} }, $form->format_amount($myconfig, $form->{"ship_$i"}));
+ push(@{ $form->{unit} }, qq|$form->{"unit_$i"}|);
+ push(@{ $form->{bin} }, qq|$form->{"bin_$i"}|);
+ push(@{ $form->{serialnumber} }, qq|$form->{"serialnumber_$i"}|);
+ push(@{ $form->{requiredate} }, qq|$form->{"reqdate_$i"}|);
+ push(@{ $form->{projectnumber} }, qq|$form->{"projectnumber_$i"}|);
+
+ push(@{ $form->{sellprice} }, $form->{"sellprice_$i"});
+
+ push(@{ $form->{listprice} }, $form->{"listprice_$i"});
+
+ push(@{ $form->{weight} }, $form->format_amount($myconfig, $form->{"weight_$i"} * $form->{"ship_$i"}));
+
+ my $sellprice = $form->parse_amount($myconfig, $form->{"sellprice_$i"});
+ my ($dec) = ($sellprice =~ /\.(\d+)/);
+ $dec = length $dec;
+ my $decimalplaces = ($dec > 2) ? $dec : 2;
+
+ my $discount = $form->round_amount($sellprice * $form->parse_amount($myconfig, $form->{"discount_$i"}) / 100, $decimalplaces);
+
+ # keep a netprice as well, (sellprice - discount)
+ $form->{"netprice_$i"} = $sellprice - $discount;
+
+ my $linetotal = $form->round_amount($form->{"qty_$i"} * $form->{"netprice_$i"}, 2);
+
+ if ($form->{"inventory_accno_id_$i"} || $form->{"assembly_$i"}) {
+ push(@{ $form->{part} }, $form->{"sku_$i"});
+ push(@{ $form->{service} }, NULL);
+ $form->{totalparts} += $linetotal;
+ } else {
+ push(@{ $form->{service} }, $form->{"sku_$i"});
+ push(@{ $form->{part} }, NULL);
+ $form->{totalservices} += $linetotal;
+ }
+
+ push(@{ $form->{netprice} }, ($form->{"netprice_$i"}) ? $form->format_amount($myconfig, $form->{"netprice_$i"}, $decimalplaces) : " ");
+
+ $discount = ($discount) ? $form->format_amount($myconfig, $discount * -1, $decimalplaces) : " ";
+
+ push(@{ $form->{discount} }, $discount);
+ push(@{ $form->{discountrate} }, $form->format_amount($myconfig, $form->{"discount_$i"}));
+
+ $form->{ordtotal} += $linetotal;
+
+ # this is for the subtotals for grouping
+ $subtotal += $linetotal;
+
+ $form->{"linetotal_$i"} = $form->format_amount($myconfig, $linetotal, 2);
+ push(@{ $form->{linetotal} }, $form->{"linetotal_$i"});
+
+ @taxaccounts = split / /, $form->{"taxaccounts_$i"};
+
+ my $ml = 1;
+ my @taxrates = ();
+
+ $tax = 0;
+
+ for (0 .. 1) {
+ $taxrate = 0;
+
+ for (@taxaccounts) { $taxrate += $form->{"${_}_rate"} if ($form->{"${_}_rate"} * $ml) > 0 }
+
+ $taxrate *= $ml;
+ $taxamount = $linetotal * $taxrate / (1 + $taxrate);
+ $taxbase = ($linetotal - $taxamount);
+
+ foreach $item (@taxaccounts) {
+ if (($form->{"${item}_rate"} * $ml) > 0) {
+
+ push @taxrates, $form->{"${item}_rate"} * 100;
+
+ if ($form->{taxincluded}) {
+ $taxaccounts{$item} += $linetotal * $form->{"${item}_rate"} / (1 + $taxrate);
+ $taxbase{$item} += $taxbase;
+ } else {
+ $taxbase{$item} += $linetotal;
+ $taxaccounts{$item} += $linetotal * $form->{"${item}_rate"};
+ }
+ }
+ }
+
+ if ($form->{taxincluded}) {
+ $tax += $linetotal * ($taxrate / (1 + ($taxrate * $ml)));
+ } else {
+ $tax += $linetotal * $taxrate;
+ }
+
+ $ml *= -1;
+ }
+
+ push(@{ $form->{lineitems} }, { amount => $linetotal, tax => $form->round_amount($tax, 2) });
+ push(@{ $form->{taxrates} }, join ' ', sort { $a <=> $b } @taxrates);
+
+ if ($form->{"assembly_$i"}) {
+ $form->{stagger} = -1;
+ &assembly_details($myconfig, $form, $dbh, $form->{"id_$i"}, $oid{$myconfig->{dbdriver}}, $form->{"qty_$i"});
+ }
+
+ }
+
+ # add subtotal
+ if ($form->{groupprojectnumber} || $form->{grouppartsgroup}) {
+ if ($subtotal) {
+ if ($j < $k) {
+ # look at next item
+ if ($sortlist[$j]->[1] ne $sameitem) {
+
+ if ($form->{"inventory_accno_id_$i"} || $form->{"assembly_$i"}) {
+ push(@{ $form->{part} }, "");
+ push(@{ $form->{service} }, NULL);
+ } else {
+ push(@{ $form->{service} }, "");
+ push(@{ $form->{part} }, NULL);
+ }
+
+ for (qw(taxrates runningnumber number sku qty ship unit bin serialnumber requiredate projectnumber sellprice listprice netprice discount discountrate weight itemnotes)) { push(@{ $form->{$_} }, "") }
+
+ push(@{ $form->{description} }, $form->{groupsubtotaldescription});
+
+ push(@{ $form->{lineitems} }, { amount => 0, tax => 0 });
+
+ if ($form->{groupsubtotaldescription} ne "") {
+ push(@{ $form->{linetotal} }, $form->format_amount($myconfig, $subtotal, 2));
+ } else {
+ push(@{ $form->{linetotal} }, "");
+ }
+ $subtotal = 0;
+ }
+
+ } else {
+
+ # got last item
+ if ($form->{groupsubtotaldescription} ne "") {
+
+ if ($form->{"inventory_accno_id_$i"} || $form->{"assembly_$i"}) {
+ push(@{ $form->{part} }, "");
+ push(@{ $form->{service} }, NULL);
+ } else {
+ push(@{ $form->{service} }, "");
+ push(@{ $form->{part} }, NULL);
+ }
+
+ for (qw(taxrates runningnumber number sku qty ship unit bin serialnumber requiredate projectnumber sellprice listprice netprice discount discountrate weight itemnotes)) { push(@{ $form->{$_} }, "") }
+
+ push(@{ $form->{description} }, $form->{groupsubtotaldescription});
+ push(@{ $form->{linetotal} }, $form->format_amount($myconfig, $subtotal, 2));
+ push(@{ $form->{lineitems} }, { amount => 0, tax => 0 });
+ }
+ }
+ }
+ }
+ }
+
+
+ $tax = 0;
+ foreach $item (sort keys %taxaccounts) {
+ if ($form->round_amount($taxaccounts{$item}, 2)) {
+ $tax += $taxamount = $form->round_amount($taxaccounts{$item}, 2);
+
+ push(@{ $form->{taxbaseinclusive} }, $form->{"${item}_taxbaseinclusive"} = $form->round_amount($taxbase{$item} + $tax, 2));
+ push(@{ $form->{taxbase} }, $form->{"${item}_taxbase"} = $form->format_amount($myconfig, $taxbase{$item}, 2));
+ push(@{ $form->{tax} }, $form->{"${item}_tax"} = $form->format_amount($myconfig, $taxamount, 2));
+
+ push(@{ $form->{taxdescription} }, $form->{"${item}_description"});
+
+ $form->{"${item}_taxrate"} = $form->format_amount($myconfig, $form->{"${item}_rate"} * 100);
+ push(@{ $form->{taxrate} }, $form->{"${item}_taxrate"});
+
+ push(@{ $form->{taxnumber} }, $form->{"${item}_taxnumber"});
+ }
+ }
+
+ # adjust taxes for lineitems
+ my $total = 0;
+ for (@{ $form->{lineitems} }) {
+ $total += $_->{tax};
+ }
+ if ($form->round_amount($total,2) != $form->round_amount($tax,2)) {
+ # get largest amount
+ for (reverse sort { $a->{tax} <=> $b->{tax} } @{ $form->{lineitems} }) {
+ $_->{tax} -= $total - $tax;
+ last;
+ }
+ }
+ $i = 1;
+ for (@{ $form->{lineitems} }) {
+ push(@{ $form->{linetax} }, $form->format_amount($myconfig, $_->{tax}, 2, ""));
+ }
+
+
+ for (qw(totalparts totalservices)) { $form->{$_} = $form->format_amount($myconfig, $form->{$_}, 2) }
+ for (qw(totalqty totalship totalweight)) { $form->{$_} = $form->format_amount($myconfig, $form->{$_}) }
+ $form->{subtotal} = $form->format_amount($myconfig, $form->{ordtotal}, 2);
+ $form->{ordtotal} = ($form->{taxincluded}) ? $form->{ordtotal} : $form->{ordtotal} + $tax;
+
+ use LedgerSMB::CP;
+ my $c;
+ if ($form->{language_code} ne "") {
+ $c = new CP $form->{language_code};
+ } else {
+ $c = new CP $myconfig->{countrycode};
+ }
+ $c->init;
+ my $whole;
+ ($whole, $form->{decimal}) = split /\./, $form->{ordtotal};
+ $form->{decimal} .= "00";
+ $form->{decimal} = substr($form->{decimal}, 0, 2);
+
+ $form->{text_decimal} = $c->num2text($form->{decimal} * 1);
+ $form->{text_amount} = $c->num2text($whole);
+ $form->{integer_amount} = $form->format_amount($myconfig, $whole);
+
+ # format amounts
+ $form->{quototal} = $form->{ordtotal} = $form->format_amount($myconfig, $form->{ordtotal}, 2);
+
+ $form->format_string(qw(text_amount text_decimal));
+
+ $query = qq|SELECT weightunit FROM defaults|;
+ ($form->{weightunit}) = $dbh->selectrow_array($query);
+
+ $dbh->disconnect;
+
+}
+
+
+sub assembly_details {
+ my ($myconfig, $form, $dbh, $id, $oid, $qty) = @_;
+
+ my $sm = "";
+ my $spacer;
+
+ $form->{stagger}++;
+ if ($form->{format} eq 'html') {
+ $spacer = "&nbsp;" x (3 * ($form->{stagger} - 1)) if $form->{stagger} > 1;
+ }
+ if ($form->{format} =~ /(postscript|pdf)/) {
+ if ($form->{stagger} > 1) {
+ $spacer = ($form->{stagger} - 1) * 3;
+ $spacer = '\rule{'.$spacer.'mm}{0mm}';
+ }
+ }
+
+ # get parts and push them onto the stack
+ my $sortorder = "";
+
+ if ($form->{grouppartsgroup}) {
+ $sortorder = qq|ORDER BY pg.partsgroup, a.$oid|;
+ } else {
+ $sortorder = qq|ORDER BY a.$oid|;
+ }
+
+ my $where = ($form->{formname} eq 'work_order') ? "1 = 1" : "a.bom = '1'";
+
+ my $query = qq|SELECT p.partnumber, p.description, p.unit, a.qty,
+ pg.partsgroup, p.partnumber AS sku, p.assembly, p.id, p.bin
+ FROM assembly a
+ JOIN parts p ON (a.parts_id = p.id)
+ LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
+ WHERE $where
+ AND a.id = '$id'
+ $sortorder|;
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
+
+ for (qw(partnumber description partsgroup)) {
+ $form->{"a_$_"} = $ref->{$_};
+ $form->format_string("a_$_");
+ }
+
+ if ($form->{grouppartsgroup} && $ref->{partsgroup} ne $sm) {
+ for (qw(taxrates number sku unit qty runningnumber ship bin serialnumber requiredate projectnumber sellprice listprice netprice discount discountrate linetotal weight itemnotes)) { push(@{ $form->{$_} }, "") }
+ $sm = ($form->{"a_partsgroup"}) ? $form->{"a_partsgroup"} : "";
+ push(@{ $form->{description} }, "$spacer$sm");
+
+ push(@{ $form->{lineitems} }, { amount => 0, tax => 0 });
+
+ }
+
+ if ($form->{stagger}) {
+
+ push(@{ $form->{description} }, qq|$spacer$form->{"a_partnumber"}, $form->{"a_description"}|);
+ for (qw(taxrates number sku runningnumber ship serialnumber requiredate projectnumber sellprice listprice netprice discount discountrate linetotal weight itemnotes)) { push(@{ $form->{$_} }, "") }
+
+ } else {
+
+ push(@{ $form->{description} }, qq|$form->{"a_description"}|);
+ push(@{ $form->{sku} }, $form->{"a_partnumber"});
+ push(@{ $form->{number} }, $form->{"a_partnumber"});
+
+ for (qw(taxrates runningnumber ship serialnumber requiredate projectnumber sellprice listprice netprice discount discountrate linetotal weight itemnotes)) { push(@{ $form->{$_} }, "") }
+
+ }
+
+ push(@{ $form->{lineitems} }, { amount => 0, tax => 0 });
+
+ push(@{ $form->{qty} }, $form->format_amount($myconfig, $ref->{qty} * $qty));
+ for (qw(unit bin)) {
+ $form->{"a_$_"} = $ref->{$_};
+ $form->format_string("a_$_");
+ push(@{ $form->{$_} }, $form->{"a_$_"});
+ }
+
+ if ($ref->{assembly} && $form->{formname} eq 'work_order') {
+ &assembly_details($myconfig, $form, $dbh, $ref->{id}, $oid, $ref->{qty} * $qty);
+ }
+
+ }
+ $sth->finish;
+
+ $form->{stagger}--;
+
+}
+
+
+sub project_description {
+ my ($self, $dbh, $id) = @_;
+
+ my $query = qq|SELECT description
+ FROM project
+ WHERE id = $id|;
+ ($_) = $dbh->selectrow_array($query);
+
+ $_;
+
+}
+
+
+sub get_warehouses {
+ my ($self, $myconfig, $form) = @_;
+
+ my $dbh = $form->dbconnect($myconfig);
+ # setup warehouses
+ my $query = qq|SELECT id, description
+ FROM warehouse
+ ORDER BY 2|;
+
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
+ push @{ $form->{all_warehouse} }, $ref;
+ }
+ $sth->finish;
+
+ $dbh->disconnect;
+
+}
+
+
+sub save_inventory {
+ my ($self, $myconfig, $form) = @_;
+
+ my ($null, $warehouse_id) = split /--/, $form->{warehouse};
+ $warehouse_id *= 1;
+
+ my $ml = ($form->{type} eq 'ship_order') ? -1 : 1;
+
+ my $dbh = $form->dbconnect_noauto($myconfig);
+ my $sth;
+ my $wth;
+ my $serialnumber;
+ my $ship;
+
+ my ($null, $employee_id) = split /--/, $form->{employee};
+ ($null, $employee_id) = $form->get_employee($dbh) if ! $employee_id;
+
+ $query = qq|SELECT serialnumber, ship
+ FROM orderitems
+ WHERE trans_id = ?
+ AND id = ?
+ FOR UPDATE|;
+ $sth = $dbh->prepare($query) || $form->dberror($query);
+
+ $query = qq|SELECT sum(qty)
+ FROM inventory
+ WHERE parts_id = ?
+ AND warehouse_id = ?|;
+ $wth = $dbh->prepare($query) || $form->dberror($query);
+
+
+ for my $i (1 .. $form->{rowcount}) {
+
+ $ship = (abs($form->{"ship_$i"}) > abs($form->{"qty_$i"})) ? $form->{"qty_$i"} : $form->{"ship_$i"};
+
+ if ($warehouse_id && $form->{type} eq 'ship_order') {
+
+ $wth->execute($form->{"id_$i"}, $warehouse_id) || $form->dberror;
+
+ ($qty) = $wth->fetchrow_array;
+ $wth->finish;
+
+ if ($ship > $qty) {
+ $ship = $qty;
+ }
+ }
+
+
+ if ($ship) {
+
+ $ship *= $ml;
+ $query = qq|INSERT INTO inventory (parts_id, warehouse_id,
+ qty, trans_id, orderitems_id, shippingdate, employee_id)
+ VALUES ($form->{"id_$i"}, $warehouse_id,
+ $ship, $form->{"id"},
+ $form->{"orderitems_id_$i"}, '$form->{shippingdate}',
+ $employee_id)|;
+ $dbh->do($query) || $form->dberror($query);
+
+ # add serialnumber, ship to orderitems
+ $sth->execute($form->{id}, $form->{"orderitems_id_$i"}) || $form->dberror;
+ ($serialnumber, $ship) = $sth->fetchrow_array;
+ $sth->finish;
+
+ $serialnumber .= " " if $serialnumber;
+ $serialnumber .= qq|$form->{"serialnumber_$i"}|;
+ $ship += $form->{"ship_$i"};
+
+ $query = qq|UPDATE orderitems SET
+ serialnumber = '$serialnumber',
+ ship = $ship,
+ reqdate = '$form->{shippingdate}'
+ WHERE trans_id = $form->{id}
+ AND id = $form->{"orderitems_id_$i"}|;
+ $dbh->do($query) || $form->dberror($query);
+
+ # update order with ship via
+ $query = qq|UPDATE oe SET
+ shippingpoint = |.$dbh->quote($form->{shippingpoint}).qq|,
+ shipvia = |.$dbh->quote($form->{shipvia}).qq|
+ WHERE id = $form->{id}|;
+ $dbh->do($query) || $form->dberror($query);
+
+ # update onhand for parts
+ $form->update_balance($dbh,
+ "parts",
+ "onhand",
+ qq|id = $form->{"id_$i"}|,
+ $form->{"ship_$i"} * $ml);
+
+ }
+ }
+
+ my $rc = $dbh->commit;
+ $dbh->disconnect;
+
+ $rc;
+
+}
+
+
+sub adj_onhand {
+ my ($dbh, $form, $ml) = @_;
+
+ my $query = qq|SELECT oi.parts_id, oi.ship, p.inventory_accno_id, p.assembly
+ FROM orderitems oi
+ JOIN parts p ON (p.id = oi.parts_id)
+ WHERE oi.trans_id = $form->{id}|;
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ $query = qq|SELECT sum(p.inventory_accno_id), p.assembly
+ FROM parts p
+ JOIN assembly a ON (a.parts_id = p.id)
+ WHERE a.id = ?
+ GROUP BY p.assembly|;
+ my $ath = $dbh->prepare($query) || $form->dberror($query);
+
+ my $ref;
+
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+
+ if ($ref->{inventory_accno_id} || $ref->{assembly}) {
+
+ # do not update if assembly consists of all services
+ if ($ref->{assembly}) {
+ $ath->execute($ref->{parts_id}) || $form->dberror($query);
+
+ my ($inv, $assembly) = $ath->fetchrow_array;
+ $ath->finish;
+
+ next unless ($inv || $assembly);
+
+ }
+
+ # adjust onhand in parts table
+ $form->update_balance($dbh,
+ "parts",
+ "onhand",
+ qq|id = $ref->{parts_id}|,
+ $ref->{ship} * $ml);
+ }
+ }
+
+ $sth->finish;
+
+}
+
+
+sub adj_inventory {
+ my ($dbh, $myconfig, $form) = @_;
+
+ my %oid = ( 'Pg' => 'oid',
+ 'PgPP' => 'oid',
+ 'Oracle' => 'rowid',
+ 'DB2' => '1=1'
+ );
+
+ # increase/reduce qty in inventory table
+ my $query = qq|SELECT oi.id, oi.parts_id, oi.ship
+ FROM orderitems oi
+ WHERE oi.trans_id = $form->{id}|;
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ $query = qq|SELECT $oid{$myconfig->{dbdriver}} AS oid, qty,
+ (SELECT SUM(qty) FROM inventory
+ WHERE trans_id = $form->{id}
+ AND orderitems_id = ?) AS total
+ FROM inventory
+ WHERE trans_id = $form->{id}
+ AND orderitems_id = ?|;
+ my $ith = $dbh->prepare($query) || $form->dberror($query);
+
+ my $qty;
+ my $ml = ($form->{type} =~ /(ship|sales)_order/) ? -1 : 1;
+
+ while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
+
+ $ith->execute($ref->{id}, $ref->{id}) || $form->dberror($query);
+
+ my $ship = $ref->{ship};
+ while (my $inv = $ith->fetchrow_hashref(NAME_lc)) {
+
+ if (($qty = (($inv->{total} * $ml) - $ship)) >= 0) {
+ $qty = $inv->{qty} * $ml if ($qty > ($inv->{qty} * $ml));
+
+ $form->update_balance($dbh,
+ "inventory",
+ "qty",
+ qq|$oid{$myconfig->{dbdriver}} = $inv->{oid}|,
+ $qty * -1 * $ml);
+ $ship -= $qty;
+ }
+ }
+ $ith->finish;
+
+ }
+ $sth->finish;
+
+ # delete inventory entries if qty = 0
+ $query = qq|DELETE FROM inventory
+ WHERE trans_id = $form->{id}
+ AND qty = 0|;
+ $dbh->do($query) || $form->dberror($query);
+
+}
+
+
+sub get_inventory {
+ my ($self, $myconfig, $form) = @_;
+
+ my $where;
+ my $query;
+ my $null;
+ my $fromwarehouse_id;
+ my $towarehouse_id;
+ my $var;
+
+ my $dbh = $form->dbconnect($myconfig);
+
+ if ($form->{partnumber} ne "") {
+ $var = $form->like(lc $form->{partnumber});
+ $where .= "
+ AND lower(p.partnumber) LIKE '$var'";
+ }
+ if ($form->{description} ne "") {
+ $var = $form->like(lc $form->{description});
+ $where .= "
+ AND lower(p.description) LIKE '$var'";
+ }
+ if ($form->{partsgroup} ne "") {
+ ($null, $var) = split /--/, $form->{partsgroup};
+ $where .= "
+ AND pg.id = $var";
+ }
+
+
+ ($null, $fromwarehouse_id) = split /--/, $form->{fromwarehouse};
+ $fromwarehouse_id *= 1;
+
+ ($null, $towarehouse_id) = split /--/, $form->{towarehouse};
+ $towarehouse_id *= 1;
+
+ my %ordinal = ( partnumber => 2,
+ description => 3,
+ partsgroup => 5,
+ warehouse => 6,
+ );
+
+ my @a = (partnumber, warehouse);
+ my $sortorder = $form->sort_order(\@a, \%ordinal);
+
+ if ($fromwarehouse_id) {
+ if ($towarehouse_id) {
+ $where .= "
+ AND NOT i.warehouse_id = $towarehouse_id";
+ }
+ $query = qq|SELECT p.id, p.partnumber, p.description,
+ sum(i.qty) * 2 AS onhand, sum(i.qty) AS qty,
+ pg.partsgroup, w.description AS warehouse, i.warehouse_id
+ FROM inventory i
+ JOIN parts p ON (p.id = i.parts_id)
+ LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
+ JOIN warehouse w ON (w.id = i.warehouse_id)
+ WHERE i.warehouse_id = $fromwarehouse_id
+ $where
+ GROUP BY p.id, p.partnumber, p.description, pg.partsgroup, w.description, i.warehouse_id
+ ORDER BY $sortorder|;
+ } else {
+ if ($towarehouse_id) {
+ $query = qq|
+ SELECT p.id, p.partnumber, p.description,
+ p.onhand, (SELECT SUM(qty) FROM inventory i WHERE i.parts_id = p.id) AS qty,
+ pg.partsgroup, '' AS warehouse, 0 AS warehouse_id
+ FROM parts p
+ LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
+ WHERE p.onhand > 0
+ $where
+ UNION|;
+ }
+
+ $query .= qq|
+ SELECT p.id, p.partnumber, p.description,
+ sum(i.qty) * 2 AS onhand, sum(i.qty) AS qty,
+ pg.partsgroup, w.description AS warehouse, i.warehouse_id
+ FROM inventory i
+ JOIN parts p ON (p.id = i.parts_id)
+ LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
+ JOIN warehouse w ON (w.id = i.warehouse_id)
+ WHERE i.warehouse_id != $towarehouse_id
+ $where
+ GROUP BY p.id, p.partnumber, p.description, pg.partsgroup, w.description, i.warehouse_id
+ ORDER BY $sortorder|;
+ }
+
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+ $ref->{qty} = $ref->{onhand} - $ref->{qty};
+ push @{ $form->{all_inventory} }, $ref if $ref->{qty} > 0;
+ }
+ $sth->finish;
+
+ $dbh->disconnect;
+
+}
+
+
+sub transfer {
+ my ($self, $myconfig, $form) = @_;
+
+ my $dbh = $form->dbconnect_noauto($myconfig);
+
+ ($form->{employee}, $form->{employee_id}) = $form->get_employee($dbh);
+
+ my @a = localtime;
+ $a[5] += 1900;
+ $a[4]++;
+ $a[4] = substr("0$a[4]", -2);
+ $a[3] = substr("0$a[3]", -2);
+ $shippingdate = "$a[5]$a[4]$a[3]";
+
+ my %total = ();
+
+ my $query = qq|INSERT INTO inventory
+ (warehouse_id, parts_id, qty, shippingdate, employee_id)
+ VALUES (?, ?, ?, '$shippingdate', $form->{employee_id})|;
+ $sth = $dbh->prepare($query) || $form->dberror($query);
+
+ my $qty;
+
+ for my $i (1 .. $form->{rowcount}) {
+ $qty = $form->parse_amount($myconfig, $form->{"transfer_$i"});
+
+ $qty = $form->{"qty_$i"} if ($qty > $form->{"qty_$i"});
+
+ if ($qty > 0) {
+ # to warehouse
+ if ($form->{warehouse_id}) {
+ $sth->execute($form->{warehouse_id}, $form->{"id_$i"}, $qty) || $form->dberror;
+ $sth->finish;
+ }
+
+ # from warehouse
+ if ($form->{"warehouse_id_$i"}) {
+ $sth->execute($form->{"warehouse_id_$i"}, $form->{"id_$i"}, $qty * -1) || $form->dberror;
+ $sth->finish;
+ }
+ }
+ }
+
+ my $rc = $dbh->commit;
+ $dbh->disconnect;
+
+ $rc;
+
+}
+
+
+sub get_soparts {
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ my $id;
+ my $ref;
+
+ # store required items from selected sales orders
+ my $query = qq|SELECT p.id, oi.qty - oi.ship AS required,
+ p.assembly
+ FROM orderitems oi
+ JOIN parts p ON (p.id = oi.parts_id)
+ WHERE oi.trans_id = ?|;
+ my $sth = $dbh->prepare($query) || $form->dberror($query);
+
+ for (my $i = 1; $i <= $form->{rowcount}; $i++) {
+
+ if ($form->{"ndx_$i"}) {
+
+ $sth->execute($form->{"ndx_$i"});
+
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+ &add_items_required("", $dbh, $form, $ref->{id}, $ref->{required}, $ref->{assembly});
+ }
+ $sth->finish;
+ }
+
+ }
+
+ $query = qq|SELECT current_date FROM defaults|;
+ ($form->{transdate}) = $dbh->selectrow_array($query);
+
+ # foreign exchange rates
+ &exchangerate_defaults($dbh, $form);
+
+ $dbh->disconnect;
+
+}
+
+
+sub add_items_required {
+ my ($self, $dbh, $form, $parts_id, $required, $assembly) = @_;
+
+ my $query;
+ my $sth;
+ my $ref;
+
+ if ($assembly) {
+ $query = qq|SELECT p.id, a.qty, p.assembly
+ FROM assembly a
+ JOIN parts p ON (p.id = a.parts_id)
+ WHERE a.id = $parts_id|;
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+ &add_items_required("", $dbh, $form, $ref->{id}, $required * $ref->{qty}, $ref->{assembly});
+ }
+ $sth->finish;
+
+ } else {
+
+ $query = qq|SELECT partnumber, description, lastcost
+ FROM parts
+ WHERE id = $parts_id|;
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+ $ref = $sth->fetchrow_hashref(NAME_lc);
+ for (keys %$ref) { $form->{orderitems}{$parts_id}{$_} = $ref->{$_} }
+ $sth->finish;
+
+ $form->{orderitems}{$parts_id}{required} += $required;
+
+ $query = qq|SELECT pv.partnumber, pv.leadtime, pv.lastcost, pv.curr,
+ pv.vendor_id, v.name
+ FROM partsvendor pv
+ JOIN vendor v ON (v.id = pv.vendor_id)
+ WHERE pv.parts_id = ?|;
+ $sth = $dbh->prepare($query) || $form->dberror($query);
+
+ # get cost and vendor
+ $sth->execute($parts_id);
+
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+ for (keys %$ref) { $form->{orderitems}{$parts_id}{partsvendor}{$ref->{vendor_id}}{$_} = $ref->{$_} }
+ }
+ $sth->finish;
+
+ }
+
+}
+
+
+sub generate_orders {
+ my ($self, $myconfig, $form) = @_;
+
+ my $i;
+ my %a;
+ my $query;
+ my $sth;
+
+ for ($i = 1; $i <= $form->{rowcount}; $i++) {
+ for (qw(qty lastcost)) { $form->{"${_}_$i"} = $form->parse_amount($myconfig, $form->{"${_}_$i"}) }
+
+ if ($form->{"qty_$i"}) {
+ ($vendor, $vendor_id) = split /--/, $form->{"vendor_$i"};
+ if ($vendor_id) {
+ $a{$vendor_id}{$form->{"id_$i"}}{qty} += $form->{"qty_$i"};
+ for (qw(curr lastcost)) { $a{$vendor_id}{$form->{"id_$i"}}{$_} = $form->{"${_}_$i"} }
+ }
+ }
+ }
+
+ # connect to database
+ my $dbh = $form->dbconnect_noauto($myconfig);
+
+ # foreign exchange rates
+ &exchangerate_defaults($dbh, $form);
+
+ my $amount;
+ my $netamount;
+ my $curr = "";
+ my %tax;
+ my $taxincluded = 0;
+ my $vendor_id;
+
+ my $description;
+ my $unit;
+
+ my $sellprice;
+
+ foreach $vendor_id (keys %a) {
+
+ %tax = ();
+
+ $query = qq|SELECT v.curr, v.taxincluded, t.rate, c.accno
+ FROM vendor v
+ LEFT JOIN vendortax vt ON (v.id = vt.vendor_id)
+ LEFT JOIN tax t ON (t.chart_id = vt.chart_id)
+ LEFT JOIN chart c ON (c.id = t.chart_id)
+ WHERE v.id = $vendor_id|;
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+ $curr = $ref->{curr};
+ $taxincluded = $ref->{taxincluded};
+ $tax{$ref->{accno}} = $ref->{rate};
+ }
+ $sth->finish;
+
+ $curr ||= $form->{defaultcurrency};
+ $taxincluded *= 1;
+
+ my $uid = localtime;
+ $uid .= "$$";
+
+ $query = qq|INSERT INTO oe (ordnumber)
+ VALUES ('$uid')|;
+ $dbh->do($query) || $form->dberror($query);
+
+ $query = qq|SELECT id FROM oe
+ WHERE ordnumber = '$uid'|;
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+ my ($id) = $sth->fetchrow_array;
+ $sth->finish;
+
+ $amount = 0;
+ $netamount = 0;
+
+ foreach my $parts_id (keys %{ $a{$vendor_id} }) {
+
+ if (($form->{$curr} * $form->{$a{$vendor_id}{$parts_id}{curr}}) > 0) {
+ $sellprice = $a{$vendor_id}{$parts_id}{lastcost} / $form->{$curr} * $form->{$a{$vendor_id}{$parts_id}{curr}};
+ } else {
+ $sellprice = $a{$vendor_id}{$parts_id}{lastcost};
+ }
+ $sellprice = $form->round_amount($sellprice, 2);
+
+ my $linetotal = $form->round_amount($sellprice * $a{$vendor_id}{$parts_id}{qty}, 2);
+
+ $query = qq|SELECT p.description, p.unit, c.accno FROM parts p
+ LEFT JOIN partstax pt ON (p.id = pt.parts_id)
+ LEFT JOIN chart c ON (c.id = pt.chart_id)
+ WHERE p.id = $parts_id|;
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ my $rate = 0;
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+ $description = $ref->{description};
+ $unit = $ref->{unit};
+ $rate += $tax{$ref->{accno}};
+ }
+ $sth->finish;
+
+ $netamount += $linetotal;
+ if ($taxincluded) {
+ $amount += $linetotal;
+ } else {
+ $amount += $form->round_amount($linetotal * (1 + $rate), 2);
+ }
+
+ $description = $dbh->quote($description);
+ $unit = $dbh->quote($unit);
+
+ $query = qq|INSERT INTO orderitems (trans_id, parts_id, description,
+ qty, ship, sellprice, unit) VALUES
+ ($id, $parts_id, $description,
+ $a{$vendor_id}{$parts_id}{qty}, 0, $sellprice, $unit)|;
+ $dbh->do($query) || $form->dberror($query);
+
+ }
+
+ my $ordnumber = $form->update_defaults($myconfig, 'ponumber');
+
+ my $null;
+ my $employee_id;
+ my $department_id;
+
+ ($null, $employee_id) = $form->get_employee($dbh);
+ ($null, $department_id) = split /--/, $form->{department};
+ $department_id *= 1;
+
+ $query = qq|UPDATE oe SET
+ ordnumber = |.$dbh->quote($ordnumber).qq|,
+ transdate = current_date,
+ vendor_id = $vendor_id,
+ customer_id = 0,
+ amount = $amount,
+ netamount = $netamount,
+ taxincluded = '$taxincluded',
+ curr = '$curr',
+ employee_id = $employee_id,
+ department_id = '$department_id',
+ ponumber = |.$dbh->quote($form->{ponumber}).qq|
+ WHERE id = $id|;
+ $dbh->do($query) || $form->dberror($query);
+
+ }
+
+ my $rc = $dbh->commit;
+ $dbh->disconnect;
+
+ $rc;
+
+}
+
+
+sub consolidate_orders {
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect_noauto($myconfig);
+
+ my $i;
+ my $id;
+ my $ref;
+ my %oe = ();
+
+ my $query = qq|SELECT * FROM oe
+ WHERE id = ?|;
+ my $sth = $dbh->prepare($query) || $form->dberror($query);
+
+ for ($i = 1; $i <= $form->{rowcount}; $i++) {
+ # retrieve order
+ if ($form->{"ndx_$i"}) {
+ $sth->execute($form->{"ndx_$i"});
+
+ $ref = $sth->fetchrow_hashref(NAME_lc);
+ $ref->{ndx} = $i;
+ $oe{oe}{$ref->{curr}}{$ref->{id}} = $ref;
+
+ $oe{vc}{$ref->{curr}}{$ref->{"$form->{vc}_id"}}++;
+ $sth->finish;
+ }
+ }
+
+ $query = qq|SELECT * FROM orderitems
+ WHERE trans_id = ?|;
+ $sth = $dbh->prepare($query) || $form->dberror($query);
+
+ foreach $curr (keys %{ $oe{oe} }) {
+
+ foreach $id (sort { $oe{oe}{$curr}{$a}->{ndx} <=> $oe{oe}{$curr}{$b}->{ndx} } keys %{ $oe{oe}{$curr} }) {
+
+ # retrieve order
+ $vc_id = $oe{oe}{$curr}{$id}->{"$form->{vc}_id"};
+
+ if ($oe{vc}{$oe{oe}{$curr}{$id}->{curr}}{$vc_id} > 1) {
+
+ push @{ $oe{orders}{$curr}{$vc_id} }, $id;
+
+ $sth->execute($id);
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+ push @{ $oe{orderitems}{$curr}{$id} }, $ref;
+ }
+ $sth->finish;
+
+ }
+ }
+ }
+
+
+ my $ordnumber = $form->{ordnumber};
+ my $numberfld = ($form->{vc} eq 'customer') ? 'sonumber' : 'ponumber';
+
+ my ($department, $department_id) = $form->{department};
+ $department_id *= 1;
+
+ my $uid = localtime;
+ $uid .= "$$";
+
+ my @orderitems = ();
+
+ foreach $curr (keys %{ $oe{orders} }) {
+
+ foreach $vc_id (sort { $a <=> $b } keys %{ $oe{orders}{$curr} }) {
+ # the orders
+ @orderitems = ();
+ $form->{customer_id} = $form->{vendor_id} = 0;
+ $form->{"$form->{vc}_id"} = $vc_id;
+ $amount = 0;
+ $netamount = 0;
+
+ foreach $id (@{ $oe{orders}{$curr}{$vc_id} }) {
+
+ # header
+ $ref = $oe{oe}{$curr}{$id};
+
+ $amount += $ref->{amount};
+ $netamount += $ref->{netamount};
+
+ foreach $item (@{ $oe{orderitems}{$curr}{$id} }) {
+ push @orderitems, $item;
+ }
+
+ # close order
+ $query = qq|UPDATE oe SET
+ closed = '1'
+ WHERE id = $id|;
+ $dbh->do($query) || $form->dberror($query);
+
+ # reset shipped
+ $query = qq|UPDATE orderitems SET
+ ship = 0
+ WHERE trans_id = $id|;
+ $dbh->do($query) || $form->dberror($query);
+
+ }
+
+ $ordnumber ||= $form->update_defaults($myconfig, $numberfld, $dbh);
+
+ $query = qq|INSERT INTO oe (ordnumber)
+ VALUES ($uid)|;
+ $dbh->do($query) || $form->dberror($query);
+
+ $query = qq|SELECT id
+ FROM oe
+ WHERE ordnumber = '$uid'|;
+ ($id) = $dbh->selectrow_array($query);
+
+ $ref->{employee_id} *= 1;
+
+ $query = qq|UPDATE oe SET
+ ordnumber = |.$dbh->quote($ordnumber).qq|,
+ transdate = current_date,
+ vendor_id = $form->{vendor_id},
+ customer_id = $form->{customer_id},
+ amount = $amount,
+ netamount = $netamount,
+ reqdate = |.$form->dbquote($ref->{reqdate}, SQL_DATE).qq|,
+ taxincluded = '$ref->{taxincluded}',
+ shippingpoint = |.$dbh->quote($ref->{shippingpoint}).qq|,
+ notes = |.$dbh->quote($ref->{notes}).qq|,
+ curr = '$curr',
+ employee_id = $ref->{employee_id},
+ intnotes = |.$dbh->quote($ref->{intnotes}).qq|,
+ shipvia = |.$dbh->quote($ref->{shipvia}).qq|,
+ language_code = '$ref->{language_code}',
+ ponumber = |.$dbh->quote($form->{ponumber}).qq|,
+ department_id = $department_id
+ WHERE id = $id|;
+ $dbh->do($query) || $form->dberror($query);
+
+
+ # add items
+ foreach $item (@orderitems) {
+ for (qw(qty sellprice discount project_id ship)) { $item->{$_} *= 1 }
+ $query = qq|INSERT INTO orderitems (
+ trans_id, parts_id, description,
+ qty, sellprice, discount,
+ unit, reqdate, project_id,
+ ship, serialnumber, notes)
+ VALUES (
+ $id, $item->{parts_id}, |.$dbh->quote($item->{description}).qq|,
+ $item->{qty}, $item->{sellprice}, $item->{discount},
+ |.$dbh->quote($item->{unit}).qq|, |.$form->dbquote($item->{reqdate}, SQL_DATE).qq|, $item->{project_id},
+ $item->{ship}, |.$dbh->quote($item->{serialnumber}).qq|, |.$dbh->quote($item->{notes}).qq|)|;
+
+ $dbh->do($query) || $form->dberror($query);
+
+ }
+ }
+ }
+
+
+ $rc = $dbh->commit;
+ $dbh->disconnect;
+
+ $rc;
+
+}
+
+
+1;
+
diff --git a/LedgerSMB/OP.pm b/LedgerSMB/OP.pm
new file mode 100755
index 00000000..65b8da31
--- /dev/null
+++ b/LedgerSMB/OP.pm
@@ -0,0 +1,101 @@
+#=====================================================================
+# LedgerSMB
+# Small Medium Business Accounting software
+#
+# See COPYRIGHT file for copyright information
+#======================================================================
+#
+# This file has NOT undergone whitespace cleanup.
+#
+#======================================================================
+#
+# Overpayment function
+# used in AR, AP, IS, IR, OE, CP
+#
+#======================================================================
+
+package OP;
+
+sub overpayment {
+ my ($self, $myconfig, $form, $dbh, $amount, $ml) = @_;
+
+ my $fxamount = $form->round_amount($amount * $form->{exchangerate}, 2);
+ my ($paymentaccno) = split /--/, $form->{account};
+
+ my ($null, $department_id) = split /--/, $form->{department};
+ $department_id *= 1;
+
+ my $uid = localtime;
+ $uid .= "$$";
+
+ # add AR/AP header transaction with a payment
+ $query = qq|INSERT INTO $form->{arap} (invnumber, employee_id)
+ VALUES ('$uid', (SELECT id FROM employee
+ WHERE login = '$form->{login}'))|;
+ $dbh->do($query) || $form->dberror($query);
+
+ $query = qq|SELECT id FROM $form->{arap}
+ WHERE invnumber = '$uid'|;
+ ($uid) = $dbh->selectrow_array($query);
+
+ my $invnumber = $form->{invnumber};
+ $invnumber = $form->update_defaults($myconfig, ($form->{arap} eq 'ar') ? "sinumber" : "vinumber", $dbh) unless $invnumber;
+
+ $query = qq|UPDATE $form->{arap} set
+ invnumber = |.$dbh->quote($invnumber).qq|,
+ $form->{vc}_id = $form->{"$form->{vc}_id"},
+ transdate = '$form->{datepaid}',
+ datepaid = '$form->{datepaid}',
+ duedate = '$form->{datepaid}',
+ netamount = 0,
+ amount = 0,
+ paid = $fxamount,
+ curr = '$form->{currency}',
+ department_id = $department_id
+ WHERE id = $uid|;
+ $dbh->do($query) || $form->dberror($query);
+
+ # add AR/AP
+ ($accno) = split /--/, $form->{$form->{ARAP}};
+
+ $query = qq|INSERT INTO acc_trans (trans_id, chart_id, transdate, amount)
+ VALUES ($uid, (SELECT id FROM chart
+ WHERE accno = '$accno'),
+ '$form->{datepaid}', $fxamount * $ml)|;
+ $dbh->do($query) || $form->dberror($query);
+
+ # add payment
+ $query = qq|INSERT INTO acc_trans (trans_id, chart_id, transdate,
+ amount, source, memo)
+ VALUES ($uid, (SELECT id FROM chart
+ WHERE accno = '$paymentaccno'),
+ '$form->{datepaid}', $amount * $ml * -1, |
+ .$dbh->quote($form->{source}).qq|, |
+ .$dbh->quote($form->{memo}).qq|)|;
+ $dbh->do($query) || $form->dberror($query);
+
+ # add exchangerate difference
+ if ($fxamount != $amount) {
+ $query = qq|INSERT INTO acc_trans (trans_id, chart_id, transdate,
+ amount, cleared, fx_transaction, source)
+ VALUES ($uid, (SELECT id FROM chart
+ WHERE accno = '$paymentaccno'),
+ '$form->{datepaid}', ($fxamount - $amount) * $ml * -1,
+ '1', '1', |
+ .$dbh->quote($form->{source}).qq|)|;
+ $dbh->do($query) || $form->dberror($query);
+ }
+
+ my %audittrail = ( tablename => $form->{arap},
+ reference => $invnumber,
+ formname => ($form->{arap} eq 'ar') ? 'deposit' : 'pre-payment',
+ action => 'posted',
+ id => $uid );
+
+ $form->audittrail($dbh, "", \%audittrail);
+
+}
+
+
+1;
+
diff --git a/LedgerSMB/PE.pm b/LedgerSMB/PE.pm
new file mode 100755
index 00000000..d85f4cc3
--- /dev/null
+++ b/LedgerSMB/PE.pm
@@ -0,0 +1,1499 @@
+#=====================================================================
+# LedgerSMB
+# Small Medium Business Accounting software
+#
+# See COPYRIGHT file for copyright information
+#======================================================================
+#
+# This file has NOT undergone whitespace cleanup.
+#
+#======================================================================
+#
+# Project module
+# also used for partsgroups
+#
+#======================================================================
+
+package PE;
+
+
+sub projects {
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ $form->{sort} = "projectnumber" unless $form->{sort};
+ my @a = ($form->{sort});
+ my %ordinal = ( projectnumber => 2,
+ description => 3,
+ startdate => 4,
+ enddate => 5,
+ );
+ my $sortorder = $form->sort_order(\@a, \%ordinal);
+
+ my $query;
+ my $where = "WHERE 1=1";
+
+ $query = qq|SELECT pr.*, c.name
+ FROM project pr
+ LEFT JOIN customer c ON (c.id = pr.customer_id)|;
+
+ if ($form->{type} eq 'job') {
+ $where .= qq| AND pr.id NOT IN (SELECT DISTINCT id
+ FROM parts
+ WHERE project_id > 0)|;
+ }
+
+ my $var;
+ if ($form->{projectnumber} ne "") {
+ $var = $form->like(lc $form->{projectnumber});
+ $where .= " AND lower(pr.projectnumber) LIKE '$var'";
+ }
+ if ($form->{description} ne "") {
+ $var = $form->like(lc $form->{description});
+ $where .= " AND lower(pr.description) LIKE '$var'";
+ }
+
+ ($form->{startdatefrom}, $form->{startdateto}) = $form->from_to($form->{year}, $form->{month}, $form->{interval}) if $form->{year} && $form->{month};
+
+ if ($form->{startdatefrom}) {
+ $where .= " AND (pr.startdate IS NULL OR pr.startdate >= '$form->{startdatefrom}')";
+ }
+ if ($form->{startdateto}) {
+ $where .= " AND (pr.startdate IS NULL OR pr.startdate <= '$form->{startdateto}')";
+ }
+
+ if ($form->{status} eq 'orphaned') {
+ $where .= qq| AND pr.id NOT IN (SELECT DISTINCT project_id
+ FROM acc_trans
+ WHERE project_id > 0
+ UNION
+ SELECT DISTINCT project_id
+ FROM invoice
+ WHERE project_id > 0
+ UNION
+ SELECT DISTINCT project_id
+ FROM orderitems
+ WHERE project_id > 0
+ UNION
+ SELECT DISTINCT project_id
+ FROM jcitems
+ WHERE project_id > 0)
+ |;
+
+ }
+ if ($form->{status} eq 'active') {
+ $where .= qq| AND (pr.enddate IS NULL OR pr.enddate >= current_date)|;
+ }
+ if ($form->{status} eq 'inactive') {
+ $where .= qq| AND pr.enddate <= current_date|;
+ }
+
+ $query .= qq|
+ $where
+ ORDER BY $sortorder|;
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ my $i = 0;
+ while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
+ push @{ $form->{all_project} }, $ref;
+ $i++;
+ }
+
+ $sth->finish;
+ $dbh->disconnect;
+
+ $i;
+
+}
+
+
+sub get_project {
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ my $query;
+ my $sth;
+ my $ref;
+ my $where;
+
+ if ($form->{id}) {
+
+ $where = "WHERE pr.id = $form->{id}" if $form->{id};
+
+ $query = qq|SELECT pr.*,
+ c.name AS customer
+ FROM project pr
+ LEFT JOIN customer c ON (c.id = pr.customer_id)
+ $where|;
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ $ref = $sth->fetchrow_hashref(NAME_lc);
+
+ for (keys %$ref) { $form->{$_} = $ref->{$_} }
+
+ $sth->finish;
+
+ # check if it is orphaned
+ $query = qq|SELECT count(*)
+ FROM acc_trans
+ WHERE project_id = $form->{id}
+ UNION
+ SELECT count(*)
+ FROM invoice
+ WHERE project_id = $form->{id}
+ UNION
+ SELECT count(*)
+ FROM orderitems
+ WHERE project_id = $form->{id}
+ UNION
+ SELECT count(*)
+ FROM jcitems
+ WHERE project_id = $form->{id}
+ |;
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ my $count;
+ while (($count) = $sth->fetchrow_array) {
+ $form->{orphaned} += $count;
+ }
+ $sth->finish;
+ $form->{orphaned} = !$form->{orphaned};
+ }
+
+ PE->get_customer($myconfig, $form, $dbh);
+
+ $dbh->disconnect;
+
+}
+
+
+sub save_project {
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ $form->{customer_id} ||= 'NULL';
+
+ $form->{projectnumber} = $form->update_defaults($myconfig, "projectnumber", $dbh) unless $form->{projectnumber};
+
+ if ($form->{id}) {
+
+ $query = qq|UPDATE project SET
+ projectnumber = |.$dbh->quote($form->{projectnumber}).qq|,
+ description = |.$dbh->quote($form->{description}).qq|,
+ startdate = |.$form->dbquote($form->{startdate}, SQL_DATE).qq|,
+ enddate = |.$form->dbquote($form->{enddate}, SQL_DATE).qq|,
+ customer_id = $form->{customer_id}
+ WHERE id = $form->{id}|;
+ } else {
+
+ $query = qq|INSERT INTO project
+ (projectnumber, description, startdate, enddate, customer_id)
+ VALUES (|
+ .$dbh->quote($form->{projectnumber}).qq|, |
+ .$dbh->quote($form->{description}).qq|, |
+ .$form->dbquote($form->{startdate}, SQL_DATE).qq|, |
+ .$form->dbquote($form->{enddate}, SQL_DATE).qq|,
+ $form->{customer_id}
+ )|;
+ }
+ $dbh->do($query) || $form->dberror($query);
+
+ $dbh->disconnect;
+
+}
+
+
+sub list_stock {
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ my $var;
+ my $where = "1 = 1";
+
+ if ($form->{status} eq 'active') {
+ $where = qq|(pr.enddate IS NULL
+ OR pr.enddate >= current_date)
+ AND pr.completed < pr.production|;
+ }
+ if ($form->{status} eq 'inactive') {
+ $where = qq|pr.completed = pr.production|;
+ }
+
+ if ($form->{projectnumber}) {
+ $var = $form->like(lc $form->{projectnumber});
+ $where .= " AND lower(pr.projectnumber) LIKE '$var'";
+ }
+
+ if ($form->{description}) {
+ $var = $form->like(lc $form->{description});
+ $where .= " AND lower(pr.description) LIKE '$var'";
+ }
+
+ $form->{sort} = "projectnumber" unless $form->{sort};
+ my @a = ($form->{sort});
+ my %ordinal = ( projectnumber => 2,
+ description => 3
+ );
+ my $sortorder = $form->sort_order(\@a, \%ordinal);
+
+ my $query = qq|SELECT pr.*, p.partnumber
+ FROM project pr
+ JOIN parts p ON (p.id = pr.parts_id)
+ WHERE $where
+ ORDER BY $sortorder|;
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
+ push @{ $form->{all_project} }, $ref;
+ }
+ $sth->finish;
+
+ $query = qq|SELECT current_date FROM defaults|;
+ ($form->{stockingdate}) = $dbh->selectrow_array($query) if !$form->{stockingdate};
+
+ $dbh->disconnect;
+
+}
+
+
+sub jobs {
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ $form->{sort} = "projectnumber" unless $form->{sort};
+ my @a = ($form->{sort});
+ my %ordinal = ( projectnumber => 2,
+ description => 3,
+ startdate => 4,
+ );
+ my $sortorder = $form->sort_order(\@a, \%ordinal);
+
+ my $query = qq|SELECT pr.*, p.partnumber, p.onhand, c.name
+ FROM project pr
+ JOIN parts p ON (p.id = pr.parts_id)
+ LEFT JOIN customer c ON (c.id = pr.customer_id)
+ WHERE 1=1|;
+
+ if ($form->{projectnumber} ne "") {
+ $var = $form->like(lc $form->{projectnumber});
+ $query .= " AND lower(pr.projectnumber) LIKE '$var'";
+ }
+ if ($form->{description} ne "") {
+ $var = $form->like(lc $form->{description});
+ $query .= " AND lower(pr.description) LIKE '$var'";
+ }
+
+ ($form->{startdatefrom}, $form->{startdateto}) = $form->from_to($form->{year}, $form->{month}, $form->{interval}) if $form->{year} && $form->{month};
+
+ if ($form->{startdatefrom}) {
+ $query .= " AND pr.startdate >= '$form->{startdatefrom}'";
+ }
+ if ($form->{startdateto}) {
+ $query .= " AND pr.startdate <= '$form->{startdateto}'";
+ }
+
+ if ($form->{status} eq 'active') {
+ $query .= qq| AND NOT pr.production = pr.completed|;
+ }
+ if ($form->{status} eq 'inactive') {
+ $query .= qq| AND pr.production = pr.completed|;
+ }
+ if ($form->{status} eq 'orphaned') {
+ $query .= qq| AND pr.completed = 0
+ AND (pr.id NOT IN SELECT DISTINCT project_id
+ FROM invoice
+ WHERE project_id > 0)
+ UNION
+ SELECT DISTINCT project_id
+ FROM orderitems
+ WHERE project_id > 0
+ SELECT DISTINCT project_id
+ FROM jcitems
+ WHERE project_id > 0
+ )|;
+ }
+
+ $query .= qq|
+ ORDER BY $sortorder|;
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
+ push @{ $form->{all_project} }, $ref;
+ }
+
+ $sth->finish;
+
+ $dbh->disconnect;
+
+}
+
+
+sub get_job {
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ my $query;
+ my $sth;
+ my $ref;
+
+ if ($form->{id}) {
+ $query = qq|SELECT weightunit
+ FROM defaults|;
+ ($form->{weightunit}) = $dbh->selectrow_array($query);
+
+ $query = qq|SELECT pr.*,
+ p.partnumber, p.description AS partdescription, p.unit, p.listprice,
+ p.sellprice, p.priceupdate, p.weight, p.notes, p.bin,
+ p.partsgroup_id,
+ ch.accno AS income_accno, ch.description AS income_description,
+ pr.customer_id, c.name AS customer,
+ pg.partsgroup
+ FROM project pr
+ LEFT JOIN parts p ON (p.id = pr.parts_id)
+ LEFT JOIN chart ch ON (ch.id = p.income_accno_id)
+ LEFT JOIN customer c ON (c.id = pr.customer_id)
+ LEFT JOIN partsgroup pg ON (pg.id = p.partsgroup_id)
+ WHERE pr.id = $form->{id}|;
+ } else {
+ $query = qq|SELECT weightunit, current_date AS startdate FROM defaults|;
+ }
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ $ref = $sth->fetchrow_hashref(NAME_lc);
+
+ for (keys %$ref) { $form->{$_} = $ref->{$_} }
+
+ $sth->finish;
+
+ if ($form->{id}) {
+ # check if it is orphaned
+ $query = qq|SELECT count(*)
+ FROM invoice
+ WHERE project_id = $form->{id}
+ UNION
+ SELECT count(*)
+ FROM orderitems
+ WHERE project_id = $form->{id}
+ UNION
+ SELECT count(*)
+ FROM jcitems
+ WHERE project_id = $form->{id}
+ |;
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ my $count;
+ while (($count) = $sth->fetchrow_array) {
+ $form->{orphaned} += $count;
+ }
+ $sth->finish;
+
+ }
+
+ $form->{orphaned} = !$form->{orphaned};
+
+ $query = qq|SELECT accno, description, link
+ FROM chart
+ WHERE link LIKE '%IC%'
+ ORDER BY accno|;
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+ for (split /:/, $ref->{link}) {
+ if (/IC/) {
+ push @{ $form->{IC_links}{$_} }, { accno => $ref->{accno},
+ description => $ref->{description} };
+ }
+ }
+ }
+ $sth->finish;
+
+ if ($form->{id}) {
+ $query = qq|SELECT ch.accno
+ FROM parts p
+ JOIN partstax pt ON (pt.parts_id = p.id)
+ JOIN chart ch ON (pt.chart_id = ch.id)
+ WHERE p.id = $form->{id}|;
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+ $form->{amount}{$ref->{accno}} = $ref->{accno};
+ }
+ $sth->finish;
+ }
+
+ PE->get_customer($myconfig, $form, $dbh);
+
+ $dbh->disconnect;
+
+}
+
+
+sub get_customer {
+ my ($self, $myconfig, $form, $dbh) = @_;
+
+ my $disconnect = 0;
+
+ if (! $dbh) {
+ $dbh = $form->dbconnect($myconfig);
+ $disconnect = 1;
+ }
+
+ my $query;
+ my $sth;
+ my $ref;
+
+ if (! $form->{startdate}) {
+ $query = qq|SELECT current_date FROM defaults|;
+ ($form->{startdate}) = $dbh->selectrow_array($query);
+ }
+
+ my $where = qq|(startdate >= '$form->{startdate}' OR startdate IS NULL OR enddate IS NULL)|;
+
+ if ($form->{enddate}) {
+ $where .= qq| AND (enddate >= '$form->{enddate}' OR enddate IS NULL)|;
+ } else {
+ $where .= qq| AND (enddate >= current_date OR enddate IS NULL)|;
+ }
+
+ $query = qq|SELECT count(*)
+ FROM customer
+ WHERE $where|;
+ my ($count) = $dbh->selectrow_array($query);
+
+ if ($count < $myconfig->{vclimit}) {
+ $query = qq|SELECT id, name
+ FROM customer
+ WHERE $where|;
+
+ if ($form->{customer_id}) {
+ $query .= qq|
+ UNION SELECT id,name
+ FROM customer
+ WHERE id = $form->{customer_id}|;
+ }
+
+ $query .= qq|
+ ORDER BY name|;
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ @{ $form->{all_customer} } = ();
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+ push @{ $form->{all_customer} }, $ref;
+ }
+ $sth->finish;
+ }
+
+ $dbh->disconnect if $disconnect;
+
+}
+
+
+sub save_job {
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect_noauto($myconfig);
+
+ my ($income_accno) = split /--/, $form->{IC_income};
+
+ my ($partsgroup, $partsgroup_id) = split /--/, $form->{partsgroup};
+ $partsgroup_id ||= 'NULL';
+
+ if ($form->{id}) {
+ $query = qq|SELECT id FROM project
+ WHERE id = $form->{id}|;
+ ($form->{id}) = $dbh->selectrow_array($query);
+ }
+
+ if (!$form->{id}) {
+ my $uid = localtime;
+ $uid .= "$$";
+
+ $query = qq|INSERT INTO project (projectnumber)
+ VALUES ('$uid')|;
+ $dbh->do($query) || $form->dberror($query);
+
+ $query = qq|SELECT id FROM project
+ WHERE projectnumber = '$uid'|;
+ ($form->{id}) = $dbh->selectrow_array($query);
+ }
+
+ $form->{projectnumber} = $form->update_defaults($myconfig, "projectnumber", $dbh) unless $form->{projectnumber};
+
+ $query = qq|UPDATE project SET
+ projectnumber = |.$dbh->quote($form->{projectnumber}).qq|,
+ description = |.$dbh->quote($form->{description}).qq|,
+ startdate = |.$form->dbquote($form->{startdate}, SQL_DATE).qq|,
+ enddate = |.$form->dbquote($form->{enddate}, SQL_DATE).qq|,
+ parts_id = $form->{id},
+ production = |.$form->parse_amount($myconfig, $form->{production}).qq|,
+ customer_id = $form->{customer_id}
+ WHERE id = $form->{id}|;
+ $dbh->do($query) || $form->dberror($query);
+
+
+ #### add/edit assembly
+ $query = qq|SELECT id FROM parts
+ WHERE id = $form->{id}|;
+ my ($id) = $dbh->selectrow_array($query);
+
+ if (!$id) {
+ $query = qq|INSERT INTO parts (id)
+ VALUES ($form->{id})|;
+ $dbh->do($query) || $form->dberror($query);
+ }
+
+ my $partnumber = ($form->{partnumber}) ? $form->{partnumber} : $form->{projectnumber};
+
+ $query = qq|UPDATE parts SET
+ partnumber = |.$dbh->quote($partnumber).qq|,
+ description = |.$dbh->quote($form->{partdescription}).qq|,
+ priceupdate = |.$form->dbquote($form->{priceupdate}, SQL_DATE).qq|,
+ listprice = |.$form->parse_amount($myconfig, $form->{listprice}).qq|,
+ sellprice = |.$form->parse_amount($myconfig, $form->{sellprice}).qq|,
+ weight = |.$form->parse_amount($myconfig, $form->{weight}).qq|,
+ bin = '$form->{bin}',
+ unit = |.$dbh->quote($form->{unit}).qq|,
+ notes = |.$dbh->quote($form->{notes}).qq|,
+ income_accno_id = (SELECT id FROM chart
+ WHERE accno = '$income_accno'),
+ partsgroup_id = $partsgroup_id,
+ assembly = '1',
+ obsolete = '1',
+ project_id = $form->{id}
+ WHERE id = $form->{id}|;
+
+ $dbh->do($query) || $form->dberror($query);
+
+ $query = qq|DELETE FROM partstax
+ WHERE parts_id = $form->{id}|;
+ $dbh->do($query) || $form->dberror($query);
+
+ for (split / /, $form->{taxaccounts}) {
+ if ($form->{"IC_tax_$_"}) {
+ $query = qq|INSERT INTO partstax (parts_id, chart_id)
+ VALUES ($form->{id},
+ (SELECT id
+ FROM chart
+ WHERE accno = '$_'))|;
+ $dbh->do($query) || $form->dberror($query);
+ }
+ }
+
+ $dbh->commit;
+ $dbh->disconnect;
+
+}
+
+
+sub stock_assembly {
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect_noauto($myconfig);
+
+ my $ref;
+
+ my $query = qq|SELECT *
+ FROM project
+ WHERE id = ?|;
+ my $sth = $dbh->prepare($query) || $form->dberror($query);
+
+ $query = qq|SELECT COUNT(*)
+ FROM parts
+ WHERE project_id = ?|;
+ my $rvh = $dbh->prepare($query) || $form->dberror($query);
+
+ if (! $form->{stockingdate}) {
+ $query = qq|SELECT current_date FROM defaults|;
+ ($form->{stockingdate}) = $dbh->selectrow_array($query);
+ }
+
+ $query = qq|SELECT *
+ FROM parts
+ WHERE id = ?|;
+ my $pth = $dbh->prepare($query) || $form->dberror($query);
+
+ $query = qq|SELECT j.*, p.lastcost FROM jcitems j
+ JOIN parts p ON (p.id = j.parts_id)
+ WHERE j.project_id = ?
+ AND j.checkedin <= '$form->{stockingdate}'
+ ORDER BY parts_id|;
+ my $jth = $dbh->prepare($query) || $form->dberror($query);
+
+ $query = qq|INSERT INTO assembly (id, parts_id, qty, bom, adj)
+ VALUES (?, ?, ?, '0', '0')|;
+ my $ath = $dbh->prepare($query) || $form->dberror($query);
+
+ my $i = 0;
+ my $sold;
+ my $ship;
+
+ while (1) {
+ $i++;
+ last unless $form->{"id_$i"};
+
+ $stock = $form->parse_amount($myconfig, $form->{"stock_$i"});
+
+ if ($stock) {
+ $sth->execute($form->{"id_$i"});
+ $ref = $sth->fetchrow_hashref(NAME_lc);
+
+ if ($stock > ($ref->{production} - $ref->{completed})) {
+ $stock = $ref->{production} - $ref->{completed};
+ }
+ if (($stock * -1) > $ref->{completed}) {
+ $stock = $ref->{completed} * -1;
+ }
+
+ $pth->execute($form->{"id_$i"});
+ $pref = $pth->fetchrow_hashref(NAME_lc);
+
+ my %assembly = ();
+ my $lastcost = 0;
+ my $sellprice = 0;
+ my $listprice = 0;
+
+ $jth->execute($form->{"id_$i"});
+ while ($jref = $jth->fetchrow_hashref(NAME_lc)) {
+ $assembly{qty}{$jref->{parts_id}} += ($jref->{qty} - $jref->{allocated});
+ $assembly{parts_id}{$jref->{parts_id}} = $jref->{parts_id};
+ $assembly{jcitems}{$jref->{id}} = $jref->{id};
+ $lastcost += $form->round_amount(($jref->{lastcost} * ($jref->{qty} - $jref->{allocated})), 2);
+ $sellprice += $form->round_amount(($jref->{sellprice} * ($jref->{qty} - $jref->{allocated})), 2);
+ $listprice += $form->round_amount(($jref->{listprice} * ($jref->{qty} - $jref->{allocated})), 2);
+ }
+ $jth->finish;
+
+ $uid = localtime;
+ $uid .= "$$";
+
+ $query = qq|INSERT INTO parts (partnumber)
+ VALUES ('$uid')|;
+ $dbh->do($query) || $form->dberror($query);
+
+ $query = qq|SELECT id
+ FROM parts
+ WHERE partnumber = '$uid'|;
+ ($uid) = $dbh->selectrow_array($query);
+
+ $lastcost = $form->round_amount($lastcost / $stock, 2);
+ $sellprice = ($pref->{sellprice}) ? $pref->{sellprice} : $form->round_amount($sellprice / $stock, 2);
+ $listprice = ($pref->{listprice}) ? $pref->{listprice} : $form->round_amount($listprice / $stock, 2);
+
+ $rvh->execute($form->{"id_$i"});
+ my ($rev) = $rvh->fetchrow_array;
+ $rvh->finish;
+
+ $query = qq|UPDATE parts SET
+ partnumber = '$pref->{partnumber}-$rev',
+ description = '$pref->{partdescription}',
+ priceupdate = '$form->{stockingdate}',
+ unit = '$pref->{unit}',
+ listprice = $listprice,
+ sellprice = $sellprice,
+ lastcost = $lastcost,
+ weight = $pref->{weight},
+ onhand = $stock,
+ notes = '$pref->{notes}',
+ assembly = '1',
+ income_accno_id = $pref->{income_accno_id},
+ bin = '$pref->{bin}',
+ project_id = $form->{"id_$i"}
+ WHERE id = $uid|;
+ $dbh->do($query) || $form->dberror($query);
+
+ $query = qq|INSERT INTO partstax (parts_id, chart_id)
+ SELECT '$uid', chart_id FROM partstax
+ WHERE parts_id = $pref->{id}|;
+ $dbh->do($query) || $form->dberror($query);
+
+
+ $pth->finish;
+
+ for (keys %{$assembly{parts_id}}) {
+ if ($assembly{qty}{$_}) {
+ $ath->execute($uid, $assembly{parts_id}{$_}, $form->round_amount($assembly{qty}{$_} / $stock, 4));
+ $ath->finish;
+ }
+ }
+
+ $form->update_balance($dbh,
+ "project",
+ "completed",
+ qq|id = $form->{"id_$i"}|,
+ $stock);
+
+ $query = qq|UPDATE jcitems SET
+ allocated = qty
+ WHERE allocated != qty
+ AND checkedin <= '$form->{stockingdate}'
+ AND project_id = $form->{"id_$i"}|;
+ $dbh->do($query) || $form->dberror($query);
+
+ $sth->finish;
+
+ }
+
+ }
+
+ my $rc = $dbh->commit;
+ $dbh->disconnect;
+
+ $rc;
+
+}
+
+
+sub delete_project {
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect_noauto($myconfig);
+
+ $query = qq|DELETE FROM project
+ WHERE id = $form->{id}|;
+ $dbh->do($query) || $form->dberror($query);
+
+ $query = qq|DELETE FROM translation
+ WHERE trans_id = $form->{id}|;
+ $dbh->do($query) || $form->dberror($query);
+
+ my $rc = $dbh->commit;
+ $dbh->disconnect;
+
+ $rc;
+
+}
+
+
+sub delete_partsgroup {
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect_noauto($myconfig);
+
+ $query = qq|DELETE FROM partsgroup
+ WHERE id = $form->{id}|;
+ $dbh->do($query) || $form->dberror($query);
+
+ $query = qq|DELETE FROM translation
+ WHERE trans_id = $form->{id}|;
+ $dbh->do($query) || $form->dberror($query);
+
+ my $rc = $dbh->commit;
+ $dbh->disconnect;
+
+ $rc;
+
+}
+
+
+sub delete_pricegroup {
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect_noauto($myconfig);
+
+ $query = qq|DELETE FROM pricegroup
+ WHERE id = $form->{id}|;
+ $dbh->do($query) || $form->dberror($query);
+
+ my $rc = $dbh->commit;
+ $dbh->disconnect;
+
+ $rc;
+
+}
+
+
+sub delete_job {
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect_noauto($myconfig);
+
+ my %audittrail = ( tablename => 'project',
+ reference => $form->{id},
+ formname => $form->{type},
+ action => 'deleted',
+ id => $form->{id} );
+
+ $form->audittrail($dbh, "", \%audittrail);
+
+ my $query = qq|DELETE FROM project
+ WHERE id = $form->{id}|;
+ $dbh->do($query) || $form->dberror($query);
+
+ $query = qq|DELETE FROM translation
+ WHERE trans_id = $form->{id}|;
+ $dbh->do($query) || $form->dberror($query);
+
+ # delete all the assemblies
+ $query = qq|DELETE FROM assembly a
+ JOIN parts p ON (a.id = p.id)
+ WHERE p.project_id = $form->{id}|;
+ $dbh->do($query) || $form->dberror($query);
+
+ $query = qq|DELETE FROM parts
+ WHERE project_id = $form->{id}|;
+ $dbh->do($query) || $form->dberror($query);
+
+ my $rc = $dbh->commit;
+ $dbh->disconnect;
+
+ $rc;
+
+}
+
+
+sub partsgroups {
+ my ($self, $myconfig, $form) = @_;
+
+ my $var;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ $form->{sort} = "partsgroup" unless $form->{partsgroup};
+ my @a = (partsgroup);
+ my $sortorder = $form->sort_order(\@a);
+
+ my $query = qq|SELECT g.*
+ FROM partsgroup g|;
+
+ my $where = "1 = 1";
+
+ if ($form->{partsgroup} ne "") {
+ $var = $form->like(lc $form->{partsgroup});
+ $where .= " AND lower(partsgroup) LIKE '$var'";
+ }
+ $query .= qq|
+ WHERE $where
+ ORDER BY $sortorder|;
+
+ if ($form->{status} eq 'orphaned') {
+ $query = qq|SELECT g.*
+ FROM partsgroup g
+ LEFT JOIN parts p ON (p.partsgroup_id = g.id)
+ WHERE $where
+ EXCEPT
+ SELECT g.*
+ FROM partsgroup g
+ JOIN parts p ON (p.partsgroup_id = g.id)
+ WHERE $where
+ ORDER BY $sortorder|;
+ }
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ my $i = 0;
+ while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
+ push @{ $form->{item_list} }, $ref;
+ $i++;
+ }
+
+ $sth->finish;
+ $dbh->disconnect;
+
+ $i;
+
+}
+
+
+sub save_partsgroup {
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ if ($form->{id}) {
+ $query = qq|UPDATE partsgroup SET
+ partsgroup = |.$dbh->quote($form->{partsgroup}).qq|
+ WHERE id = $form->{id}|;
+ } else {
+ $query = qq|INSERT INTO partsgroup
+ (partsgroup)
+ VALUES (|.$dbh->quote($form->{partsgroup}).qq|)|;
+ }
+ $dbh->do($query) || $form->dberror($query);
+
+ $dbh->disconnect;
+
+}
+
+
+sub get_partsgroup {
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ my $query = qq|SELECT *
+ FROM partsgroup
+ WHERE id = $form->{id}|;
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ my $ref = $sth->fetchrow_hashref(NAME_lc);
+
+ for (keys %$ref) { $form->{$_} = $ref->{$_} }
+
+ $sth->finish;
+
+ # check if it is orphaned
+ $query = qq|SELECT count(*)
+ FROM parts
+ WHERE partsgroup_id = $form->{id}|;
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ ($form->{orphaned}) = $sth->fetchrow_array;
+ $form->{orphaned} = !$form->{orphaned};
+
+ $sth->finish;
+
+ $dbh->disconnect;
+
+}
+
+
+sub pricegroups {
+ my ($self, $myconfig, $form) = @_;
+
+ my $var;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ $form->{sort} = "pricegroup" unless $form->{sort};
+ my @a = (pricegroup);
+ my $sortorder = $form->sort_order(\@a);
+
+ my $query = qq|SELECT g.*
+ FROM pricegroup g|;
+
+ my $where = "1 = 1";
+
+ if ($form->{pricegroup} ne "") {
+ $var = $form->like(lc $form->{pricegroup});
+ $where .= " AND lower(pricegroup) LIKE '$var'";
+ }
+ $query .= qq|
+ WHERE $where
+ ORDER BY $sortorder|;
+
+ if ($form->{status} eq 'orphaned') {
+ $query = qq|SELECT g.*
+ FROM pricegroup g
+ WHERE $where
+ AND g.id NOT IN (SELECT DISTINCT pricegroup_id
+ FROM partscustomer
+ WHERE pricegroup_id > 0)
+ ORDER BY $sortorder|;
+ }
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ my $i = 0;
+ while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
+ push @{ $form->{item_list} }, $ref;
+ $i++;
+ }
+
+ $sth->finish;
+ $dbh->disconnect;
+
+ $i;
+
+}
+
+
+sub save_pricegroup {
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ if ($form->{id}) {
+ $query = qq|UPDATE pricegroup SET
+ pricegroup = |.$dbh->quote($form->{pricegroup}).qq|
+ WHERE id = $form->{id}|;
+ } else {
+ $query = qq|INSERT INTO pricegroup
+ (pricegroup)
+ VALUES (|.$dbh->quote($form->{pricegroup}).qq|)|;
+ }
+ $dbh->do($query) || $form->dberror($query);
+
+ $dbh->disconnect;
+
+}
+
+
+sub get_pricegroup {
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ my $query = qq|SELECT *
+ FROM pricegroup
+ WHERE id = $form->{id}|;
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ my $ref = $sth->fetchrow_hashref(NAME_lc);
+
+ for (keys %$ref) { $form->{$_} = $ref->{$_} }
+
+ $sth->finish;
+
+ # check if it is orphaned
+ $query = qq|SELECT count(*)
+ FROM partscustomer
+ WHERE pricegroup_id = $form->{id}|;
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ ($form->{orphaned}) = $sth->fetchrow_array;
+ $form->{orphaned} = !$form->{orphaned};
+
+ $sth->finish;
+
+ $dbh->disconnect;
+
+}
+
+
+sub description_translations {
+ my ($self, $myconfig, $form) = @_;
+
+ my $where = "1 = 1";
+ my $var;
+ my $ref;
+
+ for (qw(partnumber description)) {
+ if ($form->{$_}) {
+ $var = $form->like(lc $form->{$_});
+ $where .= " AND lower(p.$_) LIKE '$var'";
+ }
+ }
+
+ $where .= " AND p.obsolete = '0'";
+ $where .= " AND p.id = $form->{id}" if $form->{id};
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ my %ordinal = ( 'partnumber' => 2,
+ 'description' => 3
+ );
+
+ my @a = qw(partnumber description);
+ my $sortorder = $form->sort_order(\@a, \%ordinal);
+
+ my $query = qq|SELECT l.description AS language, t.description AS translation,
+ l.code
+ FROM translation t
+ JOIN language l ON (l.code = t.language_code)
+ WHERE trans_id = ?
+ ORDER BY 1|;
+ my $tth = $dbh->prepare($query);
+
+ $query = qq|SELECT p.id, p.partnumber, p.description
+ FROM parts p
+ WHERE $where
+ ORDER BY $sortorder|;
+
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ my $tra;
+
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+ push @{ $form->{translations} }, $ref;
+
+ # get translations for description
+ $tth->execute($ref->{id}) || $form->dberror;
+
+ while ($tra = $tth->fetchrow_hashref(NAME_lc)) {
+ $form->{trans_id} = $ref->{id};
+ $tra->{id} = $ref->{id};
+ push @{ $form->{translations} }, $tra;
+ }
+ $tth->finish;
+
+ }
+ $sth->finish;
+
+ &get_language("", $dbh, $form) if $form->{id};
+
+ $dbh->disconnect;
+
+}
+
+
+sub partsgroup_translations {
+ my ($self, $myconfig, $form) = @_;
+
+ my $where = "1 = 1";
+ my $ref;
+ my $var;
+
+ if ($form->{description}) {
+ $var = $form->like(lc $form->{description});
+ $where .= " AND lower(p.partsgroup) LIKE '$var'";
+ }
+ $where .= " AND p.id = $form->{id}" if $form->{id};
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ my $query = qq|SELECT l.description AS language, t.description AS translation,
+ l.code
+ FROM translation t
+ JOIN language l ON (l.code = t.language_code)
+ WHERE trans_id = ?
+ ORDER BY 1|;
+ my $tth = $dbh->prepare($query);
+
+ $form->sort_order();
+
+ $query = qq|SELECT p.id, p.partsgroup AS description
+ FROM partsgroup p
+ WHERE $where
+ ORDER BY 2 $form->{direction}|;
+
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ my $tra;
+
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+ push @{ $form->{translations} }, $ref;
+
+ # get translations for partsgroup
+ $tth->execute($ref->{id}) || $form->dberror;
+
+ while ($tra = $tth->fetchrow_hashref(NAME_lc)) {
+ $form->{trans_id} = $ref->{id};
+ push @{ $form->{translations} }, $tra;
+ }
+ $tth->finish;
+
+ }
+ $sth->finish;
+
+ &get_language("", $dbh, $form) if $form->{id};
+
+ $dbh->disconnect;
+
+}
+
+
+sub project_translations {
+ my ($self, $myconfig, $form) = @_;
+
+ my $where = "1 = 1";
+ my $var;
+ my $ref;
+
+ for (qw(projectnumber description)) {
+ if ($form->{$_}) {
+ $var = $form->like(lc $form->{$_});
+ $where .= " AND lower(p.$_) LIKE '$var'";
+ }
+ }
+
+ $where .= " AND p.id = $form->{id}" if $form->{id};
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ my %ordinal = ( 'projectnumber' => 2,
+ 'description' => 3
+ );
+
+ my @a = qw(projectnumber description);
+ my $sortorder = $form->sort_order(\@a, \%ordinal);
+
+ my $query = qq|SELECT l.description AS language, t.description AS translation,
+ l.code
+ FROM translation t
+ JOIN language l ON (l.code = t.language_code)
+ WHERE trans_id = ?
+ ORDER BY 1|;
+ my $tth = $dbh->prepare($query);
+
+ $query = qq|SELECT p.id, p.projectnumber, p.description
+ FROM project p
+ WHERE $where
+ ORDER BY $sortorder|;
+
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ my $tra;
+
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+ push @{ $form->{translations} }, $ref;
+
+ # get translations for description
+ $tth->execute($ref->{id}) || $form->dberror;
+
+ while ($tra = $tth->fetchrow_hashref(NAME_lc)) {
+ $form->{trans_id} = $ref->{id};
+ $tra->{id} = $ref->{id};
+ push @{ $form->{translations} }, $tra;
+ }
+ $tth->finish;
+
+ }
+ $sth->finish;
+
+ &get_language("", $dbh, $form) if $form->{id};
+
+ $dbh->disconnect;
+
+}
+
+
+sub get_language {
+ my ($self, $dbh, $form) = @_;
+
+ # get language
+ my $query = qq|SELECT *
+ FROM language
+ ORDER BY 2|;
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
+ push @{ $form->{all_language} }, $ref;
+ }
+ $sth->finish;
+
+}
+
+
+sub save_translation {
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect_noauto($myconfig);
+
+ my $query = qq|DELETE FROM translation
+ WHERE trans_id = $form->{id}|;
+ $dbh->do($query) || $form->dberror($query);
+
+ $query = qq|INSERT INTO translation (trans_id, language_code, description)
+ VALUES ($form->{id}, ?, ?)|;
+ my $sth = $dbh->prepare($query) || $form->dberror($query);
+
+ foreach my $i (1 .. $form->{translation_rows}) {
+ if ($form->{"language_code_$i"} ne "") {
+ $sth->execute($form->{"language_code_$i"}, $form->{"translation_$i"});
+ $sth->finish;
+ }
+ }
+ $dbh->commit;
+ $dbh->disconnect;
+
+}
+
+
+sub delete_translation {
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ my $query = qq|DELETE FROM translation
+ WHERE trans_id = $form->{id}|;
+ $dbh->do($query) || $form->dberror($query);
+
+ $dbh->disconnect;
+
+}
+
+
+sub project_sales_order {
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ my $query = qq|SELECT current_date FROM defaults|;
+ my ($transdate) = $dbh->selectrow_array($query);
+
+ $form->all_years($myconfig, $dbh);
+
+ $form->all_projects($myconfig, $dbh, $transdate);
+
+ $form->all_employees($myconfig, $dbh, $transdate);
+
+ $dbh->disconnect;
+
+}
+
+
+sub get_jcitems {
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ my $null;
+ my $var;
+ my $where;
+
+ if ($form->{projectnumber}) {
+ ($null, $var) = split /--/, $form->{projectnumber};
+ $where .= " AND j.project_id = $var";
+ }
+
+ if ($form->{employee}) {
+ ($null, $var) = split /--/, $form->{employee};
+ $where .= " AND j.employee_id = $var";
+ }
+
+ ($form->{transdatefrom}, $form->{transdateto}) = $form->from_to($form->{year}, $form->{month}, $form->{interval}) if $form->{year} && $form->{month};
+
+ if ($form->{transdatefrom}) {
+ $where .= " AND j.checkedin >= '$form->{transdatefrom}'";
+ }
+ if ($form->{transdateto}) {
+ $where .= " AND j.checkedout <= (date '$form->{transdateto}' + interval '1 days')";
+ }
+
+ my $query;
+ my $ref;
+
+ $query = qq|SELECT j.id, j.description, j.qty - j.allocated AS qty,
+ j.sellprice, j.parts_id, pr.$form->{vc}_id, j.project_id,
+ j.checkedin::date AS transdate, j.notes,
+ c.name AS $form->{vc}, pr.projectnumber, p.partnumber
+ FROM jcitems j
+ JOIN project pr ON (pr.id = j.project_id)
+ JOIN employee e ON (e.id = j.employee_id)
+ JOIN parts p ON (p.id = j.parts_id)
+ LEFT JOIN $form->{vc} c ON (c.id = pr.$form->{vc}_id)
+ WHERE pr.parts_id IS NULL
+ AND j.allocated != j.qty
+ $where
+ ORDER BY pr.projectnumber, c.name, j.checkedin::date|;
+
+ if ($form->{summary}) {
+ $query =~ s/j\.description/p\.description/;
+ $query =~ s/c\.name,/c\.name, j\.parts_id, /;
+ }
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ # tax accounts
+ $query = qq|SELECT c.accno
+ FROM chart c
+ JOIN partstax pt ON (pt.chart_id = c.id)
+ WHERE pt.parts_id = ?|;
+ my $tth = $dbh->prepare($query) || $form->dberror($query);
+ my $ptref;
+
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+
+ $tth->execute($ref->{parts_id});
+ $ref->{taxaccounts} = "";
+ while ($ptref = $tth->fetchrow_hashref(NAME_lc)) {
+ $ref->{taxaccounts} .= "$ptref->{accno} ";
+ }
+ $tth->finish;
+ chop $ref->{taxaccounts};
+
+ $ref->{amount} = $ref->{sellprice} * $ref->{qty};
+
+ push @{ $form->{jcitems} }, $ref;
+ }
+
+ $sth->finish;
+
+ $query = qq|SELECT curr
+ FROM defaults|;
+ ($form->{currency}) = $dbh->selectrow_array($query);
+ $form->{currency} =~ s/:.*//;
+ $form->{defaultcurrency} = $form->{currency};
+
+ $query = qq|SELECT c.accno, t.rate
+ FROM tax t
+ JOIN chart c ON (c.id = t.chart_id)|;
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+ $form->{taxaccounts} .= "$ref->{accno} ";
+ $form->{"$ref->{accno}_rate"} = $ref->{rate};
+ }
+ chop $form->{taxaccounts};
+ $sth->finish;
+
+ $dbh->disconnect;
+
+}
+
+
+sub allocate_projectitems {
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect_noauto($myconfig);
+
+ for my $i (1 .. $form->{rowcount}) {
+ for (split / /, $form->{"jcitems_$i"}) {
+ my ($id, $qty) = split /:/, $_;
+ $form->update_balance($dbh,
+ 'jcitems',
+ 'allocated',
+ "id = $id",
+ $qty);
+ }
+ }
+
+ $rc = $dbh->commit;
+ $dbh->disconnect;
+
+ $rc;
+
+}
+
+
+1;
+
diff --git a/LedgerSMB/RC.pm b/LedgerSMB/RC.pm
new file mode 100755
index 00000000..8b518cba
--- /dev/null
+++ b/LedgerSMB/RC.pm
@@ -0,0 +1,391 @@
+#=====================================================================
+# LedgerSMB
+# Small Medium Business Accounting software
+#
+# See COPYRIGHT file for copyright information
+#======================================================================
+#
+# This file has NOT undergone whitespace cleanup.
+#
+#======================================================================
+#
+# Account reconciliation routines
+#
+#======================================================================
+
+package RC;
+
+
+sub paymentaccounts {
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ my $query = qq|SELECT accno, description
+ FROM chart
+ WHERE link LIKE '%_paid%'
+ AND (category = 'A' OR category = 'L')
+ ORDER BY accno|;
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
+ push @{ $form->{PR} }, $ref;
+ }
+ $sth->finish;
+
+ $form->all_years($myconfig, $dbh);
+
+ $dbh->disconnect;
+
+}
+
+
+sub payment_transactions {
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database, turn AutoCommit off
+ my $dbh = $form->dbconnect_noauto($myconfig);
+
+ my $query;
+ my $sth;
+
+ $query = qq|SELECT category FROM chart
+ WHERE accno = '$form->{accno}'|;
+ ($form->{category}) = $dbh->selectrow_array($query);
+
+ my $cleared;
+
+ ($form->{fromdate}, $form->{todate}) = $form->from_to($form->{year}, $form->{month}, $form->{interval}) if $form->{year} && $form->{month};
+
+ my $transdate = qq| AND ac.transdate < date '$form->{fromdate}'|;
+
+ if (! $form->{fromdate}) {
+ $cleared = qq| AND ac.cleared = '1'|;
+ $transdate = "";
+ }
+
+ # get beginning balance
+ $query = qq|SELECT sum(ac.amount)
+ FROM acc_trans ac
+ JOIN chart ch ON (ch.id = ac.chart_id)
+ WHERE ch.accno = '$form->{accno}'
+ $transdate
+ $cleared
+ |;
+ ($form->{beginningbalance}) = $dbh->selectrow_array($query);
+
+ # fx balance
+ $query = qq|SELECT sum(ac.amount)
+ FROM acc_trans ac
+ JOIN chart ch ON (ch.id = ac.chart_id)
+ WHERE ch.accno = '$form->{accno}'
+ AND ac.fx_transaction = '1'
+ $transdate
+ $cleared
+ |;
+ ($form->{fx_balance}) = $dbh->selectrow_array($query);
+
+
+ $transdate = "";
+ if ($form->{todate}) {
+ $transdate = qq| AND ac.transdate <= date '$form->{todate}'|;
+ }
+
+ # get statement balance
+ $query = qq|SELECT sum(ac.amount)
+ FROM acc_trans ac
+ JOIN chart ch ON (ch.id = ac.chart_id)
+ WHERE ch.accno = '$form->{accno}'
+ $transdate
+ |;
+ ($form->{endingbalance}) = $dbh->selectrow_array($query);
+
+ # fx balance
+ $query = qq|SELECT sum(ac.amount)
+ FROM acc_trans ac
+ JOIN chart ch ON (ch.id = ac.chart_id)
+ WHERE ch.accno = '$form->{accno}'
+ AND ac.fx_transaction = '1'
+ $transdate
+ |;
+ ($form->{fx_endingbalance}) = $dbh->selectrow_array($query);
+
+
+ $cleared = qq| AND ac.cleared = '0'| unless $form->{fromdate};
+
+ if ($form->{report}) {
+ $cleared = qq| AND NOT (ac.cleared = '0' OR ac.cleared = '1')|;
+ if ($form->{cleared}) {
+ $cleared = qq| AND ac.cleared = '1'|;
+ }
+ if ($form->{outstanding}) {
+ $cleared = ($form->{cleared}) ? "" : qq| AND ac.cleared = '0'|;
+ }
+ if (! $form->{fromdate}) {
+ $form->{beginningbalance} = 0;
+ $form->{fx_balance} = 0;
+ }
+ }
+
+ my $fx_transaction;
+ if ($form->{fx_transaction}) {
+ $fx_transaction = qq|
+ AND NOT
+ (ac.chart_id IN
+ (SELECT fxgain_accno_id FROM defaults
+ UNION
+ SELECT fxloss_accno_id FROM defaults))|;
+ } else {
+ $fx_transaction = qq|
+ AND ac.fx_transaction = '0'|;
+ }
+
+
+ if ($form->{summary}) {
+ $query = qq|SELECT ac.transdate, ac.source,
+ sum(ac.amount) AS amount, ac.cleared
+ FROM acc_trans ac
+ JOIN chart ch ON (ac.chart_id = ch.id)
+ WHERE ch.accno = '$form->{accno}'
+ AND ac.amount >= 0
+ $fx_transaction
+ $cleared|;
+ $query .= " AND ac.transdate >= '$form->{fromdate}'" if $form->{fromdate};
+ $query .= " AND ac.transdate <= '$form->{todate}'" if $form->{todate};
+ $query .= " GROUP BY ac.source, ac.transdate, ac.cleared";
+ $query .= qq|
+ UNION ALL
+ SELECT ac.transdate, ac.source,
+ sum(ac.amount) AS amount, ac.cleared
+ FROM acc_trans ac
+ JOIN chart ch ON (ac.chart_id = ch.id)
+ WHERE ch.accno = '$form->{accno}'
+ AND ac.amount < 0
+ $fx_transaction
+ $cleared|;
+ $query .= " AND ac.transdate >= '$form->{fromdate}'" if $form->{fromdate};
+ $query .= " AND ac.transdate <= '$form->{todate}'" if $form->{todate};
+ $query .= " GROUP BY ac.source, ac.transdate, ac.cleared";
+
+ $query .= " ORDER BY 1,2";
+
+ } else {
+
+ $query = qq|SELECT ac.transdate, ac.source, ac.fx_transaction,
+ ac.amount, ac.cleared, g.id, g.description
+ FROM acc_trans ac
+ JOIN chart ch ON (ac.chart_id = ch.id)
+ JOIN gl g ON (g.id = ac.trans_id)
+ WHERE ch.accno = '$form->{accno}'
+ $fx_transaction
+ $cleared|;
+ $query .= " AND ac.transdate >= '$form->{fromdate}'" if $form->{fromdate};
+ $query .= " AND ac.transdate <= '$form->{todate}'" if $form->{todate};
+ $query .= qq|
+ UNION ALL
+ SELECT ac.transdate, ac.source, ac.fx_transaction,
+ ac.amount, ac.cleared, a.id, n.name
+ FROM acc_trans ac
+ JOIN chart ch ON (ac.chart_id = ch.id)
+ JOIN ar a ON (a.id = ac.trans_id)
+ JOIN customer n ON (n.id = a.customer_id)
+ WHERE ch.accno = '$form->{accno}'
+ $fx_transaction
+ $cleared|;
+ $query .= " AND ac.transdate >= '$form->{fromdate}'" if $form->{fromdate};
+ $query .= " AND ac.transdate <= '$form->{todate}'" if $form->{todate};
+ $query .= qq|
+ UNION ALL
+ SELECT ac.transdate, ac.source, ac.fx_transaction,
+ ac.amount, ac.cleared, a.id, n.name
+ FROM acc_trans ac
+ JOIN chart ch ON (ac.chart_id = ch.id)
+ JOIN ap a ON (a.id = ac.trans_id)
+ JOIN vendor n ON (n.id = a.vendor_id)
+ WHERE ch.accno = '$form->{accno}'
+ $fx_transaction
+ $cleared|;
+ $query .= " AND ac.transdate >= '$form->{fromdate}'" if $form->{fromdate};
+ $query .= " AND ac.transdate <= '$form->{todate}'" if $form->{todate};
+
+ $query .= " ORDER BY 1,2,3";
+ }
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ my $dr;
+ my $cr;
+
+ if ($form->{summary}) {
+ $query = qq|SELECT c.name
+ FROM customer c
+ JOIN ar a ON (c.id = a.customer_id)
+ JOIN acc_trans ac ON (a.id = ac.trans_id)
+ JOIN chart ch ON (ac.chart_id = ch.id)
+ WHERE ac.transdate = ?
+ AND ch.accno = '$form->{accno}'
+ AND (ac.source = ? OR ac.source IS NULL)
+ AND ac.amount >= 0
+ $cleared
+ UNION
+ SELECT v.name
+ FROM vendor v
+ JOIN ap a ON (v.id = a.vendor_id)
+ JOIN acc_trans ac ON (a.id = ac.trans_id)
+ JOIN chart ch ON (ac.chart_id = ch.id)
+ WHERE ac.transdate = ?
+ AND ch.accno = '$form->{accno}'
+ AND (ac.source = ? OR ac.source IS NULL)
+ AND ac.amount > 0
+ $cleared
+ UNION
+ SELECT g.description
+ FROM gl g
+ JOIN acc_trans ac ON (g.id = ac.trans_id)
+ JOIN chart ch ON (ac.chart_id = ch.id)
+ WHERE ac.transdate = ?
+ AND ch.accno = '$form->{accno}'
+ AND (ac.source = ? OR ac.source IS NULL)
+ AND ac.amount >= 0
+ $cleared
+ |;
+
+ $query .= " ORDER BY 1";
+ $dr = $dbh->prepare($query);
+
+ $query = qq|SELECT c.name
+ FROM customer c
+ JOIN ar a ON (c.id = a.customer_id)
+ JOIN acc_trans ac ON (a.id = ac.trans_id)
+ JOIN chart ch ON (ac.chart_id = ch.id)
+ WHERE ac.transdate = ?
+ AND ch.accno = '$form->{accno}'
+ AND (ac.source = ? OR ac.source IS NULL)
+ AND ac.amount < 0
+ $cleared
+ UNION
+ SELECT v.name
+ FROM vendor v
+ JOIN ap a ON (v.id = a.vendor_id)
+ JOIN acc_trans ac ON (a.id = ac.trans_id)
+ JOIN chart ch ON (ac.chart_id = ch.id)
+ WHERE ac.transdate = ?
+ AND ch.accno = '$form->{accno}'
+ AND (ac.source = ? OR ac.source IS NULL)
+ AND ac.amount < 0
+ $cleared
+ UNION
+ SELECT g.description
+ FROM gl g
+ JOIN acc_trans ac ON (g.id = ac.trans_id)
+ JOIN chart ch ON (ac.chart_id = ch.id)
+ WHERE ac.transdate = ?
+ AND ch.accno = '$form->{accno}'
+ AND (ac.source = ? OR ac.source IS NULL)
+ AND ac.amount < 0
+ $cleared
+ |;
+
+ $query .= " ORDER BY 1";
+ $cr = $dbh->prepare($query);
+ }
+
+ my $name;
+ my $ref;
+
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+
+ if ($form->{summary}) {
+
+ if ($ref->{amount} > 0) {
+ $dr->execute($ref->{transdate}, $ref->{source}, $ref->{transdate}, $ref->{source}, $ref->{transdate}, $ref->{source});
+ $ref->{oldcleared} = $ref->{cleared};
+ $ref->{name} = ();
+
+ while (($name) = $dr->fetchrow_array) {
+ push @{ $ref->{name} }, $name;
+ }
+ $dr->finish;
+ } else {
+
+ $cr->execute($ref->{transdate}, $ref->{source}, $ref->{transdate}, $ref->{source}, $ref->{transdate}, $ref->{source});
+ $ref->{oldcleared} = $ref->{cleared};
+ $ref->{name} = ();
+ while (($name) = $cr->fetchrow_array) {
+ push @{ $ref->{name} }, $name;
+ }
+ $cr->finish;
+
+ }
+
+ } else {
+ push @{ $ref->{name} }, $ref->{description};
+ }
+
+ push @{ $form->{PR} }, $ref;
+
+ }
+ $sth->finish;
+
+ $dbh->disconnect;
+
+}
+
+
+sub reconcile {
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ my $query = qq|SELECT id FROM chart
+ WHERE accno = '$form->{accno}'|;
+ my ($chart_id) = $dbh->selectrow_array($query);
+ $chart_id *= 1;
+
+ $query = qq|SELECT trans_id FROM acc_trans
+ WHERE (source = ? OR source IS NULL)
+ AND transdate = ?
+ AND cleared = '0'
+ AND chart_id = $chart_id|;
+ my $sth = $dbh->prepare($query) || $form->dberror($query);
+
+ my $i;
+ my $trans_id;
+
+ $query = qq|UPDATE acc_trans SET cleared = '1'
+ WHERE cleared = '0'
+ AND trans_id = ?
+ AND transdate = ?
+ AND chart_id = $chart_id|;
+ my $tth = $dbh->prepare($query) || $form->dberror($query);
+
+ # clear flags
+ for $i (1 .. $form->{rowcount}) {
+ if ($form->{"cleared_$i"} && ! $form->{"oldcleared_$i"}) {
+ if ($form->{summary}) {
+ $sth->execute($form->{"source_$i"}, $form->{"transdate_$i"}) || $form->dberror;
+
+ while (($trans_id) = $sth->fetchrow_array) {
+ $tth->execute($trans_id, $form->{"transdate_$i"}) || $form->dberror;
+ $tth->finish;
+ }
+ $sth->finish;
+
+ } else {
+
+ $tth->execute($form->{"id_$i"}, $form->{"transdate_$i"}) || $form->dberror;
+ $tth->finish;
+ }
+ }
+ }
+
+ $dbh->disconnect;
+
+}
+
+1;
+
diff --git a/LedgerSMB/RP.pm b/LedgerSMB/RP.pm
new file mode 100755
index 00000000..946d4c69
--- /dev/null
+++ b/LedgerSMB/RP.pm
@@ -0,0 +1,2103 @@
+#=====================================================================
+# LedgerSMB
+# Small Medium Business Accounting software
+#
+# See COPYRIGHT file for copyright information
+#======================================================================
+#
+# This file has NOT undergone whitespace cleanup.
+#
+#======================================================================
+#
+# backend code for reports
+#
+#======================================================================
+
+package RP;
+
+
+sub yearend_statement {
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ # if todate < existing yearends, delete GL and yearends
+ my $query = qq|SELECT trans_id FROM yearend
+ WHERE transdate >= '$form->{todate}'|;
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ my @trans_id = ();
+ my $id;
+ while (($id) = $sth->fetchrow_array) {
+ push @trans_id, $id;
+ }
+ $sth->finish;
+
+ $query = qq|DELETE FROM gl
+ WHERE id = ?|;
+ $sth = $dbh->prepare($query) || $form->dberror($query);
+
+ $query = qq|DELETE FROM acc_trans
+ WHERE trans_id = ?|;
+ my $ath = $dbh->prepare($query) || $form->dberror($query);
+
+ foreach $id (@trans_id) {
+ $sth->execute($id);
+ $ath->execute($id);
+
+ $sth->finish;
+ $ath->finish;
+ }
+
+
+ my $last_period = 0;
+ my @categories = qw(I E);
+ my $category;
+
+ $form->{decimalplaces} *= 1;
+
+ &get_accounts($dbh, 0, $form->{fromdate}, $form->{todate}, $form, \@categories);
+
+ # disconnect
+ $dbh->disconnect;
+
+
+ # now we got $form->{I}{accno}{ }
+ # and $form->{E}{accno}{ }
+
+ my %account = ( 'I' => { 'label' => 'income',
+ 'labels' => 'income',
+ 'ml' => 1 },
+ 'E' => { 'label' => 'expense',
+ 'labels' => 'expenses',
+ 'ml' => -1 }
+ );
+
+ foreach $category (@categories) {
+ foreach $key (sort keys %{ $form->{$category} }) {
+ if ($form->{$category}{$key}{charttype} eq 'A') {
+ $form->{"total_$account{$category}{labels}_this_period"} += $form->{$category}{$key}{this} * $account{$category}{ml};
+ }
+ }
+ }
+
+
+ # totals for income and expenses
+ $form->{total_income_this_period} = $form->round_amount($form->{total_income_this_period}, $form->{decimalplaces});
+ $form->{total_expenses_this_period} = $form->round_amount($form->{total_expenses_this_period}, $form->{decimalplaces});
+
+ # total for income/loss
+ $form->{total_this_period} = $form->{total_income_this_period} - $form->{total_expenses_this_period};
+
+}
+
+
+sub income_statement {
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ my $last_period = 0;
+ my @categories = qw(I E);
+ my $category;
+
+ $form->{decimalplaces} *= 1;
+
+ if (! ($form->{fromdate} || $form->{todate})) {
+ if ($form->{fromyear} && $form->{frommonth}) {
+ ($form->{fromdate}, $form->{todate}) = $form->from_to($form->{fromyear}, $form->{frommonth}, $form->{interval});
+ }
+ }
+
+ &get_accounts($dbh, $last_period, $form->{fromdate}, $form->{todate}, $form, \@categories, 1);
+
+ if (! ($form->{comparefromdate} || $form->{comparetodate})) {
+ if ($form->{compareyear} && $form->{comparemonth}) {
+ ($form->{comparefromdate}, $form->{comparetodate}) = $form->from_to($form->{compareyear}, $form->{comparemonth}, $form->{interval});
+ }
+ }
+
+ # if there are any compare dates
+ if ($form->{comparefromdate} || $form->{comparetodate}) {
+ $last_period = 1;
+
+ &get_accounts($dbh, $last_period, $form->{comparefromdate}, $form->{comparetodate}, $form, \@categories, 1);
+ }
+
+
+ # disconnect
+ $dbh->disconnect;
+
+
+ # now we got $form->{I}{accno}{ }
+ # and $form->{E}{accno}{ }
+
+ my %account = ( 'I' => { 'label' => 'income',
+ 'labels' => 'income',
+ 'ml' => 1 },
+ 'E' => { 'label' => 'expense',
+ 'labels' => 'expenses',
+ 'ml' => -1 }
+ );
+
+ my $str;
+
+ foreach $category (@categories) {
+
+ foreach $key (sort keys %{ $form->{$category} }) {
+ # push description onto array
+
+ $str = ($form->{l_heading}) ? $form->{padding} : "";
+
+ if ($form->{$category}{$key}{charttype} eq "A") {
+ $str .= ($form->{l_accno}) ? "$form->{$category}{$key}{accno} - $form->{$category}{$key}{description}" : "$form->{$category}{$key}{description}";
+ }
+ if ($form->{$category}{$key}{charttype} eq "H") {
+ if ($account{$category}{subtotal} && $form->{l_subtotal}) {
+ $dash = "- ";
+ push(@{$form->{"$account{$category}{label}_account"}}, "$str$form->{bold}$account{$category}{subdescription}$form->{endbold}");
+ push(@{$form->{"$account{$category}{labels}_this_period"}}, $form->format_amount($myconfig, $account{$category}{subthis} * $account{$category}{ml}, $form->{decimalplaces}, $dash));
+
+ if ($last_period) {
+ push(@{$form->{"$account{$category}{labels}_last_period"}}, $form->format_amount($myconfig, $account{$category}{sublast} * $account{$category}{ml}, $form->{decimalplaces}, $dash));
+ }
+
+ }
+
+ $str = "$form->{br}$form->{bold}$form->{$category}{$key}{description}$form->{endbold}";
+
+ $account{$category}{subthis} = $form->{$category}{$key}{this};
+ $account{$category}{sublast} = $form->{$category}{$key}{last};
+ $account{$category}{subdescription} = $form->{$category}{$key}{description};
+ $account{$category}{subtotal} = 1;
+
+ $form->{$category}{$key}{this} = 0;
+ $form->{$category}{$key}{last} = 0;
+
+ next unless $form->{l_heading};
+
+ $dash = " ";
+ }
+
+ push(@{$form->{"$account{$category}{label}_account"}}, $str);
+
+ if ($form->{$category}{$key}{charttype} eq 'A') {
+ $form->{"total_$account{$category}{labels}_this_period"} += $form->{$category}{$key}{this} * $account{$category}{ml};
+ $dash = "- ";
+ }
+
+ push(@{$form->{"$account{$category}{labels}_this_period"}}, $form->format_amount($myconfig, $form->{$category}{$key}{this} * $account{$category}{ml}, $form->{decimalplaces}, $dash));
+
+ # add amount or - for last period
+ if ($last_period) {
+ $form->{"total_$account{$category}{labels}_last_period"} += $form->{$category}{$key}{last} * $account{$category}{ml};
+
+ push(@{$form->{"$account{$category}{labels}_last_period"}}, $form->format_amount($myconfig,$form->{$category}{$key}{last} * $account{$category}{ml}, $form->{decimalplaces}, $dash));
+ }
+ }
+
+ $str = ($form->{l_heading}) ? $form->{padding} : "";
+ if ($account{$category}{subtotal} && $form->{l_subtotal}) {
+ push(@{$form->{"$account{$category}{label}_account"}}, "$str$form->{bold}$account{$category}{subdescription}$form->{endbold}");
+ push(@{$form->{"$account{$category}{labels}_this_period"}}, $form->format_amount($myconfig, $account{$category}{subthis} * $account{$category}{ml}, $form->{decimalplaces}, $dash));
+
+ if ($last_period) {
+ push(@{$form->{"$account{$category}{labels}_last_period"}}, $form->format_amount($myconfig, $account{$category}{sublast} * $account{$category}{ml}, $form->{decimalplaces}, $dash));
+ }
+ }
+
+ }
+
+
+ # totals for income and expenses
+ $form->{total_income_this_period} = $form->round_amount($form->{total_income_this_period}, $form->{decimalplaces});
+ $form->{total_expenses_this_period} = $form->round_amount($form->{total_expenses_this_period}, $form->{decimalplaces});
+
+ # total for income/loss
+ $form->{total_this_period} = $form->{total_income_this_period} - $form->{total_expenses_this_period};
+
+ if ($last_period) {
+ # total for income/loss
+ $form->{total_last_period} = $form->format_amount($myconfig, $form->{total_income_last_period} - $form->{total_expenses_last_period}, $form->{decimalplaces}, "- ");
+
+ # totals for income and expenses for last_period
+ $form->{total_income_last_period} = $form->format_amount($myconfig, $form->{total_income_last_period}, $form->{decimalplaces}, "- ");
+ $form->{total_expenses_last_period} = $form->format_amount($myconfig, $form->{total_expenses_last_period}, $form->{decimalplaces}, "- ");
+
+ }
+
+
+ $form->{total_income_this_period} = $form->format_amount($myconfig,$form->{total_income_this_period}, $form->{decimalplaces}, "- ");
+ $form->{total_expenses_this_period} = $form->format_amount($myconfig,$form->{total_expenses_this_period}, $form->{decimalplaces}, "- ");
+ $form->{total_this_period} = $form->format_amount($myconfig,$form->{total_this_period}, $form->{decimalplaces}, "- ");
+
+}
+
+
+sub balance_sheet {
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ my $last_period = 0;
+ my @categories = qw(A L Q);
+
+ my $null;
+
+ if ($form->{asofdate}) {
+ if ($form->{asofyear} && $form->{asofmonth}) {
+ if ($form->{asofdate} !~ /\W/) {
+ $form->{asofdate} = "$form->{asofyear}$form->{asofmonth}$form->{asofdate}";
+ }
+ }
+ } else {
+ if ($form->{asofyear} && $form->{asofmonth}) {
+ ($null, $form->{asofdate}) = $form->from_to($form->{asofyear}, $form->{asofmonth});
+ }
+ }
+
+ # if there are any dates construct a where
+ if ($form->{asofdate}) {
+
+ $form->{this_period} = "$form->{asofdate}";
+ $form->{period} = "$form->{asofdate}";
+
+ }
+
+ $form->{decimalplaces} *= 1;
+
+ &get_accounts($dbh, $last_period, "", $form->{asofdate}, $form, \@categories, 1);
+
+ if ($form->{compareasofdate}) {
+ if ($form->{compareasofyear} && $form->{compareasofmonth}) {
+ if ($form->{compareasofdate} !~ /\W/) {
+ $form->{compareasofdate} = "$form->{compareasofyear}$form->{compareasofmonth}$form->{compareasofdate}";
+ }
+ }
+ } else {
+ if ($form->{compareasofyear} && $form->{compareasofmonth}) {
+ ($null, $form->{compareasofdate}) = $form->from_to($form->{compareasofyear}, $form->{compareasofmonth});
+ }
+ }
+
+ # if there are any compare dates
+ if ($form->{compareasofdate}) {
+
+ $last_period = 1;
+ &get_accounts($dbh, $last_period, "", $form->{compareasofdate}, $form, \@categories, 1);
+
+ $form->{last_period} = "$form->{compareasofdate}";
+
+ }
+
+
+ # disconnect
+ $dbh->disconnect;
+
+
+ # now we got $form->{A}{accno}{ } assets
+ # and $form->{L}{accno}{ } liabilities
+ # and $form->{Q}{accno}{ } equity
+ # build asset accounts
+
+ my $str;
+ my $key;
+
+ my %account = ( 'A' => { 'label' => 'asset',
+ 'labels' => 'assets',
+ 'ml' => -1 },
+ 'L' => { 'label' => 'liability',
+ 'labels' => 'liabilities',
+ 'ml' => 1 },
+ 'Q' => { 'label' => 'equity',
+ 'labels' => 'equity',
+ 'ml' => 1 }
+ );
+
+
+ foreach $category (@categories) {
+
+ foreach $key (sort keys %{ $form->{$category} }) {
+
+ $str = ($form->{l_heading}) ? $form->{padding} : "";
+
+ if ($form->{$category}{$key}{charttype} eq "A") {
+ $str .= ($form->{l_accno}) ? "$form->{$category}{$key}{accno} - $form->{$category}{$key}{description}" : "$form->{$category}{$key}{description}";
+ }
+ if ($form->{$category}{$key}{charttype} eq "H") {
+ if ($account{$category}{subtotal} && $form->{l_subtotal}) {
+ $dash = "- ";
+ push(@{$form->{"$account{$category}{label}_account"}}, "$str$form->{bold}$account{$category}{subdescription}$form->{endbold}");
+ push(@{$form->{"$account{$category}{label}_this_period"}}, $form->format_amount($myconfig, $account{$category}{subthis} * $account{$category}{ml}, $form->{decimalplaces}, $dash));
+
+ if ($last_period) {
+ push(@{$form->{"$account{$category}{label}_last_period"}}, $form->format_amount($myconfig, $account{$category}{sublast} * $account{$category}{ml}, $form->{decimalplaces}, $dash));
+ }
+ }
+
+ $str = "$form->{bold}$form->{$category}{$key}{description}$form->{endbold}";
+
+ $account{$category}{subthis} = $form->{$category}{$key}{this};
+ $account{$category}{sublast} = $form->{$category}{$key}{last};
+ $account{$category}{subdescription} = $form->{$category}{$key}{description};
+ $account{$category}{subtotal} = 1;
+
+ $form->{$category}{$key}{this} = 0;
+ $form->{$category}{$key}{last} = 0;
+
+ next unless $form->{l_heading};
+
+ $dash = " ";
+ }
+
+ # push description onto array
+ push(@{$form->{"$account{$category}{label}_account"}}, $str);
+
+ if ($form->{$category}{$key}{charttype} eq 'A') {
+ $form->{"total_$account{$category}{labels}_this_period"} += $form->{$category}{$key}{this} * $account{$category}{ml};
+ $dash = "- ";
+ }
+
+ push(@{$form->{"$account{$category}{label}_this_period"}}, $form->format_amount($myconfig, $form->{$category}{$key}{this} * $account{$category}{ml}, $form->{decimalplaces}, $dash));
+
+ if ($last_period) {
+ $form->{"total_$account{$category}{labels}_last_period"} += $form->{$category}{$key}{last} * $account{$category}{ml};
+
+ push(@{$form->{"$account{$category}{label}_last_period"}}, $form->format_amount($myconfig, $form->{$category}{$key}{last} * $account{$category}{ml}, $form->{decimalplaces}, $dash));
+ }
+ }
+
+ $str = ($form->{l_heading}) ? $form->{padding} : "";
+ if ($account{$category}{subtotal} && $form->{l_subtotal}) {
+ push(@{$form->{"$account{$category}{label}_account"}}, "$str$form->{bold}$account{$category}{subdescription}$form->{endbold}");
+ push(@{$form->{"$account{$category}{label}_this_period"}}, $form->format_amount($myconfig, $account{$category}{subthis} * $account{$category}{ml}, $form->{decimalplaces}, $dash));
+
+ if ($last_period) {
+ push(@{$form->{"$account{$category}{label}_last_period"}}, $form->format_amount($myconfig, $account{$category}{sublast} * $account{$category}{ml}, $form->{decimalplaces}, $dash));
+ }
+ }
+
+ }
+
+
+ # totals for assets, liabilities
+ $form->{total_assets_this_period} = $form->round_amount($form->{total_assets_this_period}, $form->{decimalplaces});
+ $form->{total_liabilities_this_period} = $form->round_amount($form->{total_liabilities_this_period}, $form->{decimalplaces});
+ $form->{total_equity_this_period} = $form->round_amount($form->{total_equity_this_period}, $form->{decimalplaces});
+
+ # calculate earnings
+ $form->{earnings_this_period} = $form->{total_assets_this_period} - $form->{total_liabilities_this_period} - $form->{total_equity_this_period};
+
+ push(@{$form->{equity_this_period}}, $form->format_amount($myconfig, $form->{earnings_this_period}, $form->{decimalplaces}, "- "));
+
+ $form->{total_equity_this_period} = $form->round_amount($form->{total_equity_this_period} + $form->{earnings_this_period}, $form->{decimalplaces});
+
+ # add liability + equity
+ $form->{total_this_period} = $form->format_amount($myconfig, $form->{total_liabilities_this_period} + $form->{total_equity_this_period}, $form->{decimalplaces}, "- ");
+
+
+ if ($last_period) {
+ # totals for assets, liabilities
+ $form->{total_assets_last_period} = $form->round_amount($form->{total_assets_last_period}, $form->{decimalplaces});
+ $form->{total_liabilities_last_period} = $form->round_amount($form->{total_liabilities_last_period}, $form->{decimalplaces});
+ $form->{total_equity_last_period} = $form->round_amount($form->{total_equity_last_period}, $form->{decimalplaces});
+
+ # calculate retained earnings
+ $form->{earnings_last_period} = $form->{total_assets_last_period} - $form->{total_liabilities_last_period} - $form->{total_equity_last_period};
+
+ push(@{$form->{equity_last_period}}, $form->format_amount($myconfig,$form->{earnings_last_period}, $form->{decimalplaces}, "- "));
+
+ $form->{total_equity_last_period} = $form->round_amount($form->{total_equity_last_period} + $form->{earnings_last_period}, $form->{decimalplaces});
+
+ # add liability + equity
+ $form->{total_last_period} = $form->format_amount($myconfig, $form->{total_liabilities_last_period} + $form->{total_equity_last_period}, $form->{decimalplaces}, "- ");
+
+ }
+
+
+ $form->{total_liabilities_last_period} = $form->format_amount($myconfig, $form->{total_liabilities_last_period}, $form->{decimalplaces}, "- ") if ($form->{total_liabilities_last_period});
+
+ $form->{total_equity_last_period} = $form->format_amount($myconfig, $form->{total_equity_last_period}, $form->{decimalplaces}, "- ") if ($form->{total_equity_last_period});
+
+ $form->{total_assets_last_period} = $form->format_amount($myconfig, $form->{total_assets_last_period}, $form->{decimalplaces}, "- ") if ($form->{total_assets_last_period});
+
+ $form->{total_assets_this_period} = $form->format_amount($myconfig, $form->{total_assets_this_period}, $form->{decimalplaces}, "- ");
+
+ $form->{total_liabilities_this_period} = $form->format_amount($myconfig, $form->{total_liabilities_this_period}, $form->{decimalplaces}, "- ");
+
+ $form->{total_equity_this_period} = $form->format_amount($myconfig, $form->{total_equity_this_period}, $form->{decimalplaces}, "- ");
+
+}
+
+
+sub get_accounts {
+ my ($dbh, $last_period, $fromdate, $todate, $form, $categories, $excludeyearend) = @_;
+
+ my $department_id;
+ my $project_id;
+
+ ($null, $department_id) = split /--/, $form->{department};
+ ($null, $project_id) = split /--/, $form->{projectnumber};
+
+ my $query;
+ my $dpt_where;
+ my $dpt_join;
+ my $project;
+ my $where = "1 = 1";
+ my $glwhere = "";
+ my $subwhere = "";
+ my $yearendwhere = "1 = 1";
+ my $item;
+
+ my $category = "AND (";
+ foreach $item (@{ $categories }) {
+ $category .= qq|c.category = '$item' OR |;
+ }
+ $category =~ s/OR $/\)/;
+
+
+ # get headings
+ $query = qq|SELECT accno, description, category
+ FROM chart c
+ WHERE c.charttype = 'H'
+ $category
+ ORDER by c.accno|;
+
+ if ($form->{accounttype} eq 'gifi')
+ {
+ $query = qq|SELECT g.accno, g.description, c.category
+ FROM gifi g
+ JOIN chart c ON (c.gifi_accno = g.accno)
+ WHERE c.charttype = 'H'
+ $category
+ ORDER BY g.accno|;
+ }
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ my @headingaccounts = ();
+ while ($ref = $sth->fetchrow_hashref(NAME_lc))
+ {
+ $form->{$ref->{category}}{$ref->{accno}}{description} = "$ref->{description}";
+ $form->{$ref->{category}}{$ref->{accno}}{charttype} = "H";
+ $form->{$ref->{category}}{$ref->{accno}}{accno} = $ref->{accno};
+
+ push @headingaccounts, $ref->{accno};
+ }
+
+ $sth->finish;
+
+ if ($form->{method} eq 'cash' && !$todate) {
+ ($todate) = $dbh->selectrow_array(qq|SELECT current_date FROM defaults|);
+ }
+
+ if ($fromdate) {
+ if ($form->{method} eq 'cash') {
+ $subwhere .= " AND transdate >= '$fromdate'";
+ $glwhere = " AND ac.transdate >= '$fromdate'";
+ } else {
+ $where .= " AND ac.transdate >= '$fromdate'";
+ }
+ }
+
+ if ($todate) {
+ $where .= " AND ac.transdate <= '$todate'";
+ $subwhere .= " AND transdate <= '$todate'";
+ $yearendwhere = "ac.transdate < '$todate'";
+ }
+
+ if ($excludeyearend) {
+ $ywhere = " AND ac.trans_id NOT IN
+ (SELECT trans_id FROM yearend)";
+
+ if ($todate) {
+ $ywhere = " AND ac.trans_id NOT IN
+ (SELECT trans_id FROM yearend
+ WHERE transdate <= '$todate')";
+ }
+
+ if ($fromdate) {
+ $ywhere = " AND ac.trans_id NOT IN
+ (SELECT trans_id FROM yearend
+ WHERE transdate >= '$fromdate')";
+ if ($todate) {
+ $ywhere = " AND ac.trans_id NOT IN
+ (SELECT trans_id FROM yearend
+ WHERE transdate >= '$fromdate'
+ AND transdate <= '$todate')";
+ }
+ }
+ }
+
+ if ($department_id) {
+ $dpt_join = qq|
+ JOIN department t ON (a.department_id = t.id)
+ |;
+ $dpt_where = qq|
+ AND t.id = $department_id
+ |;
+ }
+
+ if ($project_id) {
+ $project = qq|
+ AND ac.project_id = $project_id
+ |;
+ }
+
+
+ if ($form->{accounttype} eq 'gifi') {
+
+ if ($form->{method} eq 'cash') {
+
+ $query = qq|
+
+ SELECT g.accno, sum(ac.amount) AS amount,
+ g.description, c.category
+ FROM acc_trans ac
+ JOIN chart c ON (c.id = ac.chart_id)
+ JOIN ar a ON (a.id = ac.trans_id)
+ JOIN gifi g ON (g.accno = c.gifi_accno)
+ $dpt_join
+ WHERE $where
+ $ywhere
+ $dpt_where
+ $category
+ AND ac.trans_id IN
+ (
+ SELECT trans_id
+ FROM acc_trans
+ JOIN chart ON (chart_id = id)
+ WHERE link LIKE '%AR_paid%'
+ $subwhere
+ )
+ $project
+ GROUP BY g.accno, g.description, c.category
+
+ UNION ALL
+
+ SELECT '' AS accno, SUM(ac.amount) AS amount,
+ '' AS description, c.category
+ FROM acc_trans ac
+ JOIN chart c ON (c.id = ac.chart_id)
+ JOIN ar a ON (a.id = ac.trans_id)
+ $dpt_join
+ WHERE $where
+ $ywhere
+ $dpt_where
+ $category
+ AND c.gifi_accno = ''
+ AND ac.trans_id IN
+ (
+ SELECT trans_id
+ FROM acc_trans
+ JOIN chart ON (chart_id = id)
+ WHERE link LIKE '%AR_paid%'
+ $subwhere
+ )
+ $project
+ GROUP BY c.category
+
+ UNION ALL
+
+ SELECT g.accno, sum(ac.amount) AS amount,
+ g.description, c.category
+ FROM acc_trans ac
+ JOIN chart c ON (c.id = ac.chart_id)
+ JOIN ap a ON (a.id = ac.trans_id)
+ JOIN gifi g ON (g.accno = c.gifi_accno)
+ $dpt_join
+ WHERE $where
+ $ywhere
+ $dpt_where
+ $category
+ AND ac.trans_id IN
+ (
+ SELECT trans_id
+ FROM acc_trans
+ JOIN chart ON (chart_id = id)
+ WHERE link LIKE '%AP_paid%'
+ $subwhere
+ )
+ $project
+ GROUP BY g.accno, g.description, c.category
+
+ UNION ALL
+
+ SELECT '' AS accno, SUM(ac.amount) AS amount,
+ '' AS description, c.category
+ FROM acc_trans ac
+ JOIN chart c ON (c.id = ac.chart_id)
+ JOIN ap a ON (a.id = ac.trans_id)
+ $dpt_join
+ WHERE $where
+ $ywhere
+ $dpt_where
+ $category
+ AND c.gifi_accno = ''
+ AND ac.trans_id IN
+ (
+ SELECT trans_id
+ FROM acc_trans
+ JOIN chart ON (chart_id = id)
+ WHERE link LIKE '%AP_paid%'
+ $subwhere
+ )
+ $project
+ GROUP BY c.category
+
+ UNION ALL
+
+-- add gl
+
+ SELECT g.accno, sum(ac.amount) AS amount,
+ g.description, c.category
+ FROM acc_trans ac
+ JOIN chart c ON (c.id = ac.chart_id)
+ JOIN gifi g ON (g.accno = c.gifi_accno)
+ JOIN gl a ON (a.id = ac.trans_id)
+ $dpt_join
+ WHERE $where
+ $ywhere
+ $glwhere
+ $dpt_where
+ $category
+ AND NOT (c.link = 'AR' OR c.link = 'AP')
+ $project
+ GROUP BY g.accno, g.description, c.category
+
+ UNION ALL
+
+ SELECT '' AS accno, SUM(ac.amount) AS amount,
+ '' AS description, c.category
+ FROM acc_trans ac
+ JOIN chart c ON (c.id = ac.chart_id)
+ JOIN gl a ON (a.id = ac.trans_id)
+ $dpt_join
+ WHERE $where
+ $ywhere
+ $glwhere
+ $dpt_where
+ $category
+ AND c.gifi_accno = ''
+ AND NOT (c.link = 'AR' OR c.link = 'AP')
+ $project
+ GROUP BY c.category
+ |;
+
+ if ($excludeyearend) {
+
+ # this is for the yearend
+
+ $query .= qq|
+
+ UNION ALL
+
+ SELECT g.accno, sum(ac.amount) AS amount,
+ g.description, c.category
+ FROM yearend y
+ JOIN gl a ON (a.id = y.trans_id)
+ JOIN acc_trans ac ON (ac.trans_id = y.trans_id)
+ JOIN chart c ON (c.id = ac.chart_id)
+ JOIN gifi g ON (g.accno = c.gifi_accno)
+ $dpt_join
+ WHERE $yearendwhere
+ AND c.category = 'Q'
+ $dpt_where
+ $project
+ GROUP BY g.accno, g.description, c.category
+ |;
+ }
+
+ } else {
+
+ if ($department_id) {
+ $dpt_join = qq|
+ JOIN dpt_trans t ON (t.trans_id = ac.trans_id)
+ |;
+ $dpt_where = qq|
+ AND t.department_id = $department_id
+ |;
+ }
+
+ $query = qq|
+
+ SELECT g.accno, SUM(ac.amount) AS amount,
+ g.description, c.category
+ FROM acc_trans ac
+ JOIN chart c ON (c.id = ac.chart_id)
+ JOIN gifi g ON (c.gifi_accno = g.accno)
+ $dpt_join
+ WHERE $where
+ $ywhere
+ $dpt_where
+ $category
+ $project
+ GROUP BY g.accno, g.description, c.category
+
+ UNION ALL
+
+ SELECT '' AS accno, SUM(ac.amount) AS amount,
+ '' AS description, c.category
+ FROM acc_trans ac
+ JOIN chart c ON (c.id = ac.chart_id)
+ $dpt_join
+ WHERE $where
+ $ywhere
+ $dpt_where
+ $category
+ AND c.gifi_accno = ''
+ $project
+ GROUP BY c.category
+ |;
+
+ if ($excludeyearend) {
+
+ # this is for the yearend
+
+ $query .= qq|
+
+ UNION ALL
+
+ SELECT g.accno, sum(ac.amount) AS amount,
+ g.description, c.category
+ FROM yearend y
+ JOIN gl a ON (a.id = y.trans_id)
+ JOIN acc_trans ac ON (ac.trans_id = y.trans_id)
+ JOIN chart c ON (c.id = ac.chart_id)
+ JOIN gifi g ON (g.accno = c.gifi_accno)
+ $dpt_join
+ WHERE $yearendwhere
+ AND c.category = 'Q'
+ $dpt_where
+ $project
+ GROUP BY g.accno, g.description, c.category
+ |;
+ }
+ }
+
+ } else { # standard account
+
+ if ($form->{method} eq 'cash') {
+
+ $query = qq|
+
+ SELECT c.accno, sum(ac.amount) AS amount,
+ c.description, c.category
+ FROM acc_trans ac
+ JOIN chart c ON (c.id = ac.chart_id)
+ JOIN ar a ON (a.id = ac.trans_id)
+ $dpt_join
+ WHERE $where
+ $ywhere
+ $dpt_where
+ $category
+ AND ac.trans_id IN
+ (
+ SELECT trans_id
+ FROM acc_trans
+ JOIN chart ON (chart_id = id)
+ WHERE link LIKE '%AR_paid%'
+ $subwhere
+ )
+
+ $project
+ GROUP BY c.accno, c.description, c.category
+
+ UNION ALL
+
+ SELECT c.accno, sum(ac.amount) AS amount,
+ c.description, c.category
+ FROM acc_trans ac
+ JOIN chart c ON (c.id = ac.chart_id)
+ JOIN ap a ON (a.id = ac.trans_id)
+ $dpt_join
+ WHERE $where
+ $ywhere
+ $dpt_where
+ $category
+ AND ac.trans_id IN
+ (
+ SELECT trans_id
+ FROM acc_trans
+ JOIN chart ON (chart_id = id)
+ WHERE link LIKE '%AP_paid%'
+ $subwhere
+ )
+
+ $project
+ GROUP BY c.accno, c.description, c.category
+
+ UNION ALL
+
+ SELECT c.accno, sum(ac.amount) AS amount,
+ c.description, c.category
+ FROM acc_trans ac
+ JOIN chart c ON (c.id = ac.chart_id)
+ JOIN gl a ON (a.id = ac.trans_id)
+ $dpt_join
+ WHERE $where
+ $ywhere
+ $glwhere
+ $dpt_where
+ $category
+ AND NOT (c.link = 'AR' OR c.link = 'AP')
+ $project
+ GROUP BY c.accno, c.description, c.category
+ |;
+
+ if ($excludeyearend) {
+
+ # this is for the yearend
+
+ $query .= qq|
+
+ UNION ALL
+
+ SELECT c.accno, sum(ac.amount) AS amount,
+ c.description, c.category
+ FROM yearend y
+ JOIN gl a ON (a.id = y.trans_id)
+ JOIN acc_trans ac ON (ac.trans_id = y.trans_id)
+ JOIN chart c ON (c.id = ac.chart_id)
+ $dpt_join
+ WHERE $yearendwhere
+ AND c.category = 'Q'
+ $dpt_where
+ $project
+ GROUP BY c.accno, c.description, c.category
+ |;
+ }
+
+ } else {
+
+ if ($department_id) {
+ $dpt_join = qq|
+ JOIN dpt_trans t ON (t.trans_id = ac.trans_id)
+ |;
+ $dpt_where = qq|
+ AND t.department_id = $department_id
+ |;
+ }
+
+
+ $query = qq|
+
+ SELECT c.accno, sum(ac.amount) AS amount,
+ c.description, c.category
+ FROM acc_trans ac
+ JOIN chart c ON (c.id = ac.chart_id)
+ $dpt_join
+ WHERE $where
+ $ywhere
+ $dpt_where
+ $category
+ $project
+ GROUP BY c.accno, c.description, c.category
+ |;
+
+ if ($excludeyearend) {
+
+ # this is for the yearend
+
+ $query .= qq|
+
+ UNION ALL
+
+ SELECT c.accno, sum(ac.amount) AS amount,
+ c.description, c.category
+ FROM yearend y
+ JOIN gl a ON (a.id = y.trans_id)
+ JOIN acc_trans ac ON (ac.trans_id = y.trans_id)
+ JOIN chart c ON (c.id = ac.chart_id)
+ $dpt_join
+ WHERE $yearendwhere
+ AND c.category = 'Q'
+ $dpt_where
+ $project
+ GROUP BY c.accno, c.description, c.category
+ |;
+ }
+ }
+ }
+
+ my @accno;
+ my $accno;
+ my $ref;
+
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+
+ # get last heading account
+ @accno = grep { $_ le "$ref->{accno}" } @headingaccounts;
+ $accno = pop @accno;
+ if ($accno && ($accno ne $ref->{accno}) ) {
+ if ($last_period)
+ {
+ $form->{$ref->{category}}{$accno}{last} += $ref->{amount};
+ } else {
+ $form->{$ref->{category}}{$accno}{this} += $ref->{amount};
+ }
+ }
+
+ $form->{$ref->{category}}{$ref->{accno}}{accno} = $ref->{accno};
+ $form->{$ref->{category}}{$ref->{accno}}{description} = $ref->{description};
+ $form->{$ref->{category}}{$ref->{accno}}{charttype} = "A";
+
+ if ($last_period) {
+ $form->{$ref->{category}}{$ref->{accno}}{last} += $ref->{amount};
+ } else {
+ $form->{$ref->{category}}{$ref->{accno}}{this} += $ref->{amount};
+ }
+ }
+ $sth->finish;
+
+
+ # remove accounts with zero balance
+ foreach $category (@{ $categories }) {
+ foreach $accno (keys %{ $form->{$category} }) {
+ $form->{$category}{$accno}{last} = $form->round_amount($form->{$category}{$accno}{last}, $form->{decimalplaces});
+ $form->{$category}{$accno}{this} = $form->round_amount($form->{$category}{$accno}{this}, $form->{decimalplaces});
+
+ delete $form->{$category}{$accno} if ($form->{$category}{$accno}{this} == 0 && $form->{$category}{$accno}{last} == 0);
+ }
+ }
+
+}
+
+
+
+sub trial_balance {
+ my ($self, $myconfig, $form) = @_;
+
+ my $dbh = $form->dbconnect($myconfig);
+
+ my ($query, $sth, $ref);
+ my %balance = ();
+ my %trb = ();
+ my $null;
+ my $department_id;
+ my $project_id;
+ my @headingaccounts = ();
+ my $dpt_where;
+ my $dpt_join;
+ my $project;
+
+ my $where = "1 = 1";
+ my $invwhere = $where;
+
+ ($null, $department_id) = split /--/, $form->{department};
+ ($null, $project_id) = split /--/, $form->{projectnumber};
+
+ if ($department_id) {
+ $dpt_join = qq|
+ JOIN dpt_trans t ON (ac.trans_id = t.trans_id)
+ |;
+ $dpt_where = qq|
+ AND t.department_id = $department_id
+ |;
+ }
+
+
+ if ($project_id) {
+ $project = qq|
+ AND ac.project_id = $project_id
+ |;
+ }
+
+ ($form->{fromdate}, $form->{todate}) = $form->from_to($form->{year}, $form->{month}, $form->{interval}) if $form->{year} && $form->{month};
+
+ # get beginning balances
+ if ($form->{fromdate}) {
+
+ if ($form->{accounttype} eq 'gifi') {
+
+ $query = qq|SELECT g.accno, c.category, SUM(ac.amount) AS amount,
+ g.description, c.contra
+ FROM acc_trans ac
+ JOIN chart c ON (ac.chart_id = c.id)
+ JOIN gifi g ON (c.gifi_accno = g.accno)
+ $dpt_join
+ WHERE ac.transdate < '$form->{fromdate}'
+ $dpt_where
+ $project
+ GROUP BY g.accno, c.category, g.description, c.contra
+ |;
+
+ } else {
+
+ $query = qq|SELECT c.accno, c.category, SUM(ac.amount) AS amount,
+ c.description, c.contra
+ FROM acc_trans ac
+ JOIN chart c ON (ac.chart_id = c.id)
+ $dpt_join
+ WHERE ac.transdate < '$form->{fromdate}'
+ $dpt_where
+ $project
+ GROUP BY c.accno, c.category, c.description, c.contra
+ |;
+
+ }
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
+ $ref->{amount} = $form->round_amount($ref->{amount}, 2);
+ $balance{$ref->{accno}} = $ref->{amount};
+
+ if ($form->{all_accounts}) {
+ $trb{$ref->{accno}}{description} = $ref->{description};
+ $trb{$ref->{accno}}{charttype} = 'A';
+ $trb{$ref->{accno}}{category} = $ref->{category};
+ $trb{$ref->{accno}}{contra} = $ref->{contra};
+ }
+
+ }
+ $sth->finish;
+
+ }
+
+
+ # get headings
+ $query = qq|SELECT c.accno, c.description, c.category
+ FROM chart c
+ WHERE c.charttype = 'H'
+ ORDER by c.accno|;
+
+ if ($form->{accounttype} eq 'gifi')
+ {
+ $query = qq|SELECT g.accno, g.description, c.category, c.contra
+ FROM gifi g
+ JOIN chart c ON (c.gifi_accno = g.accno)
+ WHERE c.charttype = 'H'
+ ORDER BY g.accno|;
+ }
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while ($ref = $sth->fetchrow_hashref(NAME_lc))
+ {
+ $trb{$ref->{accno}}{description} = $ref->{description};
+ $trb{$ref->{accno}}{charttype} = 'H';
+ $trb{$ref->{accno}}{category} = $ref->{category};
+ $trb{$ref->{accno}}{contra} = $ref->{contra};
+
+ push @headingaccounts, $ref->{accno};
+ }
+
+ $sth->finish;
+
+
+ if ($form->{fromdate} || $form->{todate}) {
+ if ($form->{fromdate}) {
+ $where .= " AND ac.transdate >= '$form->{fromdate}'";
+ $invwhere .= " AND a.transdate >= '$form->{fromdate}'";
+ }
+ if ($form->{todate}) {
+ $where .= " AND ac.transdate <= '$form->{todate}'";
+ $invwhere .= " AND a.transdate <= '$form->{todate}'";
+ }
+ }
+
+
+ if ($form->{accounttype} eq 'gifi') {
+
+ $query = qq|SELECT g.accno, g.description, c.category,
+ SUM(ac.amount) AS amount, c.contra
+ FROM acc_trans ac
+ JOIN chart c ON (c.id = ac.chart_id)
+ JOIN gifi g ON (c.gifi_accno = g.accno)
+ $dpt_join
+ WHERE $where
+ $dpt_where
+ $project
+ GROUP BY g.accno, g.description, c.category, c.contra
+ ORDER BY accno|;
+
+ } else {
+
+ $query = qq|SELECT c.accno, c.description, c.category,
+ SUM(ac.amount) AS amount, c.contra
+ FROM acc_trans ac
+ JOIN chart c ON (c.id = ac.chart_id)
+ $dpt_join
+ WHERE $where
+ $dpt_where
+ $project
+ GROUP BY c.accno, c.description, c.category, c.contra
+ ORDER BY accno|;
+
+ }
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ # prepare query for each account
+ $query = qq|SELECT (SELECT SUM(ac.amount) * -1
+ FROM acc_trans ac
+ JOIN chart c ON (c.id = ac.chart_id)
+ $dpt_join
+ WHERE $where
+ $dpt_where
+ $project
+ AND ac.amount < 0
+ AND c.accno = ?) AS debit,
+
+ (SELECT SUM(ac.amount)
+ FROM acc_trans ac
+ JOIN chart c ON (c.id = ac.chart_id)
+ $dpt_join
+ WHERE $where
+ $dpt_where
+ $project
+ AND ac.amount > 0
+ AND c.accno = ?) AS credit
+ |;
+
+ if ($form->{accounttype} eq 'gifi') {
+
+ $query = qq|SELECT (SELECT SUM(ac.amount) * -1
+ FROM acc_trans ac
+ JOIN chart c ON (c.id = ac.chart_id)
+ $dpt_join
+ WHERE $where
+ $dpt_where
+ $project
+ AND ac.amount < 0
+ AND c.gifi_accno = ?) AS debit,
+
+ (SELECT SUM(ac.amount)
+ FROM acc_trans ac
+ JOIN chart c ON (c.id = ac.chart_id)
+ $dpt_join
+ WHERE $where
+ $dpt_where
+ $project
+ AND ac.amount > 0
+ AND c.gifi_accno = ?) AS credit|;
+
+ }
+
+ $drcr = $dbh->prepare($query);
+
+ # calculate debit and credit for the period
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+ $trb{$ref->{accno}}{description} = $ref->{description};
+ $trb{$ref->{accno}}{charttype} = 'A';
+ $trb{$ref->{accno}}{category} = $ref->{category};
+ $trb{$ref->{accno}}{contra} = $ref->{contra};
+ $trb{$ref->{accno}}{amount} += $ref->{amount};
+ }
+ $sth->finish;
+
+ my ($debit, $credit);
+
+ foreach my $accno (sort keys %trb) {
+ $ref = ();
+
+ $ref->{accno} = $accno;
+ for (qw(description category contra charttype amount)) { $ref->{$_} = $trb{$accno}{$_} }
+
+ $ref->{balance} = $balance{$ref->{accno}};
+
+ if ($trb{$accno}{charttype} eq 'A') {
+ if ($project_id) {
+
+ if ($ref->{amount} < 0) {
+ $ref->{debit} = $ref->{amount} * -1;
+ } else {
+ $ref->{credit} = $ref->{amount};
+ }
+ next if $form->round_amount($ref->{amount}, 2) == 0;
+
+ } else {
+
+ # get DR/CR
+ $drcr->execute($ref->{accno}, $ref->{accno});
+
+ ($debit, $credit) = (0,0);
+ while (($debit, $credit) = $drcr->fetchrow_array) {
+ $ref->{debit} += $debit;
+ $ref->{credit} += $credit;
+ }
+ $drcr->finish;
+
+ }
+
+ $ref->{debit} = $form->round_amount($ref->{debit}, 2);
+ $ref->{credit} = $form->round_amount($ref->{credit}, 2);
+
+ if (!$form->{all_accounts}) {
+ next if $form->round_amount($ref->{debit} + $ref->{credit}, 2) == 0;
+ }
+ }
+
+ # add subtotal
+ @accno = grep { $_ le "$ref->{accno}" } @headingaccounts;
+ $accno = pop @accno;
+ if ($accno) {
+ $trb{$accno}{debit} += $ref->{debit};
+ $trb{$accno}{credit} += $ref->{credit};
+ }
+
+ push @{ $form->{TB} }, $ref;
+
+ }
+
+ $dbh->disconnect;
+
+ # debits and credits for headings
+ foreach $accno (@headingaccounts) {
+ foreach $ref (@{ $form->{TB} }) {
+ if ($accno eq $ref->{accno}) {
+ $ref->{debit} = $trb{$accno}{debit};
+ $ref->{credit} = $trb{$accno}{credit};
+ }
+ }
+ }
+
+}
+
+
+sub aging {
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+ my $invoice = ($form->{arap} eq 'ar') ? 'is' : 'ir';
+
+ my $query = qq|SELECT curr FROM defaults|;
+ ($form->{currencies}) = $dbh->selectrow_array($query);
+
+ ($null, $form->{todate}) = $form->from_to($form->{year}, $form->{month}) if $form->{year} && $form->{month};
+
+ if (! $form->{todate}) {
+ $query = qq|SELECT current_date FROM defaults|;
+ ($form->{todate}) = $dbh->selectrow_array($query);
+ }
+
+ my $where = "1 = 1";
+ my $name;
+ my $null;
+ my $ref;
+ my $transdate = ($form->{overdue}) ? "duedate" : "transdate";
+
+ if ($form->{"$form->{ct}_id"}) {
+ $where .= qq| AND ct.id = $form->{"$form->{ct}_id"}|;
+ } else {
+ if ($form->{$form->{ct}} ne "") {
+ $name = $form->like(lc $form->{$form->{ct}});
+ $where .= qq| AND lower(ct.name) LIKE '$name'| if $form->{$form->{ct}};
+ }
+ }
+
+ if ($form->{department}) {
+ ($null, $department_id) = split /--/, $form->{department};
+ $where .= qq| AND a.department_id = $department_id|;
+ }
+
+ # select outstanding vendors or customers, depends on $ct
+ $query = qq|SELECT DISTINCT ct.id, ct.name, ct.language_code
+ FROM $form->{ct} ct
+ JOIN $form->{arap} a ON (a.$form->{ct}_id = ct.id)
+ WHERE $where
+ AND a.paid != a.amount
+ AND (a.$transdate <= '$form->{todate}')
+ ORDER BY ct.name|;
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror;
+
+ my @ot = ();
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+ push @ot, $ref;
+ }
+ $sth->finish;
+
+ my $buysell = ($form->{arap} eq 'ar') ? 'buy' : 'sell';
+
+ my %interval = ( 'Pg' => {
+ 'c0' => "(date '$form->{todate}' - interval '0 days')",
+ 'c30' => "(date '$form->{todate}' - interval '30 days')",
+ 'c60' => "(date '$form->{todate}' - interval '60 days')",
+ 'c90' => "(date '$form->{todate}' - interval '90 days')" },
+ 'DB2' => {
+ 'c0' => "(date ('$form->{todate}') - 0 days)",
+ 'c30' => "(date ('$form->{todate}') - 30 days)",
+ 'c60' => "(date ('$form->{todate}') - 60 days)",
+ 'c90' => "(date ('$form->{todate}') - 90 days)" }
+ );
+
+ $interval{Oracle} = $interval{PgPP} = $interval{Pg};
+
+
+ # for each company that has some stuff outstanding
+ $form->{currencies} ||= ":";
+
+
+ $where = qq|
+ a.paid != a.amount
+ AND c.id = ?
+ AND a.curr = ?|;
+
+ if ($department_id) {
+ $where .= qq| AND a.department_id = $department_id|;
+ }
+
+ $query = "";
+ my $union = "";
+
+ if ($form->{c0}) {
+ $query .= qq|
+ SELECT c.id AS ctid, c.$form->{ct}number, c.name,
+ c.address1, c.address2, c.city, c.state, c.zipcode, c.country,
+ c.contact, c.email,
+ c.phone as $form->{ct}phone, c.fax as $form->{ct}fax,
+ c.$form->{ct}number, c.taxnumber as $form->{ct}taxnumber,
+ a.invnumber, a.transdate, a.till, a.ordnumber, a.ponumber, a.notes,
+ (a.amount - a.paid) as c0, 0.00 as c30, 0.00 as c60, 0.00 as c90,
+ a.duedate, a.invoice, a.id, a.curr,
+ (SELECT $buysell FROM exchangerate e
+ WHERE a.curr = e.curr
+ AND e.transdate = a.transdate) AS exchangerate
+ FROM $form->{arap} a
+ JOIN $form->{ct} c ON (a.$form->{ct}_id = c.id)
+ WHERE $where
+ AND (
+ a.$transdate <= $interval{$myconfig->{dbdriver}}{c0}
+ AND a.$transdate >= $interval{$myconfig->{dbdriver}}{c30}
+ )
+|;
+
+ $union = qq|
+ UNION
+|;
+
+ }
+
+ if ($form->{c30}) {
+
+ $query .= qq|
+
+ $union
+
+ SELECT c.id AS ctid, c.$form->{ct}number, c.name,
+ c.address1, c.address2, c.city, c.state, c.zipcode, c.country,
+ c.contact, c.email,
+ c.phone as $form->{ct}phone, c.fax as $form->{ct}fax,
+ c.$form->{ct}number, c.taxnumber as $form->{ct}taxnumber,
+ a.invnumber, a.transdate, a.till, a.ordnumber, a.ponumber, a.notes,
+ 0.00 as c0, (a.amount - a.paid) as c30, 0.00 as c60, 0.00 as c90,
+ a.duedate, a.invoice, a.id, a.curr,
+ (SELECT $buysell FROM exchangerate e
+ WHERE a.curr = e.curr
+ AND e.transdate = a.transdate) AS exchangerate
+ FROM $form->{arap} a
+ JOIN $form->{ct} c ON (a.$form->{ct}_id = c.id)
+ WHERE $where
+ AND (
+ a.$transdate < $interval{$myconfig->{dbdriver}}{c30}
+ AND a.$transdate >= $interval{$myconfig->{dbdriver}}{c60}
+ )
+|;
+
+ $union = qq|
+ UNION
+|;
+
+ }
+
+ if ($form->{c60}) {
+
+ $query .= qq|
+
+ $union
+
+ SELECT c.id AS ctid, c.$form->{ct}number, c.name,
+ c.address1, c.address2, c.city, c.state, c.zipcode, c.country,
+ c.contact, c.email,
+ c.phone as $form->{ct}phone, c.fax as $form->{ct}fax,
+ c.$form->{ct}number, c.taxnumber as $form->{ct}taxnumber,
+ a.invnumber, a.transdate, a.till, a.ordnumber, a.ponumber, a.notes,
+ 0.00 as c0, 0.00 as c30, (a.amount - a.paid) as c60, 0.00 as c90,
+ a.duedate, a.invoice, a.id, a.curr,
+ (SELECT $buysell FROM exchangerate e
+ WHERE a.curr = e.curr
+ AND e.transdate = a.transdate) AS exchangerate
+ FROM $form->{arap} a
+ JOIN $form->{ct} c ON (a.$form->{ct}_id = c.id)
+ WHERE $where
+ AND (
+ a.$transdate < $interval{$myconfig->{dbdriver}}{c60}
+ AND a.$transdate >= $interval{$myconfig->{dbdriver}}{c90}
+ )
+|;
+
+ $union = qq|
+ UNION
+|;
+
+ }
+
+ if ($form->{c90}) {
+
+ $query .= qq|
+
+ $union
+
+ SELECT c.id AS ctid, c.$form->{ct}number, c.name,
+ c.address1, c.address2, c.city, c.state, c.zipcode, c.country,
+ c.contact, c.email,
+ c.phone as $form->{ct}phone, c.fax as $form->{ct}fax,
+ c.$form->{ct}number, c.taxnumber as $form->{ct}taxnumber,
+ a.invnumber, a.transdate, a.till, a.ordnumber, a.ponumber, a.notes,
+ 0.00 as c0, 0.00 as c30, 0.00 as c60, (a.amount - a.paid) as c90,
+ a.duedate, a.invoice, a.id, a.curr,
+ (SELECT $buysell FROM exchangerate e
+ WHERE a.curr = e.curr
+ AND e.transdate = a.transdate) AS exchangerate
+ FROM $form->{arap} a
+ JOIN $form->{ct} c ON (a.$form->{ct}_id = c.id)
+ WHERE $where
+ AND a.$transdate < $interval{$myconfig->{dbdriver}}{c90}
+|;
+ }
+
+ $query .= qq|
+
+ ORDER BY ctid, $transdate, invnumber|;
+
+ $sth = $dbh->prepare($query) || $form->dberror($query);
+
+ my @var = ();
+
+ if ($form->{c0} + $form->{c30} + $form->{c60} + $form->{c90}) {
+ foreach $curr (split /:/, $form->{currencies}) {
+
+ foreach $item (@ot) {
+
+ @var = ();
+ for (qw(c0 c30 c60 c90)) { push @var, ($item->{id}, $curr) if $form->{$_} }
+
+ $sth->execute(@var);
+
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+ $ref->{module} = ($ref->{invoice}) ? $invoice : $form->{arap};
+ $ref->{module} = 'ps' if $ref->{till};
+ $ref->{exchangerate} = 1 unless $ref->{exchangerate};
+ $ref->{language_code} = $item->{language_code};
+ push @{ $form->{AG} }, $ref;
+ }
+ $sth->finish;
+
+ }
+ }
+ }
+
+ # get language
+ my $query = qq|SELECT *
+ FROM language
+ ORDER BY 2|;
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+ push @{ $form->{all_language} }, $ref;
+ }
+ $sth->finish;
+
+ # disconnect
+ $dbh->disconnect;
+
+}
+
+
+sub get_customer {
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ my $query = qq|SELECT name, email, cc, bcc
+ FROM $form->{ct} ct
+ WHERE ct.id = $form->{"$form->{ct}_id"}|;
+ ($form->{$form->{ct}}, $form->{email}, $form->{cc}, $form->{bcc}) = $dbh->selectrow_array($query);
+
+ $dbh->disconnect;
+
+}
+
+
+sub get_taxaccounts {
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+ my $ARAP = uc $form->{db};
+
+ # get tax accounts
+ my $query = qq|SELECT DISTINCT c.accno, c.description
+ FROM chart c
+ JOIN tax t ON (c.id = t.chart_id)
+ WHERE c.link LIKE '%${ARAP}_tax%'
+ ORDER BY c.accno|;
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror;
+
+ my $ref = ();
+ while ($ref = $sth->fetchrow_hashref(NAME_lc) ) {
+ push @{ $form->{taxaccounts} }, $ref;
+ }
+ $sth->finish;
+
+ # get gifi tax accounts
+ my $query = qq|SELECT DISTINCT g.accno, g.description
+ FROM gifi g
+ JOIN chart c ON (c.gifi_accno= g.accno)
+ JOIN tax t ON (c.id = t.chart_id)
+ WHERE c.link LIKE '%${ARAP}_tax%'
+ ORDER BY accno|;
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror;
+
+ while ($ref = $sth->fetchrow_hashref(NAME_lc) ) {
+ push @{ $form->{gifi_taxaccounts} }, $ref;
+ }
+ $sth->finish;
+
+ $dbh->disconnect;
+
+}
+
+
+
+sub tax_report {
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database
+ my $dbh = $form->dbconnect($myconfig);
+
+ my ($null, $department_id) = split /--/, $form->{department};
+
+ # build WHERE
+ my $where = "1 = 1";
+ my $cashwhere = "";
+
+ if ($department_id) {
+ $where .= qq|
+ AND a.department_id = $department_id
+ |;
+ }
+
+ my $query;
+ my $sth;
+ my $accno;
+
+ if ($form->{accno}) {
+ if ($form->{accno} =~ /^gifi_/) {
+ ($null, $accno) = split /_/, $form->{accno};
+ $accno = qq| AND ch.gifi_accno = '$accno'|;
+ } else {
+ $accno = $form->{accno};
+ $accno = qq| AND ch.accno = '$accno'|;
+ }
+ }
+
+ my $table;
+ my $ARAP;
+
+ if ($form->{db} eq 'ar') {
+ $table = "customer";
+ $ARAP = "AR";
+ }
+ if ($form->{db} eq 'ap') {
+ $table = "vendor";
+ $ARAP = "AP";
+ }
+
+ my $transdate = "a.transdate";
+
+ ($form->{fromdate}, $form->{todate}) = $form->from_to($form->{year}, $form->{month}, $form->{interval}) if $form->{year} && $form->{month};
+
+ # if there are any dates construct a where
+ if ($form->{fromdate} || $form->{todate}) {
+ if ($form->{fromdate}) {
+ $where .= " AND $transdate >= '$form->{fromdate}'";
+ }
+ if ($form->{todate}) {
+ $where .= " AND $transdate <= '$form->{todate}'";
+ }
+ }
+
+
+ if ($form->{method} eq 'cash') {
+ $transdate = "a.datepaid";
+
+ my $todate = $form->{todate};
+ if (! $todate) {
+ ($todate) = $dbh->selectrow_array(qq|SELECT current_date FROM defaults|);
+ }
+
+ $cashwhere = qq|
+ AND ac.trans_id IN
+ (
+ SELECT trans_id
+ FROM acc_trans
+ JOIN chart ON (chart_id = chart.id)
+ WHERE link LIKE '%${ARAP}_paid%'
+ AND $transdate <= '$todate'
+ AND a.paid = a.amount
+ )
+ |;
+
+ }
+
+
+ my $ml = ($form->{db} eq 'ar') ? 1 : -1;
+
+ my %ordinal = ( 'transdate' => 3,
+ 'invnumber' => 4,
+ 'name' => 5
+ );
+
+ my @a = qw(transdate invnumber name);
+ my $sortorder = $form->sort_order(\@a, \%ordinal);
+
+ if ($form->{summary}) {
+
+ $query = qq|SELECT a.id, a.invoice, $transdate AS transdate,
+ a.invnumber, n.name, a.netamount,
+ ac.amount * $ml AS tax,
+ a.till
+ FROM acc_trans ac
+ JOIN $form->{db} a ON (a.id = ac.trans_id)
+ JOIN chart ch ON (ch.id = ac.chart_id)
+ JOIN $table n ON (n.id = a.${table}_id)
+ WHERE $where
+ $accno
+ $cashwhere
+ |;
+
+ if ($form->{fromdate}) {
+ # include open transactions from previous period
+ if ($cashwhere) {
+ $query .= qq|
+ UNION
+
+ SELECT a.id, a.invoice, $transdate AS transdate,
+ a.invnumber, n.name, a.netamount,
+ ac.amount * $ml AS tax,
+ a.till
+ FROM acc_trans ac
+ JOIN $form->{db} a ON (a.id = ac.trans_id)
+ JOIN chart ch ON (ch.id = ac.chart_id)
+ JOIN $table n ON (n.id = a.${table}_id)
+ WHERE a.datepaid >= '$form->{fromdate}'
+ $accno
+ $cashwhere
+ |;
+ }
+ }
+
+
+ } else {
+
+ $query = qq|SELECT a.id, '0' AS invoice, $transdate AS transdate,
+ a.invnumber, n.name, a.netamount,
+ ac.amount * $ml AS tax,
+ a.notes AS description, a.till
+ FROM acc_trans ac
+ JOIN $form->{db} a ON (a.id = ac.trans_id)
+ JOIN chart ch ON (ch.id = ac.chart_id)
+ JOIN $table n ON (n.id = a.${table}_id)
+ WHERE $where
+ $accno
+ AND a.invoice = '0'
+ $cashwhere
+
+ UNION
+
+ SELECT a.id, '1' AS invoice, $transdate AS transdate,
+ a.invnumber, n.name,
+ i.sellprice * i.qty * $ml AS netamount,
+ i.sellprice * i.qty * $ml *
+ (SELECT tx.rate FROM tax tx WHERE tx.chart_id = ch.id AND (tx.validto > $transdate OR tx.validto IS NULL) ORDER BY validto LIMIT 1) AS tax,
+ i.description, a.till
+ FROM acc_trans ac
+ JOIN $form->{db} a ON (a.id = ac.trans_id)
+ JOIN chart ch ON (ch.id = ac.chart_id)
+ JOIN $table n ON (n.id = a.${table}_id)
+ JOIN ${table}tax t ON (t.${table}_id = n.id AND t.chart_id = ch.id)
+ JOIN invoice i ON (i.trans_id = a.id)
+ JOIN partstax pt ON (pt.parts_id = i.parts_id AND pt.chart_id = ch.id)
+ WHERE $where
+ $accno
+ AND a.invoice = '1'
+ $cashwhere
+ |;
+
+ if ($form->{fromdate}) {
+ if ($cashwhere) {
+ $query .= qq|
+ UNION
+
+ SELECT a.id, '0' AS invoice, $transdate AS transdate,
+ a.invnumber, n.name, a.netamount,
+ ac.amount * $ml AS tax,
+ a.notes AS description, a.till
+ FROM acc_trans ac
+ JOIN $form->{db} a ON (a.id = ac.trans_id)
+ JOIN chart ch ON (ch.id = ac.chart_id)
+ JOIN $table n ON (n.id = a.${table}_id)
+ WHERE a.datepaid >= '$form->{fromdate}'
+ $accno
+ AND a.invoice = '0'
+ $cashwhere
+
+ UNION
+
+ SELECT a.id, '1' AS invoice, $transdate AS transdate,
+ a.invnumber, n.name,
+ i.sellprice * i.qty * $ml AS netamount,
+ i.sellprice * i.qty * $ml *
+ (SELECT tx.rate FROM tax tx WHERE tx.chart_id = ch.id AND (tx.validto > $transdate OR tx.validto IS NULL) ORDER BY validto LIMIT 1) AS tax,
+ i.description, a.till
+ FROM acc_trans ac
+ JOIN $form->{db} a ON (a.id = ac.trans_id)
+ JOIN chart ch ON (ch.id = ac.chart_id)
+ JOIN $table n ON (n.id = a.${table}_id)
+ JOIN ${table}tax t ON (t.${table}_id = n.id AND t.chart_id = ch.id)
+ JOIN invoice i ON (i.trans_id = a.id)
+ JOIN partstax pt ON (pt.parts_id = i.parts_id AND pt.chart_id = ch.id)
+ WHERE a.datepaid >= '$form->{fromdate}'
+ $accno
+ AND a.invoice = '1'
+ $cashwhere
+ |;
+ }
+ }
+ }
+
+
+ if ($form->{report} =~ /nontaxable/) {
+
+ if ($form->{summary}) {
+ # only gather up non-taxable transactions
+ $query = qq|SELECT DISTINCT a.id, a.invoice, $transdate AS transdate,
+ a.invnumber, n.name, a.netamount, a.till
+ FROM acc_trans ac
+ JOIN $form->{db} a ON (a.id = ac.trans_id)
+ JOIN $table n ON (n.id = a.${table}_id)
+ WHERE $where
+ AND a.netamount = a.amount
+ $cashwhere
+ |;
+
+ if ($form->{fromdate}) {
+ if ($cashwhere) {
+ $query .= qq|
+ UNION
+
+ SELECT DISTINCT a.id, a.invoice, $transdate AS transdate,
+ a.invnumber, n.name, a.netamount, a.till
+ FROM acc_trans ac
+ JOIN $form->{db} a ON (a.id = ac.trans_id)
+ JOIN $table n ON (n.id = a.${table}_id)
+ WHERE a.datepaid >= '$form->{fromdate}'
+ AND a.netamount = a.amount
+ $cashwhere
+ |;
+ }
+ }
+
+ } else {
+
+ # gather up details for non-taxable transactions
+ $query = qq|SELECT a.id, '0' AS invoice, $transdate AS transdate,
+ a.invnumber, n.name, a.netamount,
+ a.notes AS description, a.till
+ FROM acc_trans ac
+ JOIN $form->{db} a ON (a.id = ac.trans_id)
+ JOIN $table n ON (n.id = a.${table}_id)
+ WHERE $where
+ AND a.invoice = '0'
+ AND a.netamount = a.amount
+ $cashwhere
+ GROUP BY a.id, $transdate, a.invnumber, n.name, a.netamount,
+ a.notes, a.till
+
+ UNION
+
+ SELECT a.id, '1' AS invoice, $transdate AS transdate,
+ a.invnumber, n.name,
+ sum(ac.sellprice * ac.qty) * $ml AS netamount,
+ ac.description, a.till
+ FROM invoice ac
+ JOIN $form->{db} a ON (a.id = ac.trans_id)
+ JOIN $table n ON (n.id = a.${table}_id)
+ WHERE $where
+ AND a.invoice = '1'
+ AND (
+ a.${table}_id NOT IN (
+ SELECT ${table}_id FROM ${table}tax t (${table}_id)
+ ) OR
+ ac.parts_id NOT IN (
+ SELECT parts_id FROM partstax p (parts_id)
+ )
+ )
+ $cashwhere
+ GROUP BY a.id, a.invnumber, $transdate, n.name,
+ ac.description, a.till
+ |;
+
+ if ($form->{fromdate}) {
+ if ($cashwhere) {
+ $query .= qq|
+ UNION
+
+ SELECT a.id, '0' AS invoice, $transdate AS transdate,
+ a.invnumber, n.name, a.netamount,
+ a.notes AS description, a.till
+ FROM acc_trans ac
+ JOIN $form->{db} a ON (a.id = ac.trans_id)
+ JOIN $table n ON (n.id = a.${table}_id)
+ WHERE a.datepaid >= '$form->{fromdate}'
+ AND a.invoice = '0'
+ AND a.netamount = a.amount
+ $cashwhere
+ GROUP BY a.id, $transdate, a.invnumber, n.name, a.netamount,
+ a.notes, a.till
+
+ UNION
+
+ SELECT a.id, '1' AS invoice, $transdate AS transdate,
+ a.invnumber, n.name,
+ sum(ac.sellprice * ac.qty) * $ml AS netamount,
+ ac.description, a.till
+ FROM invoice ac
+ JOIN $form->{db} a ON (a.id = ac.trans_id)
+ JOIN $table n ON (n.id = a.${table}_id)
+ WHERE a.datepaid >= '$form->{fromdate}'
+ AND a.invoice = '1'
+ AND (
+ a.${table}_id NOT IN (
+ SELECT ${table}_id FROM ${table}tax t (${table}_id)
+ ) OR
+ ac.parts_id NOT IN (
+ SELECT parts_id FROM partstax p (parts_id)
+ )
+ )
+ $cashwhere
+ GROUP BY a.id, a.invnumber, $transdate, n.name,
+ ac.description, a.till
+ |;
+ }
+ }
+
+ }
+ }
+
+
+ $query .= qq|
+ ORDER by $sortorder|;
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while ( my $ref = $sth->fetchrow_hashref(NAME_lc)) {
+ $ref->{tax} = $form->round_amount($ref->{tax}, 2);
+ if ($form->{report} =~ /nontaxable/) {
+ push @{ $form->{TR} }, $ref if $ref->{netamount};
+ } else {
+ push @{ $form->{TR} }, $ref if $ref->{tax};
+ }
+ }
+
+ $sth->finish;
+ $dbh->disconnect;
+
+}
+
+
+sub paymentaccounts {
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database, turn AutoCommit off
+ my $dbh = $form->dbconnect_noauto($myconfig);
+
+ my $ARAP = uc $form->{db};
+
+ # get A(R|P)_paid accounts
+ my $query = qq|SELECT accno, description
+ FROM chart
+ WHERE link LIKE '%${ARAP}_paid%'
+ ORDER BY accno|;
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
+ push @{ $form->{PR} }, $ref;
+ }
+ $sth->finish;
+
+ $form->all_years($myconfig, $dbh);
+
+ $dbh->disconnect;
+
+}
+
+
+sub payments {
+ my ($self, $myconfig, $form) = @_;
+
+ # connect to database, turn AutoCommit off
+ my $dbh = $form->dbconnect_noauto($myconfig);
+
+ my $ml = 1;
+ if ($form->{db} eq 'ar') {
+ $table = 'customer';
+ $ml = -1;
+ }
+ if ($form->{db} eq 'ap') {
+ $table = 'vendor';
+ }
+
+
+ my $query;
+ my $sth;
+ my $dpt_join;
+ my $where;
+ my $var;
+
+ if ($form->{department_id}) {
+ $dpt_join = qq|
+ JOIN dpt_trans t ON (t.trans_id = ac.trans_id)
+ |;
+
+ $where = qq|
+ AND t.department_id = $form->{department_id}
+ |;
+ }
+
+ ($form->{fromdate}, $form->{todate}) = $form->from_to($form->{year}, $form->{month}, $form->{interval}) if $form->{year} && $form->{month};
+
+ if ($form->{fromdate}) {
+ $where .= " AND ac.transdate >= '$form->{fromdate}'";
+ }
+ if ($form->{todate}) {
+ $where .= " AND ac.transdate <= '$form->{todate}'";
+ }
+ if (!$form->{fx_transaction}) {
+ $where .= " AND ac.fx_transaction = '0'";
+ }
+
+ if ($form->{description} ne "") {
+ $var = $form->like(lc $form->{description});
+ $where .= " AND lower(c.name) LIKE '$var'";
+ }
+ if ($form->{source} ne "") {
+ $var = $form->like(lc $form->{source});
+ $where .= " AND lower(ac.source) LIKE '$var'";
+ }
+ if ($form->{memo} ne "") {
+ $var = $form->like(lc $form->{memo});
+ $where .= " AND lower(ac.memo) LIKE '$var'";
+ }
+
+ my %ordinal = ( 'name' => 1,
+ 'transdate' => 2,
+ 'source' => 4,
+ 'employee' => 6,
+ 'till' => 7
+ );
+
+ my @a = qw(name transdate employee);
+ my $sortorder = $form->sort_order(\@a, \%ordinal);
+
+ my $glwhere = $where;
+ $glwhere =~ s/\(c.name\)/\(g.description\)/;
+
+ # cycle through each id
+ foreach my $accno (split(/ /, $form->{paymentaccounts})) {
+
+ $query = qq|SELECT id, accno, description
+ FROM chart
+ WHERE accno = '$accno'|;
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ my $ref = $sth->fetchrow_hashref(NAME_lc);
+ push @{ $form->{PR} }, $ref;
+ $sth->finish;
+
+ $query = qq|SELECT c.name, ac.transdate, sum(ac.amount) * $ml AS paid,
+ ac.source, ac.memo, e.name AS employee, a.till, a.curr
+ FROM acc_trans ac
+ JOIN $form->{db} a ON (ac.trans_id = a.id)
+ JOIN $table c ON (c.id = a.${table}_id)
+ LEFT JOIN employee e ON (a.employee_id = e.id)
+ $dpt_join
+ WHERE ac.chart_id = $ref->{id}
+ $where|;
+
+ if ($form->{till} ne "") {
+ $query .= " AND a.invoice = '1'
+ AND NOT a.till IS NULL";
+
+ if ($myconfig->{role} eq 'user') {
+ $query .= " AND e.login = '$form->{login}'";
+ }
+ }
+
+ $query .= qq|
+ GROUP BY c.name, ac.transdate, ac.source, ac.memo,
+ e.name, a.till, a.curr
+ |;
+
+ if ($form->{till} eq "") {
+# don't need gl for a till
+
+ $query .= qq|
+ UNION
+ SELECT g.description, ac.transdate, sum(ac.amount) * $ml AS paid, ac.source,
+ ac.memo, e.name AS employee, '' AS till, '' AS curr
+ FROM acc_trans ac
+ JOIN gl g ON (g.id = ac.trans_id)
+ LEFT JOIN employee e ON (g.employee_id = e.id)
+ $dpt_join
+ WHERE ac.chart_id = $ref->{id}
+ $glwhere
+ AND (ac.amount * $ml) > 0
+ GROUP BY g.description, ac.transdate, ac.source, ac.memo, e.name
+ |;
+
+ }
+
+ $query .= qq|
+ ORDER BY $sortorder|;
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while (my $pr = $sth->fetchrow_hashref(NAME_lc)) {
+ push @{ $form->{$ref->{id}} }, $pr;
+ }
+ $sth->finish;
+
+ }
+
+ $dbh->disconnect;
+
+}
+
+
+1;
+
+
diff --git a/LedgerSMB/Session.pm b/LedgerSMB/Session.pm
new file mode 100755
index 00000000..eb3a1208
--- /dev/null
+++ b/LedgerSMB/Session.pm
@@ -0,0 +1,143 @@
+#=====================================================================
+# LedgerSMB
+# Small Medium Business Accounting software
+#
+# See COPYRIGHT file for copyright information
+#======================================================================
+#
+# This file has undergone whitespace cleanup.
+#
+#======================================================================
+# This package contains session related functions:
+#
+# check - checks validity of session based on the user's cookie and login
+#
+# create - creates a new session, writes cookie upon success
+#
+# destroy - destroys session
+#====================================================================
+package Session;
+
+sub session_check {
+
+ my ($cookie, $form, %myconfig) = @_;
+ my ($sessionid, $token) = split /:/, $cookie;
+
+ # connect to database
+ my $dbh = DBI->connect($myconfig{dbconnect}, $myconfig{dbuser}, $myconfig{dbpasswd});
+
+ my $checkQuery = $dbh->prepare("SELECT sl_login FROM session WHERE session_id = ? AND token = ? AND last_used > now() - ?::interval");
+
+ my $updateAge = $dbh->prepare("UPDATE session SET last_used = now() WHERE session_id = ?;");
+
+ #must be an integer
+ $sessionid =~ s/[^0-9]//g;
+ $sessionid = int $sessionid;
+
+ #must be 32 chars long and contain hex chars
+ $token =~ s/[^0-9a-f]//g;
+ $token = substr($token, 0, 32);
+
+ if (!$myconfig{timeout}){
+ $timeout = "1 day";
+ } else {
+ $timeout = "$myconfig{timeout} seconds";
+ }
+
+ $checkQuery->execute($sessionid, $token, $timeout) || $form->dberror('Looking for session: ');
+ my $sessionValid = $checkQuery->rows;
+
+ if($sessionValid){
+
+ #user has a valid session cookie, now check the user
+ my ($sessionLogin) = $checkQuery->fetchrow_array;
+
+ my $login = $form->{login};
+ $login =~ s/[^a-zA-Z0-9@.-]//g;
+
+ if($sessionLogin eq $login){
+ $updateAge->execute($sessionid) || $form->dberror('Updating session age: ');
+ return 1;
+
+ } else {
+ #something's wrong, they have the cookie, but wrong user. Hijack attempt?
+ #delete the cookie in the browser
+ print qq|Set-Cookie: LedgerSMB=; path=/;\n|;
+ return 0;
+ }
+
+ } else {
+ #cookie is not valid
+ #delete the cookie in the browser
+ print qq|Set-Cookie: LedgerSMB=; path=/;\n|;
+ print qq|Set-Cookie: DiedHere=true; path=/;\n|;
+ return 0;
+ }
+}
+
+sub session_create {
+ my ($form, %myconfig) = @_;
+
+ # connect to database
+ my $dbh = DBI->connect($myconfig{dbconnect}, $myconfig{dbuser}, $myconfig{dbpasswd});
+
+ # TODO Change this to use %myconfig
+ my $deleteExisting = $dbh->prepare("DELETE FROM session WHERE sl_login = ? AND age(last_used) > ?::interval");
+
+ my $seedRandom = $dbh->prepare("SELECT setseed(?);");
+
+ my $fetchSequence = $dbh->prepare("SELECT nextval('session_session_id_seq'), md5(random());");
+
+ my $createNew = $dbh->prepare("INSERT INTO session (session_id, sl_login, token) VALUES(?, ?, ?);");
+
+
+ # this is assuming that $form->{login} is safe, which might be a bad assumption
+ # so, I'm going to remove some chars, which might make previously valid logins invalid
+ my $login = $form->{login};
+ $login =~ s/[^a-zA-Z0-9@.-]//g;
+
+ #delete any existing stale sessions with this login if they exist
+ if (!$myconfig{timeout}){
+ $myconfig{timeout} = 86400;
+ }
+
+ $deleteExisting->execute($login, "$myconfig{timeout} seconds") || $form->dberror('Delete from session: ');
+
+ #doing the md5 and random stuff in the db so that LedgerSMB won't
+ #require new perl modules (Digest::MD5 and a good random generator)
+ $fetchSequence->execute() || $form->dberror('Fetch sequence id: ');
+ my ($newSessionID, $newToken) = $fetchSequence->fetchrow_array;
+
+ #create a new session
+ $createNew->execute($newSessionID, $login, $newToken) || $form->dberror('Create new session: ');
+
+ #reseed the random number generator
+ my $randomSeed = 1.0 * ('0.'. (time() ^ ($$ + ($$ <<15))));
+ $seedRandom->execute($randomSeed)|| $form->dberror('Reseed random generator: ');;
+
+ $newCookieValue = $newSessionID . ':' . $newToken;
+
+ #now set the cookie in the browser
+ #TODO set domain from ENV, also set path to install path
+ print qq|Set-Cookie: LedgerSMB=$newCookieValue; path=/;\n|;
+ $form->{LedgerSMB} = $newCookieValue;
+}
+
+sub session_destroy {
+ my ($form) = @_;
+
+ my $login = $form->{login};
+ $login =~ s/[^a-zA-Z0-9@.-]//g;
+
+ # connect to database
+ my $dbh = DBI->connect($myconfig{dbconnect}, $myconfig{dbuser}, $myconfig{dbpasswd});
+
+ my $deleteExisting = $dbh->prepare("DELETE FROM session WHERE sl_login = ?;");
+ $deleteExisting->execute($login) || $form->dberror('Delete from session: ');
+
+ #delete the cookie in the browser
+ print qq|Set-Cookie: LedgerSMB=; path=/;\n|;
+
+}
+
+1;
diff --git a/LedgerSMB/User.pm b/LedgerSMB/User.pm
new file mode 100755
index 00000000..2398b06b
--- /dev/null
+++ b/LedgerSMB/User.pm
@@ -0,0 +1,927 @@
+#=====================================================================
+# LedgerSMB
+# Small Medium Business Accounting software
+#
+# See COPYRIGHT file for copyright information
+#======================================================================
+#
+# This file has NOT undergone whitespace cleanup.
+#
+#======================================================================
+#
+# user related functions
+#
+#=====================================================================
+
+package User;
+
+
+sub new {
+ my ($type, $memfile, $login) = @_;
+ my $self = {};
+
+ if ($login ne "") {
+ &error("", "$memfile locked!") if (-f "${memfile}.LCK");
+
+ open(MEMBER, "$memfile") or &error("", "$memfile : $!");
+
+ while (<MEMBER>) {
+ if (/^\[$login\]/) {
+ while (<MEMBER>) {
+ last if /^\[/;
+ next if /^(#|\s)/;
+
+ # remove comments
+ s/^\s*#.*//g;
+
+ # remove any trailing whitespace
+ s/^\s*(.*?)\s*$/$1/;
+
+ ($key, $value) = split /=/, $_, 2;
+
+ $self->{$key} = $value;
+ }
+
+ $self->{login} = $login;
+
+ last;
+ }
+ }
+ close MEMBER;
+ }
+
+ bless $self, $type;
+}
+
+
+sub country_codes {
+
+ my %cc = ();
+ my @language = ();
+
+ # scan the locale directory and read in the LANGUAGE files
+ opendir DIR, "locale";
+
+ my @dir = grep !/(^\.\.?$|\..*)/, readdir DIR;
+
+ foreach my $dir (@dir) {
+ next unless open(FH, "locale/$dir/LANGUAGE");
+ @language = <FH>;
+ close FH;
+
+ $cc{$dir} = "@language";
+ }
+
+ closedir(DIR);
+
+ %cc;
+
+}
+
+
+sub login {
+ my ($self, $form, $userspath) = @_;
+
+ my $rc = -1;
+
+ if ($self->{login} ne "") {
+
+ if ($self->{password} ne "") {
+ my $password = crypt $form->{password}, substr($self->{login}, 0, 2);
+ if ($self->{password} ne $password) {
+ return -1;
+ }
+ }
+
+ unless (-f "$userspath/$self->{login}.conf") {
+ $self->create_config("$userspath/$self->{login}.conf");
+ }
+
+ do "$userspath/$self->{login}.conf";
+ $myconfig{dbpasswd} = unpack 'u', $myconfig{dbpasswd};
+
+ # check if database is down
+ my $dbh = DBI->connect($myconfig{dbconnect}, $myconfig{dbuser}, $myconfig{dbpasswd}) or $self->error($DBI::errstr);
+
+ # we got a connection, check the version
+ my $query = qq|SELECT version FROM defaults|;
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ my ($dbversion) = $sth->fetchrow_array;
+ $sth->finish;
+
+ # add login to employee table if it does not exist
+ # no error check for employee table, ignore if it does not exist
+ my $login = $self->{login};
+ $login =~ s/@.*//;
+ $query = qq|SELECT id FROM employee WHERE login = '$login'|;
+ $sth = $dbh->prepare($query);
+ $sth->execute;
+
+ my ($id) = $sth->fetchrow_array;
+ $sth->finish;
+
+ if (! $id) {
+ my ($employeenumber) = $form->update_defaults(\%myconfig, "employeenumber", $dbh);
+
+ $query = qq|INSERT INTO employee (login, employeenumber, name, workphone,
+ role)
+ VALUES ('$login', '$employeenumber', '$myconfig{name}',
+ '$myconfig{tel}', '$myconfig{role}')|;
+ $dbh->do($query);
+ }
+ $dbh->disconnect;
+
+ $rc = 0;
+
+
+ if ($form->{dbversion} ne $dbversion) {
+ $rc = -3;
+ $dbupdate = (calc_version($dbversion) < calc_version($form->{dbversion}));
+ }
+
+ if ($dbupdate) {
+ $rc = -4;
+
+ # if DB2 bale out
+ if ($myconfig{dbdriver} eq 'DB2') {
+ $rc = -2;
+ }
+ }
+ }
+
+ $rc;
+
+}
+
+
+sub check_recurring {
+ my ($self, $form) = @_;
+
+ $self->{dbpasswd} = unpack 'u', $self->{dbpasswd};
+
+ my $dbh = DBI->connect($self->{dbconnect}, $self->{dbuser}, $self->{dbpasswd}) or $form->dberror;
+
+ my $query = qq|SELECT count(*) FROM recurring
+ WHERE enddate >= current_date AND nextdate <= current_date|;
+ ($_) = $dbh->selectrow_array($query);
+
+ $dbh->disconnect;
+
+ $_;
+
+}
+
+
+sub dbconnect_vars {
+ my ($form, $db) = @_;
+
+ my %dboptions = (
+ 'Pg' => {
+ 'yy-mm-dd' => 'set DateStyle to \'ISO\'',
+ 'mm/dd/yy' => 'set DateStyle to \'SQL, US\'',
+ 'mm-dd-yy' => 'set DateStyle to \'POSTGRES, US\'',
+ 'dd/mm/yy' => 'set DateStyle to \'SQL, EUROPEAN\'',
+ 'dd-mm-yy' => 'set DateStyle to \'POSTGRES, EUROPEAN\'',
+ 'dd.mm.yy' => 'set DateStyle to \'GERMAN\''
+ },
+ 'Oracle' => {
+ 'yy-mm-dd' => 'ALTER SESSION SET NLS_DATE_FORMAT = \'YY-MM-DD\'',
+ 'mm/dd/yy' => 'ALTER SESSION SET NLS_DATE_FORMAT = \'MM/DD/YY\'',
+ 'mm-dd-yy' => 'ALTER SESSION SET NLS_DATE_FORMAT = \'MM-DD-YY\'',
+ 'dd/mm/yy' => 'ALTER SESSION SET NLS_DATE_FORMAT = \'DD/MM/YY\'',
+ 'dd-mm-yy' => 'ALTER SESSION SET NLS_DATE_FORMAT = \'DD-MM-YY\'',
+ 'dd.mm.yy' => 'ALTER SESSION SET NLS_DATE_FORMAT = \'DD.MM.YY\'',
+ }
+ );
+
+
+ $form->{dboptions} = $dboptions{$form->{dbdriver}}{$form->{dateformat}};
+
+ if ($form->{dbdriver} =~ /Pg/) {
+ $form->{dbconnect} = "dbi:$form->{dbdriver}:dbname=$db";
+ }
+
+ if ($form->{dbdriver} eq 'Oracle') {
+ $form->{dbconnect} = "dbi:Oracle:sid=$form->{sid}";
+ }
+
+ if ($form->{dbhost}) {
+ $form->{dbconnect} .= ";host=$form->{dbhost}";
+ }
+ if ($form->{dbport}) {
+ $form->{dbconnect} .= ";port=$form->{dbport}";
+ }
+
+}
+
+
+sub dbdrivers {
+
+ my @drivers = DBI->available_drivers();
+
+# return (grep { /(Pg|Oracle|DB2)/ } @drivers);
+ return (grep { /Pg$/ } @drivers);
+
+}
+
+
+sub dbsources {
+ my ($self, $form) = @_;
+
+ my @dbsources = ();
+ my ($sth, $query);
+
+ $form->{dbdefault} = $form->{dbuser} unless $form->{dbdefault};
+ $form->{sid} = $form->{dbdefault};
+ &dbconnect_vars($form, $form->{dbdefault});
+
+ my $dbh = DBI->connect($form->{dbconnect}, $form->{dbuser}, $form->{dbpasswd}) or $form->dberror;
+
+
+ if ($form->{dbdriver} eq 'Pg') {
+
+ $query = qq|SELECT datname FROM pg_database|;
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while (my ($db) = $sth->fetchrow_array) {
+
+ if ($form->{only_acc_db}) {
+
+ next if ($db =~ /^template/);
+
+ &dbconnect_vars($form, $db);
+ my $dbh = DBI->connect($form->{dbconnect}, $form->{dbuser}, $form->{dbpasswd}) or $form->dberror;
+
+ $query = qq|SELECT tablename FROM pg_tables
+ WHERE tablename = 'defaults'
+ AND tableowner = '$form->{dbuser}'|;
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ if ($sth->fetchrow_array) {
+ push @dbsources, $db;
+ }
+ $sth->finish;
+ $dbh->disconnect;
+ next;
+ }
+ push @dbsources, $db;
+ }
+ }
+
+ if ($form->{dbdriver} eq 'Oracle') {
+ if ($form->{only_acc_db}) {
+ $query = qq|SELECT owner FROM dba_objects
+ WHERE object_name = 'DEFAULTS'
+ AND object_type = 'TABLE'|;
+ } else {
+ $query = qq|SELECT username FROM dba_users|;
+ }
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while (my ($db) = $sth->fetchrow_array) {
+ push @dbsources, $db;
+ }
+ }
+
+
+# JJR
+ if ($form->{dbdriver} eq 'DB2') {
+ if ($form->{only_acc_db}) {
+ $query = qq|SELECT tabschema FROM syscat.tables WHERE tabname = 'DEFAULTS'|;
+ } else {
+ $query = qq|SELECT DISTINCT schemaname FROM syscat.schemata WHERE definer != 'SYSIBM' AND schemaname != 'NULLID'|;
+ }
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while (my ($db) = $sth->fetchrow_array) {
+ push @dbsources, $db;
+ }
+ }
+# End JJR
+
+# the above is not used but leave it in for future reference
+# DS, Oct. 28, 2003
+
+
+ $sth->finish;
+ $dbh->disconnect;
+
+ return @dbsources;
+
+}
+
+
+sub dbcreate {
+ my ($self, $form) = @_;
+
+ my %dbcreate = ( 'Pg' => qq|CREATE DATABASE "$form->{db}"|,
+ 'Oracle' => qq|CREATE USER "$form->{db}" DEFAULT TABLESPACE USERS TEMPORARY TABLESPACE TEMP IDENTIFIED BY "$form->{db}"|);
+
+ $dbcreate{Pg} .= " WITH ENCODING = '$form->{encoding}'" if $form->{encoding};
+
+ $form->{sid} = $form->{dbdefault};
+ &dbconnect_vars($form, $form->{dbdefault});
+ my $dbh = DBI->connect($form->{dbconnect}, $form->{dbuser}, $form->{dbpasswd}) or $form->dberror;
+ my $query = qq|$dbcreate{$form->{dbdriver}}|;
+ $dbh->do($query);
+
+ if ($form->{dbdriver} eq 'Oracle') {
+ $query = qq|GRANT CONNECT,RESOURCE TO "$form->{db}"|;
+ $dbh->do($query) || $form->dberror($query);
+ }
+ $dbh->disconnect;
+
+
+ # setup variables for the new database
+ if ($form->{dbdriver} eq 'Oracle') {
+ $form->{dbuser} = $form->{db};
+ $form->{dbpasswd} = $form->{db};
+ }
+
+
+ &dbconnect_vars($form, $form->{db});
+
+ $dbh = DBI->connect($form->{dbconnect}, $form->{dbuser}, $form->{dbpasswd}) or $form->dberror;
+
+ # create the tables
+ my $dbdriver = ($form->{dbdriver} =~ /Pg/) ? 'Pg' : $form->{dbdriver};
+
+ my $filename = qq|sql/${dbdriver}-tables.sql|;
+ $self->process_query($form, $dbh, $filename);
+
+ # create functions
+ $filename = qq|sql/${dbdriver}-functions.sql|;
+ $self->process_query($form, $dbh, $filename);
+
+ # load gifi
+ ($filename) = split /_/, $form->{chart};
+ $filename =~ s/_//;
+ $self->process_query($form, $dbh, "sql/${filename}-gifi.sql");
+
+ # load chart of accounts
+ $filename = qq|sql/$form->{chart}-chart.sql|;
+ $self->process_query($form, $dbh, $filename);
+
+ # create indices
+ $filename = qq|sql/${dbdriver}-indices.sql|;
+ $self->process_query($form, $dbh, $filename);
+
+ # create custom tables and functions
+ my $item;
+ foreach $item (qw(tables functions)) {
+ $filename = "sql/${dbdriver}-custom_${item}.sql";
+ if (-f "$filename") {
+ $self->process_query($form, $dbh, $filename);
+ }
+ }
+
+ $dbh->disconnect;
+
+}
+
+
+
+sub process_query {
+ my ($self, $form, $dbh, $filename) = @_;
+
+ return unless (-f $filename);
+
+ open(FH, "$filename") or $form->error("$filename : $!\n");
+ my $query = "";
+ my $loop = 0;
+ my $sth;
+
+
+ while (<FH>) {
+
+ if ($loop && /^--\s*end\s*(procedure|function|trigger)/i) {
+ $loop = 0;
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+ $sth->finish;
+
+ $query = "";
+ next;
+ }
+
+ if ($loop || /^create *(or replace)? *(procedure|function|trigger)/i) {
+ $loop = 1;
+ next if /^(--.*|\s+)$/;
+
+ $query .= $_;
+ next;
+ }
+
+ # don't add comments or empty lines
+ next if /^(--.*|\s+)$/;
+
+ # anything else, add to query
+ $query .= $_;
+
+ if (/;\s*$/) {
+ # strip ;... Oracle doesn't like it
+ $query =~ s/;\s*$//;
+ $query =~ s/\\'/''/g;
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+ $sth->finish;
+
+ $query = "";
+ }
+
+ }
+ close FH;
+
+}
+
+
+
+sub dbdelete {
+ my ($self, $form) = @_;
+
+ my %dbdelete = ( 'Pg' => qq|DROP DATABASE "$form->{db}"|,
+ 'Oracle' => qq|DROP USER $form->{db} CASCADE|
+ );
+
+ $form->{sid} = $form->{dbdefault};
+ &dbconnect_vars($form, $form->{dbdefault});
+ my $dbh = DBI->connect($form->{dbconnect}, $form->{dbuser}, $form->{dbpasswd}) or $form->dberror;
+ my $query = qq|$dbdelete{$form->{dbdriver}}|;
+ $dbh->do($query) || $form->dberror($query);
+
+ $dbh->disconnect;
+
+}
+
+
+
+sub dbsources_unused {
+ my ($self, $form, $memfile) = @_;
+
+ my @dbexcl = ();
+ my @dbsources = ();
+
+ $form->error("$memfile locked!") if (-f "${memfile}.LCK");
+
+ # open members file
+ open(FH, "$memfile") or $form->error("$memfile : $!");
+
+ while (<FH>) {
+ if (/^dbname=/) {
+ my ($null,$item) = split /=/;
+ push @dbexcl, $item;
+ }
+ }
+
+ close FH;
+
+ $form->{only_acc_db} = 1;
+ my @db = &dbsources("", $form);
+
+ push @dbexcl, $form->{dbdefault};
+
+ foreach $item (@db) {
+ unless (grep /$item$/, @dbexcl) {
+ push @dbsources, $item;
+ }
+ }
+
+ return @dbsources;
+
+}
+
+
+sub dbneedsupdate {
+ my ($self, $form) = @_;
+
+ my %dbsources = ();
+ my $query;
+
+ $form->{sid} = $form->{dbdefault};
+ &dbconnect_vars($form, $form->{dbdefault});
+
+ my $dbh = DBI->connect($form->{dbconnect}, $form->{dbuser}, $form->{dbpasswd}) or $form->dberror;
+
+ if ($form->{dbdriver} =~ /Pg/) {
+
+ $query = qq|SELECT d.datname FROM pg_database d, pg_user u
+ WHERE d.datdba = u.usesysid
+ AND u.usename = '$form->{dbuser}'|;
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while (my ($db) = $sth->fetchrow_array) {
+
+ next if ($db =~ /^template/);
+
+ &dbconnect_vars($form, $db);
+
+ my $dbh = DBI->connect($form->{dbconnect}, $form->{dbuser}, $form->{dbpasswd}) or $form->dberror;
+
+ $query = qq|SELECT tablename FROM pg_tables
+ WHERE tablename = 'defaults'|;
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ if ($sth->fetchrow_array) {
+ $query = qq|SELECT version FROM defaults|;
+ my $sth = $dbh->prepare($query);
+ $sth->execute;
+
+ if (my ($version) = $sth->fetchrow_array) {
+ $dbsources{$db} = $version;
+ }
+ $sth->finish;
+ }
+ $sth->finish;
+ $dbh->disconnect;
+ }
+ $sth->finish;
+ }
+
+
+ if ($form->{dbdriver} eq 'Oracle') {
+ $query = qq|SELECT owner FROM dba_objects
+ WHERE object_name = 'DEFAULTS'
+ AND object_type = 'TABLE'|;
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while (my ($db) = $sth->fetchrow_array) {
+
+ $form->{dbuser} = $db;
+ &dbconnect_vars($form, $db);
+
+ my $dbh = DBI->connect($form->{dbconnect}, $form->{dbuser}, $form->{dbpasswd}) or $form->dberror;
+
+ $query = qq|SELECT version FROM defaults|;
+ my $sth = $dbh->prepare($query);
+ $sth->execute;
+
+ if (my ($version) = $sth->fetchrow_array) {
+ $dbsources{$db} = $version;
+ }
+ $sth->finish;
+ $dbh->disconnect;
+ }
+ $sth->finish;
+ }
+
+
+# JJR
+ if ($form->{dbdriver} eq 'DB2') {
+ $query = qq|SELECT tabschema FROM syscat.tables WHERE tabname = 'DEFAULTS'|;
+
+ $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ while (my ($db) = $sth->fetchrow_array) {
+
+ &dbconnect_vars($form, $db);
+
+ my $dbh = DBI->connect($form->{dbconnect}, $form->{dbuser}, $form->{dbpasswd}) or $form->dberror;
+
+ $query = qq|SELECT version FROM defaults|;
+ my $sth = $dbh->prepare($query);
+ $sth->execute;
+
+ if (my ($version) = $sth->fetchrow_array) {
+ $dbsources{$db} = $version;
+ }
+ $sth->finish;
+ $dbh->disconnect;
+ }
+ $sth->finish;
+ }
+# End JJR
+
+# code for DB2 is not used, keep for future reference
+# DS, Oct. 28, 2003
+
+ $dbh->disconnect;
+
+ %dbsources;
+
+}
+
+
+sub dbupdate {
+ my ($self, $form) = @_;
+
+ $form->{sid} = $form->{dbdefault};
+
+ my @upgradescripts = ();
+ my $query;
+ my $rc = -2;
+
+ if ($form->{dbupdate}) {
+ # read update scripts into memory
+ opendir SQLDIR, "sql/." or $form->error($!);
+ @upgradescripts = sort script_version grep /$form->{dbdriver}-upgrade-.*?\.sql$/, readdir SQLDIR;
+ closedir SQLDIR;
+ }
+
+
+ foreach my $db (split / /, $form->{dbupdate}) {
+
+ next unless $form->{$db};
+
+ # strip db from dataset
+ $db =~ s/^db//;
+ &dbconnect_vars($form, $db);
+
+ my $dbh = DBI->connect($form->{dbconnect}, $form->{dbuser}, $form->{dbpasswd}) or $form->dberror;
+
+ # check version
+ $query = qq|SELECT version FROM defaults|;
+ my $sth = $dbh->prepare($query);
+ # no error check, let it fall through
+ $sth->execute;
+
+ my $version = $sth->fetchrow_array;
+ $sth->finish;
+
+ next unless $version;
+
+ $version = calc_version($version);
+ my $dbversion = calc_version($form->{dbversion});
+
+ foreach my $upgradescript (@upgradescripts) {
+ my $a = $upgradescript;
+ $a =~ s/(^$form->{dbdriver}-upgrade-|\.sql$)//g;
+
+ my ($mindb, $maxdb) = split /-/, $a;
+ $mindb = calc_version($mindb);
+ $maxdb = calc_version($maxdb);
+
+ next if ($version >= $maxdb);
+
+ # exit if there is no upgrade script or version == mindb
+ last if ($version < $mindb || $version >= $dbversion);
+
+ # apply upgrade
+ $self->process_query($form, $dbh, "sql/$upgradescript");
+
+ $version = $maxdb;
+
+ }
+
+ $rc = 0;
+ $dbh->disconnect;
+
+ }
+
+ $rc;
+
+}
+
+
+sub calc_version {
+
+ my @v = split /\./, $_[0];
+ my $version = 0;
+ my $i;
+
+ for ($i = 0; $i <= $#v; $i++) {
+ $version *= 1000;
+ $version += $v[$i];
+ }
+
+ return $version;
+
+}
+
+
+sub script_version {
+ my ($my_a, $my_b) = ($a, $b);
+
+ my ($a_from, $a_to, $b_from, $b_to);
+ my ($res_a, $res_b, $i);
+
+ $my_a =~ s/.*-upgrade-//;
+ $my_a =~ s/.sql$//;
+ $my_b =~ s/.*-upgrade-//;
+ $my_b =~ s/.sql$//;
+ ($a_from, $a_to) = split(/-/, $my_a);
+ ($b_from, $b_to) = split(/-/, $my_b);
+
+ $res_a = calc_version($a_from);
+ $res_b = calc_version($b_from);
+
+ if ($res_a == $res_b) {
+ $res_a = calc_version($a_to);
+ $res_b = calc_version($b_to);
+ }
+
+ return $res_a <=> $res_b;
+
+}
+
+
+sub create_config {
+ my ($self, $filename) = @_;
+
+
+ @config = &config_vars;
+
+ open(CONF, ">$filename") or $self->error("$filename : $!");
+
+ # create the config file
+ print CONF qq|# configuration file for $self->{login}
+
+\%myconfig = (
+|;
+
+ foreach $key (sort @config) {
+ $self->{$key} =~ s/\\/\\\\/g;
+ $self->{$key} =~ s/'/\\'/g;
+ print CONF qq| $key => '$self->{$key}',\n|;
+ }
+
+
+ print CONF qq|);\n\n|;
+
+ close CONF;
+
+}
+
+
+sub save_member {
+ my ($self, $memberfile, $userspath) = @_;
+
+ # format dbconnect and dboptions string
+ &dbconnect_vars($self, $self->{dbname});
+
+ $self->error("$memberfile locked!") if (-f "${memberfile}.LCK");
+ open(FH, ">${memberfile}.LCK") or $self->error("${memberfile}.LCK : $!");
+ close(FH);
+
+ if (! open(CONF, "+<$memberfile")) {
+ unlink "${memberfile}.LCK";
+ $self->error("$memberfile : $!");
+ }
+
+ @config = <CONF>;
+
+ seek(CONF, 0, 0);
+ truncate(CONF, 0);
+
+ while ($line = shift @config) {
+ last if ($line =~ /^\[$self->{login}\]/);
+ print CONF $line;
+ }
+
+ # remove everything up to next login or EOF
+ while ($line = shift @config) {
+ last if ($line =~ /^\[/);
+ }
+
+ # this one is either the next login or EOF
+ print CONF $line;
+
+ while ($line = shift @config) {
+ print CONF $line;
+ }
+
+ print CONF qq|[$self->{login}]\n|;
+
+ if ($self->{packpw}) {
+ $self->{dbpasswd} = pack 'u', $self->{dbpasswd};
+ chop $self->{dbpasswd};
+ }
+
+ if ($self->{password} ne $self->{old_password}) {
+ $self->{password} = crypt $self->{password}, substr($self->{login}, 0, 2) if $self->{password};
+ }
+
+ if ($self->{'root login'}) {
+ @config = qw(password);
+ } else {
+ @config = &config_vars;
+ }
+
+ # replace \r\n with \n
+ for (qw(address signature)) { $self->{$_} =~ s/\r?\n/\\n/g }
+
+ for (sort @config) { print CONF qq|$_=$self->{$_}\n| }
+
+ print CONF "\n";
+ close CONF;
+ unlink "${memberfile}.LCK";
+
+ # create conf file
+ if (! $self->{'root login'}) {
+ $self->create_config("$userspath/$self->{login}.conf");
+
+ $self->{dbpasswd} =~ s/\\'/'/g;
+ $self->{dbpasswd} =~ s/\\\\/\\/g;
+ $self->{dbpasswd} = unpack 'u', $self->{dbpasswd};
+
+ # check if login is in database
+ my $dbh = DBI->connect($self->{dbconnect}, $self->{dbuser}, $self->{dbpasswd}, {AutoCommit => 0}) or $self->error($DBI::errstr);
+
+ # add login to employee table if it does not exist
+ my $login = $self->{login};
+ $login =~ s/@.*//;
+ my $query = qq|SELECT id FROM employee WHERE login = '$login'|;
+ my $sth = $dbh->prepare($query);
+ $sth->execute;
+
+ my ($id) = $sth->fetchrow_array;
+ $sth->finish;
+
+ if ($id) {
+ $query = qq|UPDATE employee SET
+ role = '$self->{role}',
+ email = '$self->{email}',
+ name = '$self->{name}'
+ WHERE login = '$login'|;
+
+ } else {
+ my ($employeenumber) = Form::update_defaults("", \%$self, "employeenumber", $dbh);
+ $query = qq|INSERT INTO employee (login, employeenumber, name, workphone,
+ role, email, sales)
+ VALUES ('$login', '$employeenumber', '$self->{name}',
+ '$self->{tel}', '$self->{role}', '$self->{email}', '1')|;
+ }
+
+ $dbh->do($query);
+ $dbh->commit;
+ $dbh->disconnect;
+
+ }
+
+}
+
+
+sub delete_login {
+ my ($self, $form) = @_;
+
+ my $dbh = DBI->connect($form->{dbconnect}, $form->{dbuser}, $form->{dbpasswd}, {AutoCommit} => 0) or $form->dberror;
+
+ my $login = $form->{login};
+ $login =~ s/@.*//;
+ my $query = qq|SELECT id FROM employee
+ WHERE login = '$login'|;
+ my $sth = $dbh->prepare($query);
+ $sth->execute || $form->dberror($query);
+
+ my ($id) = $sth->fetchrow_array;
+ $sth->finish;
+
+ my $query = qq|UPDATE employee SET
+ login = NULL,
+ enddate = current_date
+ WHERE login = '$login'|;
+ $dbh->do($query);
+
+ $dbh->commit;
+ $dbh->disconnect;
+
+}
+
+
+sub config_vars {
+
+ my @conf = qw(acs address businessnumber company countrycode
+ currency dateformat dbconnect dbdriver dbhost dbname dboptions
+ dbpasswd dbport dbuser email fax menuwidth name numberformat
+ password printer role sid signature stylesheet tel
+ templates timeout vclimit);
+
+ @conf;
+
+}
+
+
+sub error {
+ my ($self, $msg) = @_;
+
+ if ($ENV{HTTP_USER_AGENT}) {
+ print qq|Content-Type: text/html
+
+<body bgcolor=ffffff>
+
+<h2><font color=red>Error!</font></h2>
+<p><b>$msg</b>|;
+
+ }
+
+ die "Error: $msg\n";
+
+}
+
+
+1;
+