This commit was generated by cvs2svn to compensate for changes in r2523,
authorivan <ivan>
Tue, 15 Jul 2003 11:45:14 +0000 (11:45 +0000)
committerivan <ivan>
Tue, 15 Jul 2003 11:45:14 +0000 (11:45 +0000)
which included commits to RCS files with non-trunk default branches.

843 files changed:
sql-ledger/SL/AM.pm [new file with mode: 0644]
sql-ledger/SL/AP.pm [new file with mode: 0644]
sql-ledger/SL/AR.pm [new file with mode: 0644]
sql-ledger/SL/CA.pm [new file with mode: 0644]
sql-ledger/SL/CP.pm [new file with mode: 0644]
sql-ledger/SL/CT.pm [new file with mode: 0644]
sql-ledger/SL/Form.pm [new file with mode: 0644]
sql-ledger/SL/GL.pm [new file with mode: 0644]
sql-ledger/SL/IC.pm [new file with mode: 0644]
sql-ledger/SL/IR.pm [new file with mode: 0644]
sql-ledger/SL/IS.pm [new file with mode: 0644]
sql-ledger/SL/Inifile.pm [new file with mode: 0644]
sql-ledger/SL/Mailer.pm [new file with mode: 0644]
sql-ledger/SL/Menu.pm [new file with mode: 0644]
sql-ledger/SL/Num2text.pm [new file with mode: 0644]
sql-ledger/SL/OE.pm [new file with mode: 0644]
sql-ledger/SL/PE.pm [new file with mode: 0644]
sql-ledger/SL/RC.pm [new file with mode: 0644]
sql-ledger/SL/RP.pm [new file with mode: 0644]
sql-ledger/SL/User.pm [new file with mode: 0644]
sql-ledger/VERSION [new file with mode: 0644]
sql-ledger/am.pl [new file with mode: 0755]
sql-ledger/bin/lynx/menu.pl [new file with mode: 0644]
sql-ledger/bin/mozilla/admin.pl [new file with mode: 0644]
sql-ledger/bin/mozilla/am.pl [new file with mode: 0644]
sql-ledger/bin/mozilla/ap.pl [new file with mode: 0644]
sql-ledger/bin/mozilla/ar.pl [new file with mode: 0644]
sql-ledger/bin/mozilla/arap.pl [new file with mode: 0644]
sql-ledger/bin/mozilla/ca.pl [new file with mode: 0644]
sql-ledger/bin/mozilla/cp.pl [new file with mode: 0644]
sql-ledger/bin/mozilla/ct.pl [new file with mode: 0644]
sql-ledger/bin/mozilla/gl.pl [new file with mode: 0644]
sql-ledger/bin/mozilla/ic.pl [new file with mode: 0644]
sql-ledger/bin/mozilla/io.pl [new file with mode: 0644]
sql-ledger/bin/mozilla/ir.pl [new file with mode: 0644]
sql-ledger/bin/mozilla/is.pl [new file with mode: 0644]
sql-ledger/bin/mozilla/login.pl [new file with mode: 0644]
sql-ledger/bin/mozilla/menu.pl [new file with mode: 0644]
sql-ledger/bin/mozilla/oe.pl [new file with mode: 0644]
sql-ledger/bin/mozilla/pe.pl [new file with mode: 0644]
sql-ledger/bin/mozilla/rc.pl [new file with mode: 0644]
sql-ledger/bin/mozilla/rp.pl [new file with mode: 0644]
sql-ledger/css/sql-ledger.css [new file with mode: 0644]
sql-ledger/doc/COPYING [new file with mode: 0644]
sql-ledger/doc/README [new file with mode: 0644]
sql-ledger/doc/UPGRADE-1.6-1.8 [new file with mode: 0644]
sql-ledger/doc/UPGRADE-1.8-1.8.3 [new file with mode: 0644]
sql-ledger/doc/UPGRADE-1.8.3-1.8.4 [new file with mode: 0644]
sql-ledger/doc/UPGRADE-1.8.4-1.8.5 [new file with mode: 0644]
sql-ledger/doc/UPGRADE-1.8.5-1.8.7 [new file with mode: 0644]
sql-ledger/doc/UPGRADE-1.8.7-2.0.0 [new file with mode: 0644]
sql-ledger/doc/UPGRADE-2.0-2.0.8 [new file with mode: 0644]
sql-ledger/doc/copyright [new file with mode: 0644]
sql-ledger/doc/faq.html [new file with mode: 0644]
sql-ledger/favicon.ico [new file with mode: 0644]
sql-ledger/locale/br/COPYING [new file with mode: 0644]
sql-ledger/locale/br/LANGUAGE [new file with mode: 0644]
sql-ledger/locale/br/admin [new file with mode: 0644]
sql-ledger/locale/br/all [new file with mode: 0644]
sql-ledger/locale/br/am [new file with mode: 0644]
sql-ledger/locale/br/ap [new file with mode: 0644]
sql-ledger/locale/br/ar [new file with mode: 0644]
sql-ledger/locale/br/arap [new file with mode: 0644]
sql-ledger/locale/br/ca [new file with mode: 0644]
sql-ledger/locale/br/cp [new file with mode: 0644]
sql-ledger/locale/br/ct [new file with mode: 0644]
sql-ledger/locale/br/gl [new file with mode: 0644]
sql-ledger/locale/br/ic [new file with mode: 0644]
sql-ledger/locale/br/io [new file with mode: 0644]
sql-ledger/locale/br/ir [new file with mode: 0644]
sql-ledger/locale/br/is [new file with mode: 0644]
sql-ledger/locale/br/login [new file with mode: 0644]
sql-ledger/locale/br/menu [new file with mode: 0644]
sql-ledger/locale/br/oe [new file with mode: 0644]
sql-ledger/locale/br/pe [new file with mode: 0644]
sql-ledger/locale/br/rc [new file with mode: 0644]
sql-ledger/locale/br/rp [new file with mode: 0644]
sql-ledger/locale/cn/COPYING [new file with mode: 0644]
sql-ledger/locale/cn/LANGUAGE [new file with mode: 0644]
sql-ledger/locale/cn/admin [new file with mode: 0644]
sql-ledger/locale/cn/all [new file with mode: 0644]
sql-ledger/locale/cn/am [new file with mode: 0644]
sql-ledger/locale/cn/ap [new file with mode: 0644]
sql-ledger/locale/cn/ar [new file with mode: 0644]
sql-ledger/locale/cn/arap [new file with mode: 0644]
sql-ledger/locale/cn/ca [new file with mode: 0644]
sql-ledger/locale/cn/cp [new file with mode: 0644]
sql-ledger/locale/cn/ct [new file with mode: 0644]
sql-ledger/locale/cn/gl [new file with mode: 0644]
sql-ledger/locale/cn/ic [new file with mode: 0644]
sql-ledger/locale/cn/io [new file with mode: 0644]
sql-ledger/locale/cn/ir [new file with mode: 0644]
sql-ledger/locale/cn/is [new file with mode: 0644]
sql-ledger/locale/cn/login [new file with mode: 0644]
sql-ledger/locale/cn/menu [new file with mode: 0644]
sql-ledger/locale/cn/oe [new file with mode: 0644]
sql-ledger/locale/cn/pe [new file with mode: 0644]
sql-ledger/locale/cn/rc [new file with mode: 0644]
sql-ledger/locale/cn/rp [new file with mode: 0644]
sql-ledger/locale/ct/COPYING [new file with mode: 0644]
sql-ledger/locale/ct/LANGUAGE [new file with mode: 0644]
sql-ledger/locale/ct/admin [new file with mode: 0644]
sql-ledger/locale/ct/all [new file with mode: 0644]
sql-ledger/locale/ct/am [new file with mode: 0644]
sql-ledger/locale/ct/ap [new file with mode: 0644]
sql-ledger/locale/ct/ar [new file with mode: 0644]
sql-ledger/locale/ct/arap [new file with mode: 0644]
sql-ledger/locale/ct/ca [new file with mode: 0644]
sql-ledger/locale/ct/cp [new file with mode: 0644]
sql-ledger/locale/ct/ct [new file with mode: 0644]
sql-ledger/locale/ct/gl [new file with mode: 0644]
sql-ledger/locale/ct/ic [new file with mode: 0644]
sql-ledger/locale/ct/io [new file with mode: 0644]
sql-ledger/locale/ct/ir [new file with mode: 0644]
sql-ledger/locale/ct/is [new file with mode: 0644]
sql-ledger/locale/ct/login [new file with mode: 0644]
sql-ledger/locale/ct/menu [new file with mode: 0644]
sql-ledger/locale/ct/oe [new file with mode: 0644]
sql-ledger/locale/ct/pe [new file with mode: 0644]
sql-ledger/locale/ct/rc [new file with mode: 0644]
sql-ledger/locale/ct/rp [new file with mode: 0644]
sql-ledger/locale/cz/COPYING [new file with mode: 0644]
sql-ledger/locale/cz/LANGUAGE [new file with mode: 0644]
sql-ledger/locale/cz/admin [new file with mode: 0644]
sql-ledger/locale/cz/all [new file with mode: 0644]
sql-ledger/locale/cz/am [new file with mode: 0644]
sql-ledger/locale/cz/ap [new file with mode: 0644]
sql-ledger/locale/cz/ar [new file with mode: 0644]
sql-ledger/locale/cz/arap [new file with mode: 0644]
sql-ledger/locale/cz/ca [new file with mode: 0644]
sql-ledger/locale/cz/cp [new file with mode: 0644]
sql-ledger/locale/cz/ct [new file with mode: 0644]
sql-ledger/locale/cz/gl [new file with mode: 0644]
sql-ledger/locale/cz/ic [new file with mode: 0644]
sql-ledger/locale/cz/io [new file with mode: 0644]
sql-ledger/locale/cz/ir [new file with mode: 0644]
sql-ledger/locale/cz/is [new file with mode: 0644]
sql-ledger/locale/cz/login [new file with mode: 0644]
sql-ledger/locale/cz/menu [new file with mode: 0644]
sql-ledger/locale/cz/oe [new file with mode: 0644]
sql-ledger/locale/cz/pe [new file with mode: 0644]
sql-ledger/locale/cz/rc [new file with mode: 0644]
sql-ledger/locale/cz/rp [new file with mode: 0644]
sql-ledger/locale/de/COPYING [new file with mode: 0644]
sql-ledger/locale/de/LANGUAGE [new file with mode: 0644]
sql-ledger/locale/de/Num2text [new file with mode: 0644]
sql-ledger/locale/de/admin [new file with mode: 0644]
sql-ledger/locale/de/all [new file with mode: 0644]
sql-ledger/locale/de/am [new file with mode: 0644]
sql-ledger/locale/de/ap [new file with mode: 0644]
sql-ledger/locale/de/ar [new file with mode: 0644]
sql-ledger/locale/de/arap [new file with mode: 0644]
sql-ledger/locale/de/ca [new file with mode: 0644]
sql-ledger/locale/de/cp [new file with mode: 0644]
sql-ledger/locale/de/ct [new file with mode: 0644]
sql-ledger/locale/de/gl [new file with mode: 0644]
sql-ledger/locale/de/ic [new file with mode: 0644]
sql-ledger/locale/de/io [new file with mode: 0644]
sql-ledger/locale/de/ir [new file with mode: 0644]
sql-ledger/locale/de/is [new file with mode: 0644]
sql-ledger/locale/de/locales.pl [new file with mode: 0755]
sql-ledger/locale/de/login [new file with mode: 0644]
sql-ledger/locale/de/menu [new file with mode: 0644]
sql-ledger/locale/de/oe [new file with mode: 0644]
sql-ledger/locale/de/pe [new file with mode: 0644]
sql-ledger/locale/de/rc [new file with mode: 0644]
sql-ledger/locale/de/rp [new file with mode: 0644]
sql-ledger/locale/dk/COPYING [new file with mode: 0644]
sql-ledger/locale/dk/LANGUAGE [new file with mode: 0644]
sql-ledger/locale/dk/admin [new file with mode: 0644]
sql-ledger/locale/dk/all [new file with mode: 0644]
sql-ledger/locale/dk/am [new file with mode: 0644]
sql-ledger/locale/dk/ap [new file with mode: 0644]
sql-ledger/locale/dk/ar [new file with mode: 0644]
sql-ledger/locale/dk/arap [new file with mode: 0644]
sql-ledger/locale/dk/ca [new file with mode: 0644]
sql-ledger/locale/dk/cp [new file with mode: 0644]
sql-ledger/locale/dk/ct [new file with mode: 0644]
sql-ledger/locale/dk/gl [new file with mode: 0644]
sql-ledger/locale/dk/ic [new file with mode: 0644]
sql-ledger/locale/dk/io [new file with mode: 0644]
sql-ledger/locale/dk/ir [new file with mode: 0644]
sql-ledger/locale/dk/is [new file with mode: 0644]
sql-ledger/locale/dk/login [new file with mode: 0644]
sql-ledger/locale/dk/menu [new file with mode: 0644]
sql-ledger/locale/dk/oe [new file with mode: 0644]
sql-ledger/locale/dk/pe [new file with mode: 0644]
sql-ledger/locale/dk/rc [new file with mode: 0644]
sql-ledger/locale/dk/rp [new file with mode: 0644]
sql-ledger/locale/ee/COPYING [new file with mode: 0644]
sql-ledger/locale/ee/LANGUAGE [new file with mode: 0644]
sql-ledger/locale/ee/admin [new file with mode: 0644]
sql-ledger/locale/ee/all [new file with mode: 0644]
sql-ledger/locale/ee/am [new file with mode: 0644]
sql-ledger/locale/ee/ap [new file with mode: 0644]
sql-ledger/locale/ee/ar [new file with mode: 0644]
sql-ledger/locale/ee/arap [new file with mode: 0644]
sql-ledger/locale/ee/ca [new file with mode: 0644]
sql-ledger/locale/ee/cp [new file with mode: 0644]
sql-ledger/locale/ee/ct [new file with mode: 0644]
sql-ledger/locale/ee/gl [new file with mode: 0644]
sql-ledger/locale/ee/ic [new file with mode: 0644]
sql-ledger/locale/ee/io [new file with mode: 0644]
sql-ledger/locale/ee/ir [new file with mode: 0644]
sql-ledger/locale/ee/is [new file with mode: 0644]
sql-ledger/locale/ee/login [new file with mode: 0644]
sql-ledger/locale/ee/menu [new file with mode: 0644]
sql-ledger/locale/ee/oe [new file with mode: 0644]
sql-ledger/locale/ee/pe [new file with mode: 0644]
sql-ledger/locale/ee/rc [new file with mode: 0644]
sql-ledger/locale/ee/rp [new file with mode: 0644]
sql-ledger/locale/en_GB/COPYING [new file with mode: 0644]
sql-ledger/locale/en_GB/LANGUAGE [new file with mode: 0644]
sql-ledger/locale/en_GB/admin [new file with mode: 0644]
sql-ledger/locale/en_GB/all [new file with mode: 0644]
sql-ledger/locale/en_GB/am [new file with mode: 0644]
sql-ledger/locale/en_GB/ap [new file with mode: 0644]
sql-ledger/locale/en_GB/ar [new file with mode: 0644]
sql-ledger/locale/en_GB/arap [new file with mode: 0644]
sql-ledger/locale/en_GB/bp [new file with mode: 0644]
sql-ledger/locale/en_GB/ca [new file with mode: 0644]
sql-ledger/locale/en_GB/cp [new file with mode: 0644]
sql-ledger/locale/en_GB/ct [new file with mode: 0644]
sql-ledger/locale/en_GB/gl [new file with mode: 0644]
sql-ledger/locale/en_GB/ic [new file with mode: 0644]
sql-ledger/locale/en_GB/io [new file with mode: 0644]
sql-ledger/locale/en_GB/ir [new file with mode: 0644]
sql-ledger/locale/en_GB/is [new file with mode: 0644]
sql-ledger/locale/en_GB/login [new file with mode: 0644]
sql-ledger/locale/en_GB/menu [new file with mode: 0644]
sql-ledger/locale/en_GB/oe [new file with mode: 0644]
sql-ledger/locale/en_GB/pe [new file with mode: 0644]
sql-ledger/locale/en_GB/rc [new file with mode: 0644]
sql-ledger/locale/en_GB/rp [new file with mode: 0644]
sql-ledger/locale/es/COPYING [new file with mode: 0644]
sql-ledger/locale/es/LANGUAGE [new file with mode: 0644]
sql-ledger/locale/es/Num2text [new file with mode: 0644]
sql-ledger/locale/es/admin [new file with mode: 0644]
sql-ledger/locale/es/all [new file with mode: 0644]
sql-ledger/locale/es/am [new file with mode: 0644]
sql-ledger/locale/es/ap [new file with mode: 0644]
sql-ledger/locale/es/ar [new file with mode: 0644]
sql-ledger/locale/es/arap [new file with mode: 0644]
sql-ledger/locale/es/ca [new file with mode: 0644]
sql-ledger/locale/es/cp [new file with mode: 0644]
sql-ledger/locale/es/ct [new file with mode: 0644]
sql-ledger/locale/es/gl [new file with mode: 0644]
sql-ledger/locale/es/ic [new file with mode: 0644]
sql-ledger/locale/es/io [new file with mode: 0644]
sql-ledger/locale/es/ir [new file with mode: 0644]
sql-ledger/locale/es/is [new file with mode: 0644]
sql-ledger/locale/es/login [new file with mode: 0644]
sql-ledger/locale/es/menu [new file with mode: 0644]
sql-ledger/locale/es/oe [new file with mode: 0644]
sql-ledger/locale/es/pe [new file with mode: 0644]
sql-ledger/locale/es/rc [new file with mode: 0644]
sql-ledger/locale/es/rp [new file with mode: 0644]
sql-ledger/locale/fi/COPYING [new file with mode: 0644]
sql-ledger/locale/fi/LANGUAGE [new file with mode: 0644]
sql-ledger/locale/fi/admin [new file with mode: 0644]
sql-ledger/locale/fi/all [new file with mode: 0644]
sql-ledger/locale/fi/am [new file with mode: 0644]
sql-ledger/locale/fi/ap [new file with mode: 0644]
sql-ledger/locale/fi/ar [new file with mode: 0644]
sql-ledger/locale/fi/arap [new file with mode: 0644]
sql-ledger/locale/fi/ca [new file with mode: 0644]
sql-ledger/locale/fi/cp [new file with mode: 0644]
sql-ledger/locale/fi/ct [new file with mode: 0644]
sql-ledger/locale/fi/gl [new file with mode: 0644]
sql-ledger/locale/fi/ic [new file with mode: 0644]
sql-ledger/locale/fi/io [new file with mode: 0644]
sql-ledger/locale/fi/ir [new file with mode: 0644]
sql-ledger/locale/fi/is [new file with mode: 0644]
sql-ledger/locale/fi/login [new file with mode: 0644]
sql-ledger/locale/fi/menu [new file with mode: 0644]
sql-ledger/locale/fi/oe [new file with mode: 0644]
sql-ledger/locale/fi/pe [new file with mode: 0644]
sql-ledger/locale/fi/rc [new file with mode: 0644]
sql-ledger/locale/fi/rp [new file with mode: 0644]
sql-ledger/locale/fr/COPYING [new file with mode: 0644]
sql-ledger/locale/fr/LANGUAGE [new file with mode: 0644]
sql-ledger/locale/fr/admin [new file with mode: 0644]
sql-ledger/locale/fr/all [new file with mode: 0644]
sql-ledger/locale/fr/am [new file with mode: 0644]
sql-ledger/locale/fr/ap [new file with mode: 0644]
sql-ledger/locale/fr/ar [new file with mode: 0644]
sql-ledger/locale/fr/arap [new file with mode: 0644]
sql-ledger/locale/fr/ca [new file with mode: 0644]
sql-ledger/locale/fr/cp [new file with mode: 0644]
sql-ledger/locale/fr/ct [new file with mode: 0644]
sql-ledger/locale/fr/gl [new file with mode: 0644]
sql-ledger/locale/fr/ic [new file with mode: 0644]
sql-ledger/locale/fr/io [new file with mode: 0644]
sql-ledger/locale/fr/ir [new file with mode: 0644]
sql-ledger/locale/fr/is [new file with mode: 0644]
sql-ledger/locale/fr/login [new file with mode: 0644]
sql-ledger/locale/fr/menu [new file with mode: 0644]
sql-ledger/locale/fr/oe [new file with mode: 0644]
sql-ledger/locale/fr/pe [new file with mode: 0644]
sql-ledger/locale/fr/rc [new file with mode: 0644]
sql-ledger/locale/fr/rp [new file with mode: 0644]
sql-ledger/locale/hu/COPYING [new file with mode: 0644]
sql-ledger/locale/hu/LANGUAGE [new file with mode: 0644]
sql-ledger/locale/hu/admin [new file with mode: 0644]
sql-ledger/locale/hu/all [new file with mode: 0644]
sql-ledger/locale/hu/am [new file with mode: 0644]
sql-ledger/locale/hu/ap [new file with mode: 0644]
sql-ledger/locale/hu/ar [new file with mode: 0644]
sql-ledger/locale/hu/arap [new file with mode: 0644]
sql-ledger/locale/hu/ca [new file with mode: 0644]
sql-ledger/locale/hu/cp [new file with mode: 0644]
sql-ledger/locale/hu/ct [new file with mode: 0644]
sql-ledger/locale/hu/gl [new file with mode: 0644]
sql-ledger/locale/hu/ic [new file with mode: 0644]
sql-ledger/locale/hu/io [new file with mode: 0644]
sql-ledger/locale/hu/ir [new file with mode: 0644]
sql-ledger/locale/hu/is [new file with mode: 0644]
sql-ledger/locale/hu/login [new file with mode: 0644]
sql-ledger/locale/hu/menu [new file with mode: 0644]
sql-ledger/locale/hu/oe [new file with mode: 0644]
sql-ledger/locale/hu/pe [new file with mode: 0644]
sql-ledger/locale/hu/rc [new file with mode: 0644]
sql-ledger/locale/hu/rp [new file with mode: 0644]
sql-ledger/locale/is/COPYING [new file with mode: 0644]
sql-ledger/locale/is/LANGUAGE [new file with mode: 0644]
sql-ledger/locale/is/admin [new file with mode: 0644]
sql-ledger/locale/is/all [new file with mode: 0644]
sql-ledger/locale/is/am [new file with mode: 0644]
sql-ledger/locale/is/ap [new file with mode: 0644]
sql-ledger/locale/is/ar [new file with mode: 0644]
sql-ledger/locale/is/arap [new file with mode: 0644]
sql-ledger/locale/is/ca [new file with mode: 0644]
sql-ledger/locale/is/cp [new file with mode: 0644]
sql-ledger/locale/is/ct [new file with mode: 0644]
sql-ledger/locale/is/gl [new file with mode: 0644]
sql-ledger/locale/is/ic [new file with mode: 0644]
sql-ledger/locale/is/io [new file with mode: 0644]
sql-ledger/locale/is/ir [new file with mode: 0644]
sql-ledger/locale/is/is [new file with mode: 0644]
sql-ledger/locale/is/login [new file with mode: 0644]
sql-ledger/locale/is/menu [new file with mode: 0644]
sql-ledger/locale/is/oe [new file with mode: 0644]
sql-ledger/locale/is/pe [new file with mode: 0644]
sql-ledger/locale/is/rc [new file with mode: 0644]
sql-ledger/locale/is/rp [new file with mode: 0644]
sql-ledger/locale/it/COPYING [new file with mode: 0644]
sql-ledger/locale/it/LANGUAGE [new file with mode: 0644]
sql-ledger/locale/it/Num2text [new file with mode: 0644]
sql-ledger/locale/it/admin [new file with mode: 0644]
sql-ledger/locale/it/all [new file with mode: 0644]
sql-ledger/locale/it/am [new file with mode: 0644]
sql-ledger/locale/it/ap [new file with mode: 0644]
sql-ledger/locale/it/ar [new file with mode: 0644]
sql-ledger/locale/it/arap [new file with mode: 0644]
sql-ledger/locale/it/ca [new file with mode: 0644]
sql-ledger/locale/it/cp [new file with mode: 0644]
sql-ledger/locale/it/ct [new file with mode: 0644]
sql-ledger/locale/it/gl [new file with mode: 0644]
sql-ledger/locale/it/ic [new file with mode: 0644]
sql-ledger/locale/it/io [new file with mode: 0644]
sql-ledger/locale/it/ir [new file with mode: 0644]
sql-ledger/locale/it/is [new file with mode: 0644]
sql-ledger/locale/it/login [new file with mode: 0644]
sql-ledger/locale/it/menu [new file with mode: 0644]
sql-ledger/locale/it/oe [new file with mode: 0644]
sql-ledger/locale/it/pe [new file with mode: 0644]
sql-ledger/locale/it/qe [new file with mode: 0644]
sql-ledger/locale/it/rc [new file with mode: 0644]
sql-ledger/locale/it/rp [new file with mode: 0644]
sql-ledger/locale/lt/COPYING [new file with mode: 0644]
sql-ledger/locale/lt/LANGUAGE [new file with mode: 0644]
sql-ledger/locale/lt/admin [new file with mode: 0644]
sql-ledger/locale/lt/all [new file with mode: 0644]
sql-ledger/locale/lt/am [new file with mode: 0644]
sql-ledger/locale/lt/ap [new file with mode: 0644]
sql-ledger/locale/lt/ar [new file with mode: 0644]
sql-ledger/locale/lt/arap [new file with mode: 0644]
sql-ledger/locale/lt/ca [new file with mode: 0644]
sql-ledger/locale/lt/cp [new file with mode: 0644]
sql-ledger/locale/lt/ct [new file with mode: 0644]
sql-ledger/locale/lt/gl [new file with mode: 0644]
sql-ledger/locale/lt/ic [new file with mode: 0644]
sql-ledger/locale/lt/io [new file with mode: 0644]
sql-ledger/locale/lt/ir [new file with mode: 0644]
sql-ledger/locale/lt/is [new file with mode: 0644]
sql-ledger/locale/lt/login [new file with mode: 0644]
sql-ledger/locale/lt/menu [new file with mode: 0644]
sql-ledger/locale/lt/oe [new file with mode: 0644]
sql-ledger/locale/lt/pe [new file with mode: 0644]
sql-ledger/locale/lt/rc [new file with mode: 0644]
sql-ledger/locale/lt/rp [new file with mode: 0644]
sql-ledger/locale/mx/COPYING [new file with mode: 0644]
sql-ledger/locale/mx/LANGUAGE [new file with mode: 0644]
sql-ledger/locale/mx/admin [new file with mode: 0644]
sql-ledger/locale/mx/all [new file with mode: 0644]
sql-ledger/locale/mx/am [new file with mode: 0644]
sql-ledger/locale/mx/ap [new file with mode: 0644]
sql-ledger/locale/mx/ar [new file with mode: 0644]
sql-ledger/locale/mx/arap [new file with mode: 0644]
sql-ledger/locale/mx/ca [new file with mode: 0644]
sql-ledger/locale/mx/cp [new file with mode: 0644]
sql-ledger/locale/mx/ct [new file with mode: 0644]
sql-ledger/locale/mx/gl [new file with mode: 0644]
sql-ledger/locale/mx/ic [new file with mode: 0644]
sql-ledger/locale/mx/io [new file with mode: 0644]
sql-ledger/locale/mx/ir [new file with mode: 0644]
sql-ledger/locale/mx/is [new file with mode: 0644]
sql-ledger/locale/mx/login [new file with mode: 0644]
sql-ledger/locale/mx/menu [new file with mode: 0644]
sql-ledger/locale/mx/oe [new file with mode: 0644]
sql-ledger/locale/mx/pe [new file with mode: 0644]
sql-ledger/locale/mx/rc [new file with mode: 0644]
sql-ledger/locale/mx/rp [new file with mode: 0644]
sql-ledger/locale/nl/COPYING [new file with mode: 0644]
sql-ledger/locale/nl/LANGUAGE [new file with mode: 0644]
sql-ledger/locale/nl/Num2text [new file with mode: 0644]
sql-ledger/locale/nl/admin [new file with mode: 0644]
sql-ledger/locale/nl/all [new file with mode: 0644]
sql-ledger/locale/nl/am [new file with mode: 0644]
sql-ledger/locale/nl/ap [new file with mode: 0644]
sql-ledger/locale/nl/ar [new file with mode: 0644]
sql-ledger/locale/nl/arap [new file with mode: 0644]
sql-ledger/locale/nl/ca [new file with mode: 0644]
sql-ledger/locale/nl/cp [new file with mode: 0644]
sql-ledger/locale/nl/ct [new file with mode: 0644]
sql-ledger/locale/nl/gl [new file with mode: 0644]
sql-ledger/locale/nl/ic [new file with mode: 0644]
sql-ledger/locale/nl/io [new file with mode: 0644]
sql-ledger/locale/nl/ir [new file with mode: 0644]
sql-ledger/locale/nl/is [new file with mode: 0644]
sql-ledger/locale/nl/login [new file with mode: 0644]
sql-ledger/locale/nl/menu [new file with mode: 0644]
sql-ledger/locale/nl/oe [new file with mode: 0644]
sql-ledger/locale/nl/pe [new file with mode: 0644]
sql-ledger/locale/nl/rc [new file with mode: 0644]
sql-ledger/locale/nl/rp [new file with mode: 0644]
sql-ledger/locale/no/COPYING [new file with mode: 0644]
sql-ledger/locale/no/LANGUAGE [new file with mode: 0644]
sql-ledger/locale/no/admin [new file with mode: 0644]
sql-ledger/locale/no/all [new file with mode: 0644]
sql-ledger/locale/no/am [new file with mode: 0644]
sql-ledger/locale/no/ap [new file with mode: 0644]
sql-ledger/locale/no/ar [new file with mode: 0644]
sql-ledger/locale/no/arap [new file with mode: 0644]
sql-ledger/locale/no/ca [new file with mode: 0644]
sql-ledger/locale/no/cp [new file with mode: 0644]
sql-ledger/locale/no/ct [new file with mode: 0644]
sql-ledger/locale/no/gl [new file with mode: 0644]
sql-ledger/locale/no/ic [new file with mode: 0644]
sql-ledger/locale/no/io [new file with mode: 0644]
sql-ledger/locale/no/ir [new file with mode: 0644]
sql-ledger/locale/no/is [new file with mode: 0644]
sql-ledger/locale/no/login [new file with mode: 0644]
sql-ledger/locale/no/menu [new file with mode: 0644]
sql-ledger/locale/no/oe [new file with mode: 0644]
sql-ledger/locale/no/pe [new file with mode: 0644]
sql-ledger/locale/no/rc [new file with mode: 0644]
sql-ledger/locale/no/rp [new file with mode: 0644]
sql-ledger/locale/pa/COPYING [new file with mode: 0644]
sql-ledger/locale/pa/LANGUAGE [new file with mode: 0644]
sql-ledger/locale/pa/admin [new file with mode: 0644]
sql-ledger/locale/pa/all [new file with mode: 0644]
sql-ledger/locale/pa/am [new file with mode: 0644]
sql-ledger/locale/pa/ap [new file with mode: 0644]
sql-ledger/locale/pa/ar [new file with mode: 0644]
sql-ledger/locale/pa/arap [new file with mode: 0644]
sql-ledger/locale/pa/ca [new file with mode: 0644]
sql-ledger/locale/pa/cp [new file with mode: 0644]
sql-ledger/locale/pa/ct [new file with mode: 0644]
sql-ledger/locale/pa/gl [new file with mode: 0644]
sql-ledger/locale/pa/ic [new file with mode: 0644]
sql-ledger/locale/pa/io [new file with mode: 0644]
sql-ledger/locale/pa/ir [new file with mode: 0644]
sql-ledger/locale/pa/is [new file with mode: 0644]
sql-ledger/locale/pa/login [new file with mode: 0644]
sql-ledger/locale/pa/menu [new file with mode: 0644]
sql-ledger/locale/pa/oe [new file with mode: 0644]
sql-ledger/locale/pa/pe [new file with mode: 0644]
sql-ledger/locale/pa/rc [new file with mode: 0644]
sql-ledger/locale/pa/rp [new file with mode: 0644]
sql-ledger/locale/pl/COPYING [new file with mode: 0644]
sql-ledger/locale/pl/LANGUAGE [new file with mode: 0644]
sql-ledger/locale/pl/admin [new file with mode: 0644]
sql-ledger/locale/pl/all [new file with mode: 0644]
sql-ledger/locale/pl/am [new file with mode: 0644]
sql-ledger/locale/pl/ap [new file with mode: 0644]
sql-ledger/locale/pl/ar [new file with mode: 0644]
sql-ledger/locale/pl/arap [new file with mode: 0644]
sql-ledger/locale/pl/ca [new file with mode: 0644]
sql-ledger/locale/pl/cp [new file with mode: 0644]
sql-ledger/locale/pl/ct [new file with mode: 0644]
sql-ledger/locale/pl/gl [new file with mode: 0644]
sql-ledger/locale/pl/ic [new file with mode: 0644]
sql-ledger/locale/pl/io [new file with mode: 0644]
sql-ledger/locale/pl/ir [new file with mode: 0644]
sql-ledger/locale/pl/is [new file with mode: 0644]
sql-ledger/locale/pl/login [new file with mode: 0644]
sql-ledger/locale/pl/menu [new file with mode: 0644]
sql-ledger/locale/pl/oe [new file with mode: 0644]
sql-ledger/locale/pl/pe [new file with mode: 0644]
sql-ledger/locale/pl/rc [new file with mode: 0644]
sql-ledger/locale/pl/rp [new file with mode: 0644]
sql-ledger/locale/pt/COPYING [new file with mode: 0644]
sql-ledger/locale/pt/LANGUAGE [new file with mode: 0644]
sql-ledger/locale/pt/admin [new file with mode: 0644]
sql-ledger/locale/pt/all [new file with mode: 0644]
sql-ledger/locale/pt/am [new file with mode: 0644]
sql-ledger/locale/pt/ap [new file with mode: 0644]
sql-ledger/locale/pt/ar [new file with mode: 0644]
sql-ledger/locale/pt/arap [new file with mode: 0644]
sql-ledger/locale/pt/ca [new file with mode: 0644]
sql-ledger/locale/pt/cp [new file with mode: 0644]
sql-ledger/locale/pt/ct [new file with mode: 0644]
sql-ledger/locale/pt/gl [new file with mode: 0644]
sql-ledger/locale/pt/ic [new file with mode: 0644]
sql-ledger/locale/pt/io [new file with mode: 0644]
sql-ledger/locale/pt/ir [new file with mode: 0644]
sql-ledger/locale/pt/is [new file with mode: 0644]
sql-ledger/locale/pt/login [new file with mode: 0644]
sql-ledger/locale/pt/menu [new file with mode: 0644]
sql-ledger/locale/pt/oe [new file with mode: 0644]
sql-ledger/locale/pt/pe [new file with mode: 0644]
sql-ledger/locale/pt/rc [new file with mode: 0644]
sql-ledger/locale/pt/rp [new file with mode: 0644]
sql-ledger/locale/ru/COPYING [new file with mode: 0644]
sql-ledger/locale/ru/LANGUAGE [new file with mode: 0644]
sql-ledger/locale/ru/admin [new file with mode: 0644]
sql-ledger/locale/ru/all [new file with mode: 0644]
sql-ledger/locale/ru/am [new file with mode: 0644]
sql-ledger/locale/ru/ap [new file with mode: 0644]
sql-ledger/locale/ru/ar [new file with mode: 0644]
sql-ledger/locale/ru/arap [new file with mode: 0644]
sql-ledger/locale/ru/ca [new file with mode: 0644]
sql-ledger/locale/ru/cp [new file with mode: 0644]
sql-ledger/locale/ru/ct [new file with mode: 0644]
sql-ledger/locale/ru/gl [new file with mode: 0644]
sql-ledger/locale/ru/ic [new file with mode: 0644]
sql-ledger/locale/ru/io [new file with mode: 0644]
sql-ledger/locale/ru/ir [new file with mode: 0644]
sql-ledger/locale/ru/is [new file with mode: 0644]
sql-ledger/locale/ru/login [new file with mode: 0644]
sql-ledger/locale/ru/menu [new file with mode: 0644]
sql-ledger/locale/ru/oe [new file with mode: 0644]
sql-ledger/locale/ru/pe [new file with mode: 0644]
sql-ledger/locale/ru/rc [new file with mode: 0644]
sql-ledger/locale/ru/rp [new file with mode: 0644]
sql-ledger/locale/se/COPYING [new file with mode: 0644]
sql-ledger/locale/se/LANGUAGE [new file with mode: 0644]
sql-ledger/locale/se/admin [new file with mode: 0644]
sql-ledger/locale/se/all [new file with mode: 0644]
sql-ledger/locale/se/am [new file with mode: 0644]
sql-ledger/locale/se/ap [new file with mode: 0644]
sql-ledger/locale/se/ar [new file with mode: 0644]
sql-ledger/locale/se/arap [new file with mode: 0644]
sql-ledger/locale/se/ca [new file with mode: 0644]
sql-ledger/locale/se/cp [new file with mode: 0644]
sql-ledger/locale/se/ct [new file with mode: 0644]
sql-ledger/locale/se/gl [new file with mode: 0644]
sql-ledger/locale/se/ic [new file with mode: 0644]
sql-ledger/locale/se/io [new file with mode: 0644]
sql-ledger/locale/se/ir [new file with mode: 0644]
sql-ledger/locale/se/is [new file with mode: 0644]
sql-ledger/locale/se/login [new file with mode: 0644]
sql-ledger/locale/se/menu [new file with mode: 0644]
sql-ledger/locale/se/oe [new file with mode: 0644]
sql-ledger/locale/se/pe [new file with mode: 0644]
sql-ledger/locale/se/rc [new file with mode: 0644]
sql-ledger/locale/se/rp [new file with mode: 0644]
sql-ledger/locale/tr/COPYING [new file with mode: 0644]
sql-ledger/locale/tr/LANGUAGE [new file with mode: 0644]
sql-ledger/locale/tr/admin [new file with mode: 0644]
sql-ledger/locale/tr/all [new file with mode: 0644]
sql-ledger/locale/tr/am [new file with mode: 0644]
sql-ledger/locale/tr/ap [new file with mode: 0644]
sql-ledger/locale/tr/ar [new file with mode: 0644]
sql-ledger/locale/tr/arap [new file with mode: 0644]
sql-ledger/locale/tr/ca [new file with mode: 0644]
sql-ledger/locale/tr/cp [new file with mode: 0644]
sql-ledger/locale/tr/ct [new file with mode: 0644]
sql-ledger/locale/tr/gl [new file with mode: 0644]
sql-ledger/locale/tr/ic [new file with mode: 0644]
sql-ledger/locale/tr/io [new file with mode: 0644]
sql-ledger/locale/tr/ir [new file with mode: 0644]
sql-ledger/locale/tr/is [new file with mode: 0644]
sql-ledger/locale/tr/login [new file with mode: 0644]
sql-ledger/locale/tr/menu [new file with mode: 0644]
sql-ledger/locale/tr/oe [new file with mode: 0644]
sql-ledger/locale/tr/pe [new file with mode: 0644]
sql-ledger/locale/tr/rc [new file with mode: 0644]
sql-ledger/locale/tr/rp [new file with mode: 0644]
sql-ledger/locale/tw/COPYING [new file with mode: 0644]
sql-ledger/locale/tw/LANGUAGE [new file with mode: 0644]
sql-ledger/locale/tw/admin [new file with mode: 0644]
sql-ledger/locale/tw/all [new file with mode: 0644]
sql-ledger/locale/tw/am [new file with mode: 0644]
sql-ledger/locale/tw/ap [new file with mode: 0644]
sql-ledger/locale/tw/ar [new file with mode: 0644]
sql-ledger/locale/tw/arap [new file with mode: 0644]
sql-ledger/locale/tw/ca [new file with mode: 0644]
sql-ledger/locale/tw/cp [new file with mode: 0644]
sql-ledger/locale/tw/ct [new file with mode: 0644]
sql-ledger/locale/tw/gl [new file with mode: 0644]
sql-ledger/locale/tw/ic [new file with mode: 0644]
sql-ledger/locale/tw/io [new file with mode: 0644]
sql-ledger/locale/tw/ir [new file with mode: 0644]
sql-ledger/locale/tw/is [new file with mode: 0644]
sql-ledger/locale/tw/login [new file with mode: 0644]
sql-ledger/locale/tw/menu [new file with mode: 0644]
sql-ledger/locale/tw/oe [new file with mode: 0644]
sql-ledger/locale/tw/pe [new file with mode: 0644]
sql-ledger/locale/tw/rc [new file with mode: 0644]
sql-ledger/locale/tw/rp [new file with mode: 0644]
sql-ledger/locale/ua/COPYING [new file with mode: 0644]
sql-ledger/locale/ua/LANGUAGE [new file with mode: 0644]
sql-ledger/locale/ua/admin [new file with mode: 0644]
sql-ledger/locale/ua/all [new file with mode: 0644]
sql-ledger/locale/ua/am [new file with mode: 0644]
sql-ledger/locale/ua/ap [new file with mode: 0644]
sql-ledger/locale/ua/ar [new file with mode: 0644]
sql-ledger/locale/ua/arap [new file with mode: 0644]
sql-ledger/locale/ua/ca [new file with mode: 0644]
sql-ledger/locale/ua/cp [new file with mode: 0644]
sql-ledger/locale/ua/ct [new file with mode: 0644]
sql-ledger/locale/ua/gl [new file with mode: 0644]
sql-ledger/locale/ua/ic [new file with mode: 0644]
sql-ledger/locale/ua/io [new file with mode: 0644]
sql-ledger/locale/ua/ir [new file with mode: 0644]
sql-ledger/locale/ua/is [new file with mode: 0644]
sql-ledger/locale/ua/login [new file with mode: 0644]
sql-ledger/locale/ua/menu [new file with mode: 0644]
sql-ledger/locale/ua/oe [new file with mode: 0644]
sql-ledger/locale/ua/pe [new file with mode: 0644]
sql-ledger/locale/ua/rc [new file with mode: 0644]
sql-ledger/locale/ua/rp [new file with mode: 0644]
sql-ledger/locale/ve/COPYING [new file with mode: 0644]
sql-ledger/locale/ve/LANGUAGE [new file with mode: 0644]
sql-ledger/locale/ve/admin [new file with mode: 0644]
sql-ledger/locale/ve/all [new file with mode: 0644]
sql-ledger/locale/ve/am [new file with mode: 0644]
sql-ledger/locale/ve/ap [new file with mode: 0644]
sql-ledger/locale/ve/ar [new file with mode: 0644]
sql-ledger/locale/ve/arap [new file with mode: 0644]
sql-ledger/locale/ve/ca [new file with mode: 0644]
sql-ledger/locale/ve/cp [new file with mode: 0644]
sql-ledger/locale/ve/ct [new file with mode: 0644]
sql-ledger/locale/ve/gl [new file with mode: 0644]
sql-ledger/locale/ve/ic [new file with mode: 0644]
sql-ledger/locale/ve/io [new file with mode: 0644]
sql-ledger/locale/ve/ir [new file with mode: 0644]
sql-ledger/locale/ve/is [new file with mode: 0644]
sql-ledger/locale/ve/login [new file with mode: 0644]
sql-ledger/locale/ve/menu [new file with mode: 0644]
sql-ledger/locale/ve/oe [new file with mode: 0644]
sql-ledger/locale/ve/pe [new file with mode: 0644]
sql-ledger/locale/ve/rc [new file with mode: 0644]
sql-ledger/locale/ve/rp [new file with mode: 0644]
sql-ledger/login.pl [new file with mode: 0755]
sql-ledger/menu.ini [new file with mode: 0644]
sql-ledger/setup.pl [new file with mode: 0755]
sql-ledger/sql-ledger.conf.default [new file with mode: 0644]
sql-ledger/sql-ledger.png [new file with mode: 0644]
sql-ledger/sql/Austria-chart.sql [new file with mode: 0644]
sql-ledger/sql/Austria-gifi.sql [new file with mode: 0644]
sql-ledger/sql/Brazil_General-chart.sql [new file with mode: 0644]
sql-ledger/sql/Canada-gifi.sql [new file with mode: 0644]
sql-ledger/sql/Canada_General-chart.sql [new file with mode: 0644]
sql-ledger/sql/Czech_Republic-chart.sql [new file with mode: 0644]
sql-ledger/sql/Danish_Default-chart.sql [new file with mode: 0644]
sql-ledger/sql/Default-chart.sql [new file with mode: 0644]
sql-ledger/sql/Dutch_Default-chart.sql [new file with mode: 0644]
sql-ledger/sql/Dutch_Standard-chart.sql [new file with mode: 0644]
sql-ledger/sql/France-chart.sql [new file with mode: 0644]
sql-ledger/sql/German-Sample-chart.sql [new file with mode: 0644]
sql-ledger/sql/German-Sample-gifi.sql [new file with mode: 0644]
sql-ledger/sql/Germany-DATEV-SKR03-chart.sql [new file with mode: 0644]
sql-ledger/sql/Germany-DATEV-SKR03-gifi.sql [new file with mode: 0644]
sql-ledger/sql/Germany-SKR03-chart.sql [new file with mode: 0644]
sql-ledger/sql/Germany-SKR03-gifi.sql [new file with mode: 0644]
sql-ledger/sql/Italy-chart.sql [new file with mode: 0644]
sql-ledger/sql/Oracle-indices.sql [new file with mode: 0644]
sql-ledger/sql/Oracle-tables.sql [new file with mode: 0644]
sql-ledger/sql/Oracle-upgrade-1.8.0-1.8.4.sql [new file with mode: 0644]
sql-ledger/sql/Oracle-upgrade-1.8.4-1.8.5.sql [new file with mode: 0644]
sql-ledger/sql/Oracle-upgrade-1.8.5-2.0.0.sql [new file with mode: 0644]
sql-ledger/sql/Oracle-upgrade-2.0.0-2.0.8.sql [new file with mode: 0644]
sql-ledger/sql/Pg-indices.sql [new file with mode: 0644]
sql-ledger/sql/Pg-tables.sql [new file with mode: 0644]
sql-ledger/sql/Pg-upgrade-1.2.6-1.2.7.sql [new file with mode: 0644]
sql-ledger/sql/Pg-upgrade-1.2.7-1.4.0.sql [new file with mode: 0644]
sql-ledger/sql/Pg-upgrade-1.4.0-1.6.0.sql [new file with mode: 0644]
sql-ledger/sql/Pg-upgrade-1.6.0-1.8.0.sql [new file with mode: 0644]
sql-ledger/sql/Pg-upgrade-1.8.0-1.8.4.sql [new file with mode: 0644]
sql-ledger/sql/Pg-upgrade-1.8.4-1.8.5.sql [new file with mode: 0644]
sql-ledger/sql/Pg-upgrade-1.8.5-2.0.0.sql [new file with mode: 0644]
sql-ledger/sql/Pg-upgrade-2.0.0-2.0.8.sql [new file with mode: 0644]
sql-ledger/sql/Poland-chart.sql [new file with mode: 0644]
sql-ledger/sql/Simplified_Chinese_Default-chart.sql [new file with mode: 0644]
sql-ledger/sql/Spain-chart.sql [new file with mode: 0644]
sql-ledger/sql/Swiss-German-chart.sql [new file with mode: 0644]
sql-ledger/sql/Swiss-German-gifi.sql [new file with mode: 0644]
sql-ledger/sql/Traditional_Chinese_Default-chart.sql [new file with mode: 0644]
sql-ledger/sql/US_General-chart.sql [new file with mode: 0644]
sql-ledger/templates/Brazilian_Portuguese-balance_sheet.html [new file with mode: 0644]
sql-ledger/templates/Brazilian_Portuguese-check.tex [new file with mode: 0644]
sql-ledger/templates/Brazilian_Portuguese-income_statement.html [new file with mode: 0644]
sql-ledger/templates/Brazilian_Portuguese-invoice.html [new file with mode: 0644]
sql-ledger/templates/Brazilian_Portuguese-invoice.tex [new file with mode: 0644]
sql-ledger/templates/Brazilian_Portuguese-packing_list.html [new file with mode: 0644]
sql-ledger/templates/Brazilian_Portuguese-packing_list.tex [new file with mode: 0644]
sql-ledger/templates/Brazilian_Portuguese-purchase_order.html [new file with mode: 0644]
sql-ledger/templates/Brazilian_Portuguese-purchase_order.tex [new file with mode: 0644]
sql-ledger/templates/Brazilian_Portuguese-receipt.tex [new file with mode: 0644]
sql-ledger/templates/Brazilian_Portuguese-sales_order.html [new file with mode: 0644]
sql-ledger/templates/Brazilian_Portuguese-sales_order.tex [new file with mode: 0644]
sql-ledger/templates/Brazilian_Portuguese-statement.html [new file with mode: 0644]
sql-ledger/templates/Brazilian_Portuguese-statement.tex [new file with mode: 0644]
sql-ledger/templates/Danish-balance_sheet.html [new file with mode: 0644]
sql-ledger/templates/Danish-check.tex [new file with mode: 0644]
sql-ledger/templates/Danish-income_statement.html [new file with mode: 0644]
sql-ledger/templates/Danish-invoice.html [new file with mode: 0644]
sql-ledger/templates/Danish-invoice.tex [new file with mode: 0644]
sql-ledger/templates/Danish-packing_list.html [new file with mode: 0644]
sql-ledger/templates/Danish-packing_list.tex [new file with mode: 0644]
sql-ledger/templates/Danish-purchase_order.html [new file with mode: 0644]
sql-ledger/templates/Danish-purchase_order.tex [new file with mode: 0644]
sql-ledger/templates/Danish-receipt.tex [new file with mode: 0644]
sql-ledger/templates/Danish-sales_order.html [new file with mode: 0644]
sql-ledger/templates/Danish-sales_order.tex [new file with mode: 0644]
sql-ledger/templates/Danish-statement.html [new file with mode: 0644]
sql-ledger/templates/Danish-statement.tex [new file with mode: 0644]
sql-ledger/templates/Default-balance_sheet.html [new file with mode: 0644]
sql-ledger/templates/Default-check.tex [new file with mode: 0644]
sql-ledger/templates/Default-income_statement.html [new file with mode: 0644]
sql-ledger/templates/Default-invoice.html [new file with mode: 0644]
sql-ledger/templates/Default-invoice.tex [new file with mode: 0644]
sql-ledger/templates/Default-packing_list.html [new file with mode: 0644]
sql-ledger/templates/Default-packing_list.tex [new file with mode: 0644]
sql-ledger/templates/Default-purchase_order.html [new file with mode: 0644]
sql-ledger/templates/Default-purchase_order.tex [new file with mode: 0644]
sql-ledger/templates/Default-receipt.tex [new file with mode: 0644]
sql-ledger/templates/Default-sales_order.html [new file with mode: 0644]
sql-ledger/templates/Default-sales_order.tex [new file with mode: 0644]
sql-ledger/templates/Default-statement.html [new file with mode: 0644]
sql-ledger/templates/Default-statement.tex [new file with mode: 0644]
sql-ledger/templates/Dutch-balance_sheet.html [new file with mode: 0644]
sql-ledger/templates/Dutch-check.tex [new file with mode: 0644]
sql-ledger/templates/Dutch-income_statement.html [new file with mode: 0644]
sql-ledger/templates/Dutch-invoice.html [new file with mode: 0644]
sql-ledger/templates/Dutch-invoice.tex [new file with mode: 0644]
sql-ledger/templates/Dutch-packing_list.html [new file with mode: 0644]
sql-ledger/templates/Dutch-packing_list.tex [new file with mode: 0644]
sql-ledger/templates/Dutch-purchase_order.html [new file with mode: 0644]
sql-ledger/templates/Dutch-purchase_order.tex [new file with mode: 0644]
sql-ledger/templates/Dutch-receipt.tex [new file with mode: 0644]
sql-ledger/templates/Dutch-sales_order.html [new file with mode: 0644]
sql-ledger/templates/Dutch-sales_order.tex [new file with mode: 0644]
sql-ledger/templates/Dutch-statement.html [new file with mode: 0644]
sql-ledger/templates/Dutch-statement.tex [new file with mode: 0644]
sql-ledger/templates/Estonian-balance_sheet.html [new file with mode: 0644]
sql-ledger/templates/Estonian-check.tex [new file with mode: 0644]
sql-ledger/templates/Estonian-income_statement.html [new file with mode: 0644]
sql-ledger/templates/Estonian-invoice.html [new file with mode: 0644]
sql-ledger/templates/Estonian-invoice.tex [new file with mode: 0644]
sql-ledger/templates/Estonian-packing_list.html [new file with mode: 0644]
sql-ledger/templates/Estonian-packing_list.tex [new file with mode: 0644]
sql-ledger/templates/Estonian-purchase_order.html [new file with mode: 0644]
sql-ledger/templates/Estonian-purchase_order.tex [new file with mode: 0644]
sql-ledger/templates/Estonian-receipt.tex [new file with mode: 0644]
sql-ledger/templates/Estonian-sales_order.html [new file with mode: 0644]
sql-ledger/templates/Estonian-sales_order.tex [new file with mode: 0644]
sql-ledger/templates/Estonian-statement.html [new file with mode: 0644]
sql-ledger/templates/Estonian-statement.tex [new file with mode: 0644]
sql-ledger/templates/French-balance_sheet.html [new file with mode: 0644]
sql-ledger/templates/French-check.tex [new file with mode: 0644]
sql-ledger/templates/French-income_statement.html [new file with mode: 0644]
sql-ledger/templates/French-invoice.html [new file with mode: 0644]
sql-ledger/templates/French-invoice.tex [new file with mode: 0644]
sql-ledger/templates/French-packing_list.html [new file with mode: 0644]
sql-ledger/templates/French-packing_list.tex [new file with mode: 0644]
sql-ledger/templates/French-purchase_order.html [new file with mode: 0644]
sql-ledger/templates/French-purchase_order.tex [new file with mode: 0644]
sql-ledger/templates/French-receipt.tex [new file with mode: 0644]
sql-ledger/templates/French-sales_order.html [new file with mode: 0644]
sql-ledger/templates/French-sales_order.tex [new file with mode: 0644]
sql-ledger/templates/French-statement.html [new file with mode: 0644]
sql-ledger/templates/French-statement.tex [new file with mode: 0644]
sql-ledger/templates/German-balance_sheet.html [new file with mode: 0644]
sql-ledger/templates/German-check.tex [new file with mode: 0644]
sql-ledger/templates/German-income_statement.html [new file with mode: 0644]
sql-ledger/templates/German-invoice.html [new file with mode: 0644]
sql-ledger/templates/German-invoice.tex [new file with mode: 0644]
sql-ledger/templates/German-packing_list.html [new file with mode: 0644]
sql-ledger/templates/German-packing_list.tex [new file with mode: 0644]
sql-ledger/templates/German-purchase_order.html [new file with mode: 0644]
sql-ledger/templates/German-purchase_order.tex [new file with mode: 0644]
sql-ledger/templates/German-receipt.tex [new file with mode: 0644]
sql-ledger/templates/German-sales_order.html [new file with mode: 0644]
sql-ledger/templates/German-sales_order.tex [new file with mode: 0644]
sql-ledger/templates/German-statement.html [new file with mode: 0644]
sql-ledger/templates/German-statement.tex [new file with mode: 0644]
sql-ledger/templates/Service-balance_sheet.html [new file with mode: 0644]
sql-ledger/templates/Service-check.tex [new file with mode: 0644]
sql-ledger/templates/Service-income_statement.html [new file with mode: 0644]
sql-ledger/templates/Service-invoice.html [new file with mode: 0644]
sql-ledger/templates/Service-invoice.tex [new file with mode: 0644]
sql-ledger/templates/Service-packing_list.html [new file with mode: 0644]
sql-ledger/templates/Service-packing_list.tex [new file with mode: 0644]
sql-ledger/templates/Service-purchase_order.html [new file with mode: 0644]
sql-ledger/templates/Service-purchase_order.tex [new file with mode: 0644]
sql-ledger/templates/Service-receipt.tex [new file with mode: 0644]
sql-ledger/templates/Service-sales_order.html [new file with mode: 0644]
sql-ledger/templates/Service-sales_order.tex [new file with mode: 0644]
sql-ledger/templates/Service-statement.html [new file with mode: 0644]
sql-ledger/templates/Service-statement.tex [new file with mode: 0644]
sql-ledger/templates/Spanish_A4-balance_sheet.html [new file with mode: 0644]
sql-ledger/templates/Spanish_A4-check.tex [new file with mode: 0644]
sql-ledger/templates/Spanish_A4-income_statement.html [new file with mode: 0644]
sql-ledger/templates/Spanish_A4-invoice.html [new file with mode: 0644]
sql-ledger/templates/Spanish_A4-invoice.tex [new file with mode: 0644]
sql-ledger/templates/Spanish_A4-packing_list.html [new file with mode: 0644]
sql-ledger/templates/Spanish_A4-packing_list.tex [new file with mode: 0644]
sql-ledger/templates/Spanish_A4-purchase_order.html [new file with mode: 0644]
sql-ledger/templates/Spanish_A4-purchase_order.tex [new file with mode: 0644]
sql-ledger/templates/Spanish_A4-receipt.tex [new file with mode: 0644]
sql-ledger/templates/Spanish_A4-sales_order.html [new file with mode: 0644]
sql-ledger/templates/Spanish_A4-sales_order.tex [new file with mode: 0644]
sql-ledger/templates/Spanish_A4-statement.html [new file with mode: 0644]
sql-ledger/templates/Spanish_A4-statement.tex [new file with mode: 0644]
sql-ledger/templates/Spanish_Letter-balance_sheet.html [new file with mode: 0644]
sql-ledger/templates/Spanish_Letter-check.tex [new file with mode: 0644]
sql-ledger/templates/Spanish_Letter-income_statement.html [new file with mode: 0644]
sql-ledger/templates/Spanish_Letter-invoice.html [new file with mode: 0644]
sql-ledger/templates/Spanish_Letter-invoice.tex [new file with mode: 0644]
sql-ledger/templates/Spanish_Letter-packing_list.html [new file with mode: 0644]
sql-ledger/templates/Spanish_Letter-packing_list.tex [new file with mode: 0644]
sql-ledger/templates/Spanish_Letter-purchase_order.html [new file with mode: 0644]
sql-ledger/templates/Spanish_Letter-purchase_order.tex [new file with mode: 0644]
sql-ledger/templates/Spanish_Letter-receipt.tex [new file with mode: 0644]
sql-ledger/templates/Spanish_Letter-sales_order.html [new file with mode: 0644]
sql-ledger/templates/Spanish_Letter-sales_order.tex [new file with mode: 0644]
sql-ledger/templates/Spanish_Letter-statement.html [new file with mode: 0644]
sql-ledger/templates/Spanish_Letter-statement.tex [new file with mode: 0644]
sql-ledger/users/members.default [new file with mode: 0644]

diff --git a/sql-ledger/SL/AM.pm b/sql-ledger/SL/AM.pm
new file mode 100644 (file)
index 0000000..d691b3c
--- /dev/null
@@ -0,0 +1,694 @@
+#=====================================================================
+# SQL-Ledger Accounting
+# Copyright (C) 2001
+#
+#  Author: Dieter Simader
+#   Email: dsimader@sql-ledger.org
+#     Web: http://www.sql-ledger.org
+#
+#  Contributors:
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#======================================================================
+#
+# 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
+                 FROM chart
+                WHERE id = $form->{id}|;
+  my $sth = $dbh->prepare($query);
+  $sth->execute || $form->dberror($query);
+
+  my $ref = $sth->fetchrow_hashref(NAME_lc);
+  
+  foreach my $key (keys %$ref) {
+    $form->{"$key"} = $ref->{"$key"};
+  }
+
+  $sth->finish;
+
+
+  # get default accounts
+  $query = qq|SELECT inventory_accno_id, income_accno_id, expense_accno_id
+              FROM defaults|;
+  $sth = $dbh->prepare($query);
+  $sth->execute || $form->dberror($query);
+
+  $ref = $sth->fetchrow_hashref(NAME_lc);
+
+  map { $form->{$_} = $ref->{$_} } keys %ref;
+
+  $sth->finish;
+  $dbh->disconnect;
+
+}
+
+
+sub save_account {
+  my ($self, $myconfig, $form) = @_;
+
+  # connect to database, turn off AutoCommit
+  my $dbh = $form->dbconnect_noauto($myconfig);
+
+  # sanity check, can't have AR with AR_...
+  if ($form->{AR} || $form->{AP} || $form->{IC}) {
+    map { delete $form->{$_} } qw(AR_amount AR_tax AR_paid AP_amount AP_tax AP_paid IC_sale IC_cogs IC_taxpart IC_income IC_expense IC_taxservice);
+  }
+  
+  $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_sale},
+                   $form->{IC_cogs},
+                   $form->{IC_taxpart},
+                   $form->{IC_income},
+                   $form->{IC_expense},
+                   $form->{IC_taxservice},
+                   $form->{CT_tax}
+                   ) {
+     $form->{link} .= "${item}:" if ($item);
+  }
+  chop $form->{link};
+
+  # if we have an id then replace the old record
+  $form->{description} =~ s/'/''/g;
+
+  # strip blanks from accno
+  map { $form->{$_} =~ s/ //g; } qw(accno gifi_accno);
+  
+  my ($query, $sth);
+  
+  if ($form->{id}) {
+    $query = qq|UPDATE chart SET
+                accno = '$form->{accno}',
+               description = '$form->{description}',
+               charttype = '$form->{charttype}',
+               gifi_accno = '$form->{gifi_accno}',
+               category = '$form->{category}',
+               link = '$form->{link}'
+               WHERE id = $form->{id}|;
+  } else {
+    $query = qq|INSERT INTO chart 
+                (accno, description, charttype, gifi_accno, category, link)
+                VALUES ('$form->{accno}', '$form->{description}',
+               '$form->{charttype}', '$form->{gifi_accno}',
+               '$form->{category}', '$form->{link}')|;
+  }
+  $dbh->do($query) || $form->dberror($query);
+  
+
+  if ($form->{IC_taxpart} || $form->{IC_taxservice} || $form->{CT_tax}) {
+
+    my $chart_id = $form->{id};
+    
+    unless ($form->{id}) {
+      # get id from chart
+      $query = qq|SELECT id
+                  FROM chart
+                 WHERE accno = '$form->{accno}'|;
+      $sth = $dbh->prepare($query);
+      $sth->execute || $form->dberror($query);
+
+      ($chart_id) = $sth->fetchrow_array;
+      $sth->finish;
+    }
+    
+    # add account if it doesn't exist in tax
+    $query = qq|SELECT chart_id
+                FROM tax
+               WHERE chart_id = $chart_id|;
+    $sth = $dbh->prepare($query);
+    $sth->execute || $form->dberror($query);
+
+    my ($tax_id) = $sth->fetchrow_array;
+    $sth->finish;
+    
+    # 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);
+
+  # 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}'|;
+  my $sth = $dbh->prepare($query);
+  $sth->execute || $form->dberror($query);
+
+  my $ref = $sth->fetchrow_hashref(NAME_lc);
+  
+  map { $form->{$_} = $ref->{$_} } keys %$ref;
+
+  $sth->finish;
+  $dbh->disconnect;
+
+}
+
+
+sub save_gifi {
+  my ($self, $myconfig, $form) = @_;
+  
+  # connect to database
+  my $dbh = $form->dbconnect($myconfig);
+  
+  $form->{description} =~ s/'/''/g;
+  $form->{accno} =~ s/ //g;
+
+  # id is the old account number!
+  if ($form->{id}) {
+    $query = qq|UPDATE gifi SET
+                accno = '$form->{accno}',
+               description = '$form->{description}'
+               WHERE accno = '$form->{id}'|;
+  } else {
+    $query = qq|INSERT INTO gifi 
+                (accno, description)
+                VALUES ('$form->{accno}', '$form->{description}')|;
+  }
+  $dbh->do($query) || $form->dberror($query);
+  
+  $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 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 \r
+  $form->{body} =~ s/\r\n/\n/g;
+  print TEMPLATE $form->{body};
+
+  close(TEMPLATE);
+
+}
+
+
+
+sub save_preferences {
+  my ($self, $myconfig, $form, $memberfile, $userspath) = @_;
+
+  map { ($form->{$_}) = split /--/, $form->{$_} } qw(inventory_accno income_accno expense_accno fxgain_accno fxloss_accno);
+  
+  my @a;
+  $form->{curr} =~ s/ //g;
+  map { push(@a, uc pack "A3", $_) if $_ } split /:/, $form->{curr};
+  $form->{curr} = join ':', @a;
+    
+  # connect to database
+  my $dbh = $form->dbconnect_noauto($myconfig);
+  
+  # these defaults are database wide
+  # user specific variables are in myconfig
+  # save defaults
+  my $query = qq|UPDATE defaults SET
+                 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}'),
+                fxgain_accno_id =
+                    (SELECT id FROM chart
+                               WHERE accno = '$form->{fxgain_accno}'),
+                fxloss_accno_id =
+                    (SELECT id FROM chart
+                               WHERE accno = '$form->{fxloss_accno}'),
+                invnumber = '$form->{invnumber}',
+                sonumber = '$form->{sonumber}',
+                ponumber = '$form->{ponumber}',
+                yearend = '$form->{yearend}',
+                curr = '$form->{curr}',
+                weightunit = '$form->{weightunit}',
+                businessnumber = '$form->{businessnumber}'
+               |;
+  $dbh->do($query) || $form->dberror($query);
+
+  # update name
+  my $name = $form->{name};
+  $name =~ s/'/''/g;
+  $query = qq|UPDATE employee
+              SET name = '$name'
+             WHERE login = '$form->{login}'|;
+  $dbh->do($query) || $form->dberror($query);
+  
+  foreach my $item (split / /, $form->{taxaccounts}) {
+    $query = qq|UPDATE tax
+               SET rate = |.($form->{$item} / 100).qq|,
+               taxnumber = '$form->{"taxnumber_$item"}'
+               WHERE chart_id = $item|;
+    $dbh->do($query) || $form->dberror($query);
+  }
+
+  my $rc = $dbh->commit;
+  $dbh->disconnect;
+
+  # save first currency in myconfig
+  $form->{currency} = substr($form->{curr},0,3);
+  
+  my $myconfig = new User "$memberfile", "$form->{login}";
+  
+  foreach my $item (keys %$form) {
+    $myconfig->{$item} = $form->{$item};
+  }
+
+  $myconfig->save_member($memberfile, $userspath);
+
+  $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);
+  
+  $form->{defaults} = $sth->fetchrow_hashref(NAME_lc);
+  $form->{defaults}{IC} = $form->{defaults}{inventory_accno_id};
+  $form->{defaults}{IC_income} = $form->{defaults}{income_accno_id};
+  $form->{defaults}{IC_expense} = $form->{defaults}{expense_accno_id};
+  $form->{defaults}{FX_gain} = $form->{defaults}{fxgain_accno_id};
+  $form->{defaults}{FX_loss} = $form->{defaults}{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 || $self->dberror($query);
+
+  while (my $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->{IC}{$nkey}{$ref->{accno}} } = ( id => $ref->{id},
+                                        description => $ref->{description} );
+      }
+    }
+  }
+  $sth->finish;
+
+
+  $query = qq|SELECT id, accno, description
+              FROM chart
+             WHERE category = 'I'
+             AND charttype = 'A'
+              ORDER BY accno|;
+  $sth = $dbh->prepare($query);
+  $sth->execute || $self->dberror($query);
+
+  while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
+    %{ $form->{IC}{FX_gain}{$ref->{accno}} } = ( id => $ref->{id},
+                                      description => $ref->{description} );
+  }
+  $sth->finish;
+
+  $query = qq|SELECT id, accno, description
+              FROM chart
+             WHERE category = 'E'
+             AND charttype = 'A'
+              ORDER BY accno|;
+  $sth = $dbh->prepare($query);
+  $sth->execute || $self->dberror($query);
+
+  while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
+    %{ $form->{IC}{FX_loss}{$ref->{accno}} } = ( id => $ref->{id},
+                                      description => $ref->{description} );
+  }
+  $sth->finish;
+
+
+  # now get the tax rates and numbers
+  $query = qq|SELECT chart.id, chart.accno, chart.description,
+              tax.rate * 100 AS rate, tax.taxnumber
+              FROM chart, tax
+             WHERE chart.id = tax.chart_id|;
+
+  $sth = $dbh->prepare($query);
+  $sth->execute || $form->dberror($query);
+
+  while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
+    $form->{taxrates}{$ref->{accno}}{id} = $ref->{id};
+    $form->{taxrates}{$ref->{accno}}{description} = $ref->{description};
+    $form->{taxrates}{$ref->{accno}}{taxnumber} = $ref->{taxnumber} if $ref->{taxnumber};
+    $form->{taxrates}{$ref->{accno}}{rate} = $ref->{rate} if $ref->{rate};
+  }
+
+  $sth->finish;
+  $dbh->disconnect;
+  
+}
+
+
+sub backup {
+  my ($self, $myconfig, $form, $userspath) = @_;
+  
+  my ($tmpfile, $out, $mail);
+  
+  if ($form->{media} eq 'email') {
+
+    my $boundary = time;
+    $tmpfile = "$userspath/$boundary.$myconfig->{dbname}-$form->{dbversion}.sql";
+    $out = $form->{OUT};
+    $form->{OUT} = ">$tmpfile";
+    
+    use SL::Mailer;
+    $mail = new Mailer;
+
+    $mail->{to} = qq|"$myconfig->{name}" <$myconfig->{email}>|;
+    $mail->{from} = qq|"$myconfig->{name}" <$myconfig->{email}>|;
+    $mail->{subject} = "SQL-Ledger Backup / $myconfig->{dbname}-$form->{dbversion}.sql";
+    @{ $mail->{attachments} } = ($tmpfile);
+    $mail->{version} = $form->{version};
+    $mail->{fileid} = "$boundary.";
+
+    $myconfig->{signature} =~ s/\\n/\r\n/g;
+    $mail->{message} = "--\n$myconfig->{signature}";
+    
+  }
+    
+  if ($form->{OUT}) {
+    open(OUT, "$form->{OUT}") or $form->error("$form->{OUT} : $!");
+  } else {
+    open(OUT, ">-") or $form->error("STDOUT : $!");
+  }
+
+  if ($form->{media} eq 'file') {
+    print OUT qq|Content-Type: Application/File;
+Content-Disposition: filename="$myconfig->{dbname}-$form->{dbversion}.sql"\n\n|;
+  }
+
+  # connect to database
+  my $dbh = $form->dbconnect($myconfig);
+
+  # get all the tables
+  my @tables = $dbh->tables;
+  
+  my $today = scalar localtime;
+  
+
+  $myconfig->{dbhost} = 'localhost' unless $myconfig->{dbhost};
+  
+  print OUT qq|-- SQL-Ledger Backup
+-- Dataset: $myconfig->{dbname}
+-- Version: $form->{dbversion}
+-- Host: $myconfig->{dbhost}
+-- Login: $form->{login}
+-- User: $myconfig->{name}
+-- Date: $today
+--
+-- set options
+$myconfig->{dboptions};
+--
+|;
+
+  foreach $table (@tables) {
+    my $query = qq|SELECT * FROM $table|;
+    
+    my $sth = $dbh->prepare($query);
+    $sth->execute || $form->dberror($query);
+
+    $query = qq|INSERT INTO $table (|;
+    map { $query .= qq|$sth->{NAME}->[$_],| } (0 .. $sth->{NUM_OF_FIELDS} - 1);
+    chop $query;
+
+    $query .= qq|) VALUES|;
+    
+    print OUT qq|--
+DELETE FROM $table;
+|;
+    while (my @arr = $sth->fetchrow_array) {
+
+      $fields = "(";
+      foreach my $item (@arr) {
+       if (defined $item) {
+         $item =~ s/'/''/g;
+         $fields .= qq|'$item',|;
+       } else {
+         $fields .= 'NULL,';
+       }
+      }
+       
+      chop $fields;
+      $fields .= ")";
+       
+      print OUT qq|$query $fields;\n|;
+    }
+    
+    $sth->finish;
+  }
+
+  $query = qq|SELECT last_value FROM id|;
+  $sth = $dbh->prepare($query);
+  $sth->execute || $form->dberror($query);
+
+  my ($id) = $sth->fetchrow_array;
+  $sth->finish;
+  
+  print OUT qq|--
+DROP SEQUENCE id;
+CREATE SEQUENCE id START $id;
+|;
+  
+  close(OUT);
+  
+  $dbh->disconnect;
+
+  if ($form->{media} eq 'email') {
+    my $err = $mail->send($out);
+    $_ = $tmpfile;
+    unlink;
+  }
+    
+}
+
+
+sub closedto {
+  my ($self, $myconfig, $form) = @_;
+
+  my $dbh = $form->dbconnect($myconfig);
+
+  my $query = qq|SELECT closedto, revtrans FROM defaults|;
+  my $sth = $dbh->prepare($query);
+  $sth->execute || $form->dberror($query);
+
+  ($form->{closedto}, $form->{revtrans}) = $sth->fetchrow_array;
+  
+  $sth->finish;
+  
+  $dbh->disconnect;
+
+}
+
+sub closebooks {
+  my ($self, $myconfig, $form) = @_;
+
+  my $dbh = $form->dbconnect($myconfig);
+
+  if ($form->{revtrans}) {
+   
+    $query = qq|UPDATE defaults SET closedto = NULL,
+                                   revtrans = '1'|;
+  } else {
+    if ($form->{closedto}) {
+    
+      $query = qq|UPDATE defaults SET closedto = '$form->{closedto}',
+                                     revtrans = '0'|;
+    } else {
+      
+      $query = qq|UPDATE defaults SET closedto = NULL,
+                                     revtrans = '0'|;
+    }
+  }
+
+  # set close in defaults
+  $dbh->do($query) || $form->dberror($query);
+  
+  $dbh->disconnect;
+  
+}
+
+
+1;
+
diff --git a/sql-ledger/SL/AP.pm b/sql-ledger/SL/AP.pm
new file mode 100644 (file)
index 0000000..e1870f8
--- /dev/null
@@ -0,0 +1,381 @@
+#=====================================================================
+# SQL-Ledger Accounting
+# Copyright (C) 2001
+#
+#  Author: Dieter Simader
+#   Email: dsimader@sql-ledger.org
+#     Web: http://www.sql-ledger.org
+#
+#  Contributors:
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#======================================================================
+#
+# Accounts Payables database backend routines
+#
+#======================================================================
+
+
+package AP;
+
+
+sub post_transaction {
+  my ($self, $myconfig, $form) = @_;
+
+  # connect to database
+  my $dbh = $form->dbconnect_noauto($myconfig);
+  
+  my ($null, $taxrate, $amount);
+  my $exchangerate = 0;
+  
+  # split and store id numbers in link accounts
+  ($form->{AP}{payables}) = split(/--/, $form->{AP});
+  map { ($form->{AP}{"amount_$_"}) = split(/--/, $form->{"AP_amount_$_"}) } (1 .. $form->{rowcount});
+
+  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});
+  }
+  
+  # reverse and parse amounts
+  for my $i (1 .. $form->{rowcount}) {
+    $form->{"amount_$i"} = $form->round_amount($form->parse_amount($myconfig, $form->{"amount_$i"}) * $form->{exchangerate} * -1, 2);
+    $amount += ($form->{"amount_$i"} * -1);
+  }
+
+  # this is for ap
+  $form->{amount} = $amount;
+  
+  # taxincluded doesn't make sense if there is no amount
+  $form->{taxincluded} = 0 if ($form->{amount} == 0);
+
+  for my $item (split / /, $form->{taxaccounts}) {
+    $form->{AP}{"tax_$item"} = $item;
+
+    $amount = $form->round_amount($form->parse_amount($myconfig, $form->{"tax_$item"}), 2);
+    
+    $form->{"tax_$item"} = $form->round_amount($amount * $form->{exchangerate}, 2) * -1;
+    $form->{total_tax} += ($form->{"tax_$item"} * -1);
+  }
+
+  # adjust paidaccounts if there is no date in the last row
+  $form->{paidaccounts}-- unless ($form->{"datepaid_$form->{paidaccounts}"});
+  
+  $form->{invpaid} = 0;
+  # add payments
+  for my $i (1 .. $form->{paidaccounts}) {
+    $form->{"paid_$i"} = $form->round_amount($form->parse_amount($myconfig, $form->{"paid_$i"}), 2);
+    
+    $form->{invpaid} += $form->{"paid_$i"};
+    $form->{datepaid} = $form->{"datepaid_$i"};
+
+  }
+  
+  $form->{invpaid} = $form->round_amount($form->{invpaid} * $form->{exchangerate}, 2);
+  
+  if ($form->{taxincluded} *= 1) {
+    for $i (1 .. $form->{rowcount}) {
+      $tax = $form->{total_tax} * $form->{"amount_$i"} / $form->{amount};
+      $amount = $form->{"amount_$i"} - $tax;
+      $form->{"amount_$i"} = $form->round_amount($amount, 2);
+      $diff += $amount - $form->{"amount_$i"};
+    }
+
+    # deduct taxes from amount
+    $form->{amount} -= $form->{total_tax};
+    # deduct difference from amount_1
+    $form->{amount_1} += $form->round_amount($diff, 2);
+  }
+
+  $form->{netamount} = $form->{amount};
+  
+  # store invoice total, this goes into ap table
+  $form->{invtotal} = $form->{amount} + $form->{total_tax};
+  
+  # amount for total AP
+  $form->{payables} = $form->{invtotal};
+
+  my ($query, $sth);
+
+  # if we have an id delete old records
+  if ($form->{id}) {
+
+    # delete detail records
+    $query = qq|DELETE FROM acc_trans WHERE trans_id = $form->{id}|;
+
+    $dbh->do($query) || $form->dberror($query);
+    
+  } else {
+    my $uid = time;
+    $uid .= $form->{login};
+
+    $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;
+   
+  }
+
+  # escape '
+  $form->{notes} =~ s/'/''/g;
+    
+  $form->{datepaid} = $form->{transdate} unless ($form->{datepaid});
+  my $datepaid = ($form->{invpaid} != 0) ? qq|'$form->{datepaid}'| : 'NULL';
+
+  $query = qq|UPDATE ap SET
+             invnumber = '$form->{invnumber}',
+             transdate = '$form->{transdate}',
+             ordnumber = '$form->{ordnumber}',
+             vendor_id = $form->{vendor_id},
+             taxincluded = '$form->{taxincluded}',
+             amount = $form->{invtotal},
+             duedate = '$form->{duedate}',
+             paid = $form->{invpaid},
+             datepaid = $datepaid,
+             netamount = $form->{netamount},
+             curr = '$form->{currency}',
+             notes = '$form->{notes}'
+             WHERE id = $form->{id}
+            |;
+  $dbh->do($query) || $form->dberror($query);
+
+
+  # update exchangerate
+  if (($form->{currency} ne $form->{defaultcurrency}) && !$exchangerate) {
+    $form->update_exchangerate($dbh, $form->{currency}, $form->{transdate}, 0, $form->{exchangerate});
+  }
+
+  # add individual transactions
+  foreach my $item (keys %{ $form->{AP} }) {
+    if ($form->{$item} != 0) {
+      $project_id = 'NULL';
+      if ($item =~ /amount_/) {
+       if ($form->{"project_id_$'"} && $form->{"projectnumber_$'"}) { 
+         $project_id = $form->{"project_id_$'"};
+       }
+      }
+
+      # insert detail records in acc_trans
+      $query = qq|INSERT INTO acc_trans (trans_id, chart_id, amount, transdate,
+                                         project_id)
+                  VALUES ($form->{id}, (SELECT id FROM chart
+                                       WHERE accno = '$form->{AP}{$item}'),
+                 $form->{$item}, '$form->{transdate}', $project_id)|;
+      $dbh->do($query) || $form->dberror($query);
+    }
+  }
+
+  # if there is no amount but a payment record a payable
+  if ($form->{amount} == 0 && $form->{invtotal} == 0) {
+    $form->{payables} = $form->{invpaid};
+  }
+  # add paid transactions
+  for my $i (1 .. $form->{paidaccounts}) {
+    if ($form->{"paid_$i"} != 0) {
+
+      $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"});
+      }
+      
+      
+      # get paid account
+      ($form->{AP}{"paid_$i"}) = split(/--/, $form->{"AP_paid_$i"});
+      $form->{"datepaid_$i"} = $form->{transdate} unless ($form->{"datepaid_$i"});
+
+      # if there is no amount and invtotal is zero there is no exchangerate
+      if ($form->{amount} == 0 && $form->{invtotal} == 0) {
+       $form->{exchangerate} = $form->{"exchangerate_$i"};
+      }
+      
+      $amount = $form->round_amount($form->{"paid_$i"} * $form->{exchangerate} * -1, 2);
+      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}{payables}'),
+                   $amount, '$form->{"datepaid_$i"}')|;
+       $dbh->do($query) || $form->dberror($query);
+      }
+      $form->{payables} = $amount;
+
+      # add payment
+      $query = qq|INSERT INTO acc_trans (trans_id, chart_id, amount,
+                  transdate, source)
+                  VALUES ($form->{id},
+                        (SELECT id FROM chart
+                         WHERE accno = '$form->{AP}{"paid_$i"}'),
+                 $form->{"paid_$i"}, '$form->{"datepaid_$i"}',
+                 '$form->{"source_$i"}')|;
+      $dbh->do($query) || $form->dberror($query);
+      
+      # add exchange rate difference
+      $amount = $form->round_amount($form->{"paid_$i"} * ($form->{"exchangerate_$i"} - 1), 2);
+      if ($amount != 0) {
+       $query = qq|INSERT INTO acc_trans (trans_id, chart_id, amount,
+                   transdate, fx_transaction, cleared)
+                   VALUES ($form->{id},
+                          (SELECT id FROM chart
+                           WHERE accno = '$form->{AP}{"paid_$i"}'),
+                   $amount, '$form->{"datepaid_$i"}', '1', '0')|;
+
+       $dbh->do($query) || $form->dberror($query);
+      }
+
+      # exchangerate gain/loss
+      $amount = $form->round_amount($form->{"paid_$i"} * ($form->{exchangerate} - $form->{"exchangerate_$i"}), 2);
+
+      if ($amount != 0) {
+       $accno = ($amount > 0) ? $form->{fxgain_accno} : $form->{fxloss_accno};
+       $query = qq|INSERT INTO acc_trans (trans_id, chart_id, amount,
+                   transdate, fx_transaction, cleared)
+                   VALUES ($form->{id}, (SELECT id FROM chart
+                                         WHERE accno = '$accno'),
+                   $amount, '$form->{"datepaid_$i"}', '1', '0')|;
+       $dbh->do($query) || $form->dberror($query);
+      }
+
+      # update exchange rate record
+      if (($form->{currency} ne $form->{defaultcurrency}) && !$exchangerate) {
+       $form->update_exchangerate($dbh, $form->{currency}, $form->{"datepaid_$i"}, 0, $form->{"exchangerate_$i"});
+      }
+    }
+  }
+  
+  my $rc = $dbh->commit;
+  $dbh->disconnect;
+
+  $rc;
+  
+}
+
+
+
+
+sub delete_transaction {
+  my ($self, $myconfig, $form) = @_;
+
+  # connect to database
+  my $dbh = $form->dbconnect_noauto($myconfig);
+
+  # check for other foreign currency transactions
+  $form->delete_exchangerate($dbh) if ($form->{currency} ne $form->{defaultcurrency});
+  
+  my $query = qq|DELETE FROM ap 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 ap_transactions {
+  my ($self, $myconfig, $form) = @_;
+
+  # connect to database
+  my $dbh = $form->dbconnect($myconfig);
+
+  my $incemp = qq|, (SELECT e.name FROM employee e
+                   WHERE a.employee_id = e.id) AS employee
+                | if ($form->{l_employee});
+                   
+  my $query = qq|SELECT a.id, a.invnumber, a.transdate, a.duedate,
+                 a.amount, a.paid, a.ordnumber, v.name, a.invoice,
+                a.netamount, a.datepaid, a.notes
+                
+                $incemp
+                
+                FROM ap a, vendor v
+                WHERE a.vendor_id = v.id|;
+
+  if ($form->{vendor_id}) {
+    $query .= " AND a.vendor_id = $form->{vendor_id}";
+  } else {
+    if ($form->{vendor}) {
+      my $vendor = $form->like(lc $form->{vendor});
+      $query .= " AND lower(v.name) LIKE '$vendor'";
+    }
+  }
+  if ($form->{invnumber}) {
+    my $invnumber = $form->like(lc $form->{invnumber});
+    $query .= " AND lower(a.invnumber) LIKE '$invnumber'";
+  }
+  if ($form->{ordnumber}) {
+    my $ordnumber = $form->like(lc $form->{ordnumber});
+    $query .= " AND lower(a.ordnumber) LIKE '$ordnumber'";
+  }
+  if ($form->{notes}) {
+    my $notes = $form->like(lc $form->{notes});
+    $query .= " AND lower(a.notes) LIKE '$notes'";
+  }
+
+  $query .= " AND a.transdate >= '$form->{transdatefrom}'" if $form->{transdatefrom};
+  $query .= " AND a.transdate <= '$form->{transdateto}'" if $form->{transdateto};
+  if ($form->{open} || $form->{closed}) {
+    unless ($form->{open} && $form->{closed}) {
+      $query .= " AND a.amount <> a.paid" if ($form->{open});
+      $query .= " AND a.amount = a.paid" if ($form->{closed});
+    }
+  }
+
+  my @a = (transdate, invnumber, name);
+  push @a, "employee" if $self->{l_employee};
+  my $sortorder = join ', ', $form->sort_columns(@a);
+  $sortorder = $form->{sort} unless $sortorder;
+
+  $query .= " ORDER by $sortorder";
+
+  my $sth = $dbh->prepare($query);
+  $sth->execute || $form->dberror($query);
+
+  while (my $ap = $sth->fetchrow_hashref(NAME_lc)) {
+    push @{ $form->{AP} }, $ap;
+  }
+  
+  $sth->finish;
+  $dbh->disconnect;
+  
+}
+
+
+1;
+
diff --git a/sql-ledger/SL/AR.pm b/sql-ledger/SL/AR.pm
new file mode 100644 (file)
index 0000000..4ea3d82
--- /dev/null
@@ -0,0 +1,381 @@
+#=====================================================================
+# SQL-Ledger Accounting
+# Copyright (C) 2001
+#
+#  Author: Dieter Simader
+#   Email: dsimader@sql-ledger.org
+#     Web: http://www.sql-ledger.org
+#
+#  Contributors:
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#======================================================================
+#
+# Accounts Receivable module backend routines
+#
+#======================================================================
+
+package AR;
+
+
+sub post_transaction {
+  my ($self, $myconfig, $form) = @_;
+
+  my ($null, $taxrate, $amount, $tax, $diff);
+  my $exchangerate = 0;
+  my $i;
+
+  # split and store id numbers in link accounts
+  map { ($form->{AR}{"amount_$_"}) = split(/--/, $form->{"AR_amount_$_"}) } (1 .. $form->{rowcount});
+  ($form->{AR}{receivables}) = split(/--/, $form->{AR});
+
+  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}); 
+
+  for $i (1 .. $form->{rowcount}) {
+    $form->{"amount_$i"} = $form->round_amount($form->parse_amount($myconfig, $form->{"amount_$i"}) * $form->{exchangerate}, 2);
+    $amount += $form->{"amount_$i"};
+  }
+  
+  # this is for ar
+  $form->{amount} = $amount;
+
+  # taxincluded doesn't make sense if there is no amount
+  $form->{taxincluded} = 0 if ($form->{amount} == 0);
+
+  foreach my $item (split / /, $form->{taxaccounts}) {
+    $form->{AR}{"tax_$item"} = $item;
+
+    $amount = $form->round_amount($form->parse_amount($myconfig, $form->{"tax_$item"}), 2);
+    
+    $form->{"tax_$item"} = $form->round_amount($amount * $form->{exchangerate}, 2);
+    $form->{total_tax} += $form->{"tax_$item"};
+
+  }
+
+  # adjust paidaccounts if there is no date in the last row
+  $form->{paidaccounts}-- unless ($form->{"datepaid_$form->{paidaccounts}"});
+
+  $form->{invpaid} = 0;
+  # add payments
+  for $i (1 .. $form->{paidaccounts}) {
+    $form->{"paid_$i"} = $form->round_amount($form->parse_amount($myconfig, $form->{"paid_$i"}), 2);
+    
+    $form->{invpaid} += $form->{"paid_$i"};
+    $form->{datepaid} = $form->{"datepaid_$i"};
+
+    # reverse payment
+    $form->{"paid_$i"} *= -1;
+
+  }
+  
+  $form->{invpaid} = $form->round_amount($form->{invpaid} * $form->{exchangerate}, 2);
+  if ($form->{taxincluded} *= 1) {
+    for $i (1 .. $form->{rowcount}) {
+      $tax = $form->{total_tax} * $form->{"amount_$i"} / $form->{amount};
+      $amount = $form->{"amount_$i"} - $tax;
+      $form->{"amount_$i"} = $form->round_amount($amount, 2);
+      $diff += $amount - $form->{"amount_$i"};
+    }
+    
+    $form->{amount} -= $form->{total_tax};
+    # deduct difference from amount_1
+    $form->{amount_1} += $form->round_amount($diff, 2);
+  }
+
+  # store invoice total, this goes into ar table
+  $form->{invtotal} = $form->{amount} + $form->{total_tax};
+  
+  # connect to database
+  my $dbh = $form->dbconnect_noauto($myconfig);
+
+  my ($query, $sth);
+  
+  # if we have an id delete old records
+  if ($form->{id}) {
+
+    # delete detail records
+    $query = qq|DELETE FROM acc_trans WHERE trans_id = $form->{id}|;
+    $dbh->do($query) || $form->dberror($query);
+    
+  } else {
+    my $uid = time;
+    $uid .= $form->{login};
+
+    $query = qq|INSERT INTO ar (invnumber, employee_id)
+                VALUES ('$uid', (SELECT id FROM employee
+                                WHERE login = '$form->{login}') )|;
+    $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;
+
+  }
+
+  # escape '
+  $form->{notes} =~ s/'/''/g;
+
+  # record last payment date in ar table
+  $form->{datepaid} = $form->{transdate} unless $form->{datepaid};
+  my $datepaid = ($form->{invpaid} != 0) ? qq|'$form->{datepaid}'| : 'NULL';
+  
+  $query = qq|UPDATE ar set
+             invnumber = '$form->{invnumber}',
+             ordnumber = '$form->{ordnumber}',
+             transdate = '$form->{transdate}',
+             customer_id = $form->{customer_id},
+             taxincluded = '$form->{taxincluded}',
+             amount = $form->{invtotal},
+             duedate = '$form->{duedate}',
+             paid = $form->{invpaid},
+             datepaid = $datepaid,
+             netamount = $form->{amount},
+             curr = '$form->{currency}',
+             notes = '$form->{notes}'
+             WHERE id = $form->{id}|;
+  $dbh->do($query) || $form->dberror($query);
+
+  
+  # amount for AR account
+  $form->{receivables} = $form->round_amount($form->{invtotal} * -1, 2);
+  
+
+  # update exchangerate
+  if (($form->{currency} ne $form->{defaultcurrency}) && !$exchangerate) {
+    $form->update_exchangerate($dbh, $form->{currency}, $form->{transdate}, $form->{exchangerate}, 0);
+  }
+  
+  # add individual transactions for AR, amount and taxes
+  foreach my $item (keys %{ $form->{AR} }) {
+    if ($form->{$item} != 0) {
+      $project_id = 'NULL';
+      if ($item =~ /amount_/) {
+       if ($form->{"project_id_$'"} && $form->{"projectnumber_$'"}) {
+         $project_id = $form->{"project_id_$'"};
+       }
+      }
+      
+      # insert detail records in acc_trans
+      $query = qq|INSERT INTO acc_trans (trans_id, chart_id, amount, transdate,
+                                         project_id)
+                 VALUES ($form->{id}, (SELECT id FROM chart
+                                       WHERE accno = '$form->{AR}{$item}'),
+                 $form->{$item}, '$form->{transdate}', $project_id)|;
+      $dbh->do($query) || $form->dberror($query);
+    }
+  }
+
+  # if there is no amount but a payment record a receivables
+  if ($form->{amount} == 0 && $form->{invtotal} == 0) {
+    $form->{receivables} = $form->{invpaid} * -1;
+  }
+  
+  # add paid transactions
+  for my $i (1 .. $form->{paidaccounts}) {
+    if ($form->{"paid_$i"} != 0) {
+      
+       ($form->{AR}{"paid_$i"}) = split(/--/, $form->{"AR_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"}, 'buy');
+       
+       $form->{"exchangerate_$i"} = ($exchangerate) ? $exchangerate : $form->parse_amount($myconfig, $form->{"exchangerate_$i"}); 
+      }
+      
+
+      # if there is no amount and invtotal is zero there is no exchangerate
+      if ($form->{amount} == 0 && $form->{invtotal} == 0) {
+       $form->{exchangerate} = $form->{"exchangerate_$i"};
+      }
+      
+      # receivables amount
+      $amount = $form->round_amount($form->{"paid_$i"} * $form->{exchangerate} * -1, 2);
+      
+      if ($form->{receivables} != 0) {
+       # add receivable
+       $query = qq|INSERT INTO acc_trans (trans_id, chart_id, amount,
+                   transdate)
+                   VALUES ($form->{id},
+                          (SELECT id FROM chart
+                           WHERE accno = '$form->{AR}{receivables}'),
+                   $amount, '$form->{"datepaid_$i"}')|;
+       $dbh->do($query) || $form->dberror($query);
+      }
+      $form->{receivables} = $amount;
+      
+      # add payment
+      $query = qq|INSERT INTO acc_trans (trans_id, chart_id, amount,
+                 transdate, source)
+                 VALUES ($form->{id},
+                        (SELECT id FROM chart
+                         WHERE accno = '$form->{AR}{"paid_$i"}'),
+                 $form->{"paid_$i"}, '$form->{"datepaid_$i"}',
+                 '$form->{"source_$i"}')|;
+      $dbh->do($query) || $form->dberror($query);
+      
+      
+      # exchangerate difference for payment
+      $amount = $form->round_amount($form->{"paid_$i"} * ($form->{"exchangerate_$i"} - 1), 2);
+       
+      if ($amount != 0) {
+       $query = qq|INSERT INTO acc_trans (trans_id, chart_id, amount,
+                   transdate, fx_transaction, cleared)
+                   VALUES ($form->{id},
+                          (SELECT id FROM chart
+                           WHERE accno = '$form->{AR}{"paid_$i"}'),
+                   $amount, '$form->{"datepaid_$i"}', '1', '0')|;
+       $dbh->do($query) || $form->dberror($query);
+      }
+       
+      # exchangerate gain/loss
+      $amount = $form->round_amount($form->{"paid_$i"} * ($form->{exchangerate} - $form->{"exchangerate_$i"}), 2);
+      
+      if ($amount != 0) {
+       $accno = ($amount > 0) ? $form->{fxgain_accno} : $form->{fxloss_accno};
+       $query = qq|INSERT INTO acc_trans (trans_id, chart_id, amount,
+                   transdate, fx_transaction, cleared)
+                   VALUES ($form->{id}, (SELECT id FROM chart
+                                         WHERE accno = '$accno'),
+                   $amount, '$form->{"datepaid_$i"}', '1', '0')|;
+       $dbh->do($query) || $form->dberror($query);
+      }
+      
+      # update exchangerate record
+      if (($form->{currency} ne $form->{defaultcurrency}) && !$exchangerate) {
+       $form->update_exchangerate($dbh, $form->{currency}, $form->{"datepaid_$i"}, $form->{"exchangerate_$i"}, 0);
+      }
+    }
+  }
+
+
+  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);
+
+  # check for other foreign currency transactions
+  $form->delete_exchangerate($dbh) if ($form->{currency} ne $form->{defaultcurrency});
+
+  my $query = qq|DELETE FROM ar 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
+  my $rc = $dbh->commit;
+  $dbh->disconnect;
+  
+  $rc;
+
+}
+
+
+
+sub ar_transactions {
+  my ($self, $myconfig, $form) = @_;
+
+  # connect to database
+  my $dbh = $form->dbconnect($myconfig);
+
+  my $incemp = qq|, (SELECT e.name FROM employee e
+                   WHERE a.employee_id = e.id) AS employee
+                  | if ($form->{l_employee});
+                  
+  my $query = qq|SELECT a.id, a.invnumber, a.ordnumber, a.transdate,
+                 a.duedate, a.netamount, a.amount, a.paid, c.name,
+                a.invoice, a.datepaid, a.terms, a.notes, a.shippingpoint
+                
+                $incemp
+                
+                FROM ar a, customer c
+                WHERE a.customer_id = c.id|;
+  
+  if ($form->{customer_id}) {
+    $query .= " AND a.customer_id = $form->{customer_id}";
+  } else {
+    if ($form->{customer}) {
+      my $customer = $form->like(lc $form->{customer});
+      $query .= " AND lower(c.name) LIKE '$customer'";
+    }
+  }
+  if ($form->{invnumber}) {
+    my $invnumber = $form->like(lc $form->{invnumber});
+    $query .= " AND lower(a.invnumber) LIKE '$invnumber'";
+  }
+  if ($form->{ordnumber}) {
+    my $ordnumber = $form->like(lc $form->{ordnumber});
+    $query .= " AND lower(a.ordnumber) LIKE '$ordnumber'";
+  }
+  if ($form->{notes}) {
+    my $notes = $form->like(lc $form->{notes});
+    $query .= " AND lower(a.notes) LIKE '$notes'";
+  }
+  
+  $query .= " AND a.transdate >= '$form->{transdatefrom}'" if $form->{transdatefrom};
+  $query .= " AND a.transdate <= '$form->{transdateto}'" if $form->{transdateto};
+  if ($form->{open} || $form->{closed}) {
+    unless ($form->{open} && $form->{closed}) {
+    $query .= " AND a.amount <> a.paid" if ($form->{open});
+    $query .= " AND a.amount = a.paid" if ($form->{closed});
+    }
+  }
+
+  my @a = (transdate, invnumber, name);
+  push @a, "employee" if $form->{l_employee};
+  my $sortorder = join ', ', $form->sort_columns(@a);
+  $sortorder = $form->{sort} unless $sortorder;
+  
+  $query .= " ORDER by $sortorder";
+  
+  my $sth = $dbh->prepare($query);
+  $sth->execute || $form->dberror($query);
+
+  while (my $ar = $sth->fetchrow_hashref(NAME_lc)) {
+    push @{ $form->{AR} }, $ar;
+  }
+  
+  $sth->finish;
+  $dbh->disconnect;
+
+}
+
+
+1;
+
diff --git a/sql-ledger/SL/CA.pm b/sql-ledger/SL/CA.pm
new file mode 100644 (file)
index 0000000..b71749d
--- /dev/null
@@ -0,0 +1,262 @@
+#=====================================================================
+# SQL-Ledger Accounting
+# Copyright (C) 2001
+#
+#  Author: Dieter Simader
+#   Email: dsimader@sql-ledger.org
+#     Web: http://www.sql-ledger.org
+#
+#  Contributors:
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#======================================================================
+# chart of accounts
+#
+# CHANGE LOG:
+#   DS. 2000-07-04  Created
+#
+#======================================================================
+
+
+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 $where = '1 = 1';
+  # build WHERE clause from dates if any
+  if ($form->{fromdate}) {
+    $where .= " AND ac.transdate >= '$form->{fromdate}'";
+  }
+  if ($form->{todate}) {
+    $where .= " AND ac.transdate <= '$form->{todate}'";
+  }
+
+  my $sortorder = join ', ', $form->sort_columns(qw(transdate reference description));
+  my $false = ($myconfig->{dbdriver} eq 'Pg') ? FALSE : q|'0'|;
+  
+  # Oracle workaround, use ordinal positions
+  my %ordinal = ( transdate => 4,
+                 reference => 2,
+                 description => 3 );
+  map { $sortorder =~ s/$_/$ordinal{$_}/ } keys %ordinal;
+
+   
+  if ($form->{accno}) {
+    # get category for account
+    $query = qq|SELECT category
+                FROM chart
+               WHERE accno = '$form->{accno}'|;
+    $sth = $dbh->prepare($query);
+
+    $sth->execute || $form->dberror($query);
+    ($form->{category}) = $sth->fetchrow_array;
+    $sth->finish;
+    
+    if ($form->{fromdate}) {
+      # get beginning balance
+      $query = qq|SELECT SUM(ac.amount)
+                 FROM acc_trans ac, chart c
+                 WHERE ac.chart_id = c.id
+                 AND c.accno = '$form->{accno}'
+                 AND ac.transdate < date '$form->{fromdate}'
+                 |;
+      $sth = $dbh->prepare($query);
+
+      $sth->execute || $form->dberror($query);
+      ($form->{balance}) = $sth->fetchrow_array;
+      $sth->finish;
+    }
+  }
+  
+  if ($form->{accounttype} eq 'gifi' && $form->{gifi_accno}) {
+    # get category for account
+    $query = qq|SELECT category
+                FROM chart
+               WHERE gifi_accno = '$form->{gifi_accno}'|;
+    $sth = $dbh->prepare($query);
+
+    $sth->execute || $form->dberror($query);
+    ($form->{category}) = $sth->fetchrow_array;
+    $sth->finish;
+    if ($form->{fromdate}) {
+      # get beginning balance
+      $query = qq|SELECT SUM(ac.amount)
+                 FROM acc_trans ac, chart c
+                 WHERE ac.chart_id = c.id
+                 AND c.gifi_accno = '$form->{gifi_accno}'
+                 AND ac.transdate < date '$form->{fromdate}'
+                 |;
+      $sth = $dbh->prepare($query);
+
+      $sth->execute || $form->dberror($query);
+      ($form->{balance}) = $sth->fetchrow_array;
+      $sth->finish;
+    }
+  }
+  $query = "";
+  
+  foreach my $id (@id) {
+    
+    # get all transactions
+    $query .= qq|
+      SELECT g.id, g.reference, g.description, ac.transdate,
+               $false AS invoice,
+               ac.amount, 'gl' as charttype
+               FROM gl g, acc_trans ac
+               WHERE $where
+               AND ac.chart_id = $id
+               AND ac.trans_id = g.id
+      UNION ALL
+      SELECT a.id, a.invnumber, c.name, ac.transdate,
+               a.invoice,
+               ac.amount, 'ar' as charttype
+               FROM ar a, acc_trans ac, customer c
+               WHERE $where
+               AND ac.chart_id = $id
+               AND ac.trans_id = a.id
+               AND a.customer_id = c.id
+      UNION ALL
+      SELECT a.id, a.invnumber, v.name, ac.transdate,
+               a.invoice,
+               ac.amount, 'ap' as charttype
+               FROM ap a, acc_trans ac, vendor v
+               WHERE $where
+               AND ac.chart_id = $id
+               AND ac.trans_id = a.id
+               AND a.vendor_id = v.id
+      UNION ALL|;
+  }
+
+  $query =~ s/UNION ALL$//;
+  $query .= qq|
+      ORDER BY $sortorder|;
+
+  $sth = $dbh->prepare($query);
+  $sth->execute || $form->dberror($query);
+
+  while (my $ca = $sth->fetchrow_hashref(NAME_lc)) {
+
+    # gl
+    if ($ca->{charttype} eq "gl") {
+      $ca->{module} = "gl";
+    }
+
+    # ap
+    if ($ca->{charttype} eq "ap") {
+      $ca->{module} = ($ca->{invoice}) ? 'ir' : 'ap';
+    }
+
+    # ar
+    if ($ca->{charttype} eq "ar") {
+      $ca->{module} = ($ca->{invoice}) ? 'is' : 'ar';
+    }
+
+    if ($ca->{amount} < 0) {
+      $ca->{debit} = $ca->{amount} * -1;
+      $ca->{credit} = 0;
+    } else {
+      $ca->{credit} = $ca->{amount};
+      $ca->{debit} = 0;
+    }
+
+    push @{ $form->{CA} }, $ca;
+
+  }
+  $sth->finish;
+  $dbh->disconnect;
+
+}
+
+1;
+
diff --git a/sql-ledger/SL/CP.pm b/sql-ledger/SL/CP.pm
new file mode 100644 (file)
index 0000000..f84bd15
--- /dev/null
@@ -0,0 +1,308 @@
+#=====================================================================
+# SQL-Ledger Accounting
+# Copyright (C) 2002
+#
+#  Author: Dieter Simader
+#   Email: dsimader@sql-ledger.org
+#     Web: http://www.sql-ledger.org
+#
+#  Contributors:
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#======================================================================
+#
+# 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 SL::Num2text;
+    }
+  } else {
+    use SL::Num2text;
+  }
+
+  bless $self, $type;
+
+}
+
+
+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 '%$form->{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;
+  
+  # get currencies and closedto
+  $query = qq|SELECT curr, closedto
+              FROM defaults|;
+  $sth = $dbh->prepare($query);
+  $sth->execute || $form->dberror($query);
+  
+  ($form->{currencies}, $form->{closedto}) = $sth->fetchrow_array;
+  $sth->finish;
+
+  $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 $sth = $dbh->prepare($query);
+  $sth->execute || $form->dberror($query);
+  my ($count) = $sth->fetchrow_array;
+  $sth->finish;
+
+  # build selection list
+  if ($count < $myconfig->{vclimit}) {
+    $query = qq|SELECT DISTINCT ct.id, ct.name
+                FROM $form->{vc} ct, $arap a
+               WHERE a.$form->{vc}_id = ct.id
+               AND a.amount != a.paid
+               ORDER BY name|;
+    $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;
+
+  }
+
+  $dbh->disconnect;
+
+}
+
+
+sub get_openinvoices {
+  my ($self, $myconfig, $form) = @_;
+
+  return unless $form->{"$form->{vc}_id"};
+
+  # connect to database
+  my $dbh = $form->dbconnect($myconfig);
+
+  my $where = qq|WHERE $form->{vc}_id = $form->{"$form->{vc}_id"}
+                 AND curr = '$form->{currency}'
+                AND NOT amount = paid|;
+  
+  if ($form->{transdatefrom}) {
+    $where .= " AND transdate >= '$form->{transdatefrom}'";
+  }
+  if ($form->{transdateto}) {
+    $where .= " AND transdate <= '$form->{transdateto}'";
+  }
+  
+  my ($arap, $buysell);
+  if ($form->{vc} eq 'customer') {
+    $arap = "ar";
+    $buysell = "buy";
+  } else {
+    $arap = "ap";
+    $buysell = "sell";
+  }
+  
+  my $query = qq|SELECT id, invnumber, transdate, amount, paid, curr
+                FROM $arap
+                $where
+                ORDER BY id|;
+  my $sth = $dbh->prepare($query);
+  $sth->execute || $form->dberror($query);
+
+  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});
+    push @{ $form->{PR} }, $ref;
+  }
+  
+  $sth->finish;
+  $dbh->disconnect;
+
+}
+
+
+
+sub process_payment {
+  my ($self, $myconfig, $form) = @_;
+    
+  # connect to database, turn AutoCommit off
+  my $dbh = $form->dbconnect_noauto($myconfig);
+
+  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 $sth = $dbh->prepare($query);
+  $sth->execute || $form->dberror($query);
+
+  my ($fxgain_accno_id, $fxloss_accno_id) = $sth->fetchrow_array;
+  $sth->finish;
+
+  my ($ARAP, $arap, $buysell);
+  
+  if ($form->{vc} eq 'customer') {
+    $ARAP = "AR";
+    $arap = "ar";
+    $buysell = "buy";
+  } else {
+    $ARAP = "AP";
+    $arap = "ap";
+    $buysell = "sell";
+  }
+  
+  # go through line by line
+  for my $i (1 .. $form->{rowcount}) {
+
+    if ($form->{"paid_$i"}) {
+
+      $form->{"paid_$i"} = $form->parse_amount($myconfig, $form->{"paid_$i"});
+      
+      # get exchangerate for original 
+      $query = qq|SELECT $buysell FROM exchangerate e, $arap a
+                 WHERE e.curr = '$form->{currency}'
+                 AND a.transdate = e.transdate
+                 AND a.id = $form->{"id_$i"}|;
+      $sth = $dbh->prepare($query);
+      $sth->execute || $form->dberror($query);
+
+      my ($exchangerate) = $sth->fetchrow_array;
+      $sth->finish;
+
+      $exchangerate = 1 unless $exchangerate;
+
+      $query = qq|SELECT c.id FROM chart c, acc_trans a
+                  WHERE a.chart_id = c.id
+                 AND c.link = '$ARAP'
+                 AND a.trans_id = $form->{"id_$i"}|;
+      $sth = $dbh->prepare($query);
+      $sth->execute || $form->dberror($query);
+
+      my ($id) = $sth->fetchrow_array;
+      $sth->finish;
+
+      my $amount = $form->round_amount($form->{"paid_$i"} * $exchangerate * -1, 2);
+      $ml = ($ARAP eq 'AR') ? -1 : 1;
+      # 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)
+                  VALUES ($form->{"id_$i"},
+                        (SELECT id FROM chart
+                         WHERE accno = '$paymentaccno'),
+                 '$form->{datepaid}', $form->{"paid_$i"} * $ml,
+                 '$form->{source}')|;
+      $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 != 0) {
+        # exchangerate difference
+       $query = qq|INSERT INTO acc_trans (trans_id, chart_id, transdate,
+                   amount, cleared, fx_transaction)
+                   VALUES ($form->{"id_$i"},
+                          (SELECT id FROM chart
+                           WHERE accno = '$paymentaccno'),
+                 '$form->{datepaid}', $amount * $ml, '0', '1')|;
+       $dbh->do($query) || $form->dberror($query);
+
+        # gain/loss
+        
+       $amount = $form->round_amount($form->{"paid_$i"} * ($exchangerate - $form->{exchangerate}) * $ml, 2);
+       if ($amount != 0) {
+         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);
+
+      # update AR/AP transaction
+      $query = qq|UPDATE $arap set
+                 paid = paid + $form->{"paid_$i"},
+                 datepaid = '$form->{datepaid}'
+                 WHERE id = $form->{"id_$i"}|;
+      $dbh->do($query) || $form->dberror($query);
+    }
+  }
+  
+  my $rc = $dbh->commit;
+  $dbh->disconnect;
+
+  $rc;
+
+}
+
+
+1;
+
diff --git a/sql-ledger/SL/CT.pm b/sql-ledger/SL/CT.pm
new file mode 100644 (file)
index 0000000..7c42cb8
--- /dev/null
@@ -0,0 +1,447 @@
+#=====================================================================
+# SQL-Ledger Accounting
+# Copyright (C) 2001
+#
+#  Author: Dieter Simader
+#   Email: dsimader@sql-ledger.org
+#     Web: http://www.sql-ledger.org
+#
+#  Contributors:
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#======================================================================
+#
+# backend code for customers and vendors
+#
+# CHANGE LOG:
+#   DS. 2000-07-04  Created
+#
+#======================================================================
+
+package CT;
+
+
+sub get_tuple {
+  my ($self, $myconfig, $form) = @_;
+
+  my $dbh = $form->dbconnect($myconfig);
+  my $query = qq|SELECT *
+                 FROM $form->{db}
+                 WHERE id = $form->{id}|;
+  my $sth = $dbh->prepare($query);
+  $sth->execute || $form->dberror($query);
+  
+  my $ref = $sth->fetchrow_hashref(NAME_lc);
+  
+  map { $form->{$_} = $ref->{$_} } keys %$ref;
+
+  $sth->finish;
+
+
+  # get ship to
+  $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);
+
+  map { $form->{$_} = $ref->{$_} } keys %$ref;
+
+  $sth->finish;
+
+
+  # get tax labels
+  $query = qq|SELECT accno, description
+              FROM chart, tax
+             WHERE link LIKE '%CT_tax%'
+             AND chart.id = tax.chart_id
+             ORDER BY 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 taxes for customer/vendor
+  $query = qq|SELECT chart_id, accno
+              FROM $form->{db}tax, chart
+              WHERE chart_id = chart.id
+              AND $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;
+
+  
+  $dbh->disconnect;
+
+}
+
+
+sub taxaccounts {
+  my ($self, $myconfig, $form) = @_;
+
+  my $dbh = $form->dbconnect($myconfig);
+
+  # get tax labels
+  my $query = qq|SELECT accno, description
+                 FROM chart, tax
+                WHERE link LIKE '%CT_tax%'
+                AND chart.id = tax.chart_id
+                ORDER BY accno|;
+  $sth = $dbh->prepare($query);
+  $sth->execute || $form->dberror($query);
+
+  while (my $taxref = $sth->fetchrow_hashref(NAME_lc)) {
+    $form->{taxaccounts} .= "$taxref->{accno} ";
+    $form->{tax}{$taxref->{accno}}{description} = $taxref->{description};
+  }
+  $sth->finish;
+  chop $form->{taxaccounts};
+
+  $dbh->disconnect;
+
+}
+
+
+sub delete_customer {
+  my ($self, $myconfig, $form) = @_;
+
+  # connect to database, turn AutoCommit off
+  my $dbh = $form->dbconnect_noauto($myconfig);
+
+  my $query = qq|SELECT id FROM ar
+                 WHERE customer_id = $form->{id}
+                UNION
+                SELECT id FROM oe
+                WHERE customer_id = $form->{id}|;
+  my $sth = $dbh->prepare($query) || $form->dberror($query);
+  $sth->execute;
+
+  my ($rc) = $sth->fetchrow_array;
+  $sth->finish;
+
+  if ($rc) {
+    $dbh->disconnect;
+    $rc = -1;
+  } else {
+    
+    # delete customer
+    $query = qq|DELETE FROM customer
+                WHERE 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|DELETE FROM customertax
+                WHERE customer_id = $form->{id}|;
+    $dbh->do($query) || $form->dberror($query);
+
+    # commit and redirect
+    $rc = $dbh->commit;
+    $dbh->disconnect;
+    
+  }
+
+  $rc;
+  
+}
+
+
+sub save_customer {
+  my ($self, $myconfig, $form) = @_;
+
+  # connect to database
+  my $dbh = $form->dbconnect($myconfig);
+
+  # escape '
+  map { $form->{$_} =~ s/'/''/g } qw(customernumber name addr1 addr2 addr3 addr4 contact notes);
+
+  # assign value discount, terms, creditlimit
+  $form->{discount} /= 100;
+  $form->{terms} *= 1;
+  $form->{taxincluded} *= 1;
+  $form->{creditlimit} = $form->parse_amount($myconfig, $form->{creditlimit});
+  
+  my ($query, $sth);
+
+  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);
+  } else {
+    my $uid = time;
+    $uid .= $form->{login};
+
+    $query = qq|INSERT INTO customer (name)
+                VALUES ('$uid')|;
+    $dbh->do($query) || $form->dberror($query);
+    
+    $query = qq|SELECT id FROM customer
+                WHERE name = '$uid'|;
+    $sth = $dbh->prepare($query);
+    $sth->execute || $form->dberror($query);
+
+    ($form->{id}) = $sth->fetchrow_array;
+    $sth->finish;
+
+  }
+               
+  $query = qq|UPDATE customer SET
+              customernumber = '$form->{customernumber}',
+             name = '$form->{name}',
+             addr1 = '$form->{addr1}',
+             addr2 = '$form->{addr2}',
+             addr3 = '$form->{addr3}',
+             addr4 = '$form->{addr4}',
+             contact = '$form->{contact}',
+             phone = '$form->{phone}',
+             fax = '$form->{fax}',
+             email = '$form->{email}',
+             cc = '$form->{cc}',
+             bcc = '$form->{bcc}',
+             notes = '$form->{notes}',
+             discount = $form->{discount},
+             creditlimit = $form->{creditlimit},
+             terms = $form->{terms},
+             taxincluded = '$form->{taxincluded}'
+             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->disconnect;
+
+}
+
+
+sub save_vendor {
+  my ($self, $myconfig, $form) = @_;
+
+  # connect to database
+  my $dbh = $form->dbconnect($myconfig);
+
+  # escape '
+  map { $form->{$_} =~ s/'/''/g } qw(vendornumber name addr1 addr2 addr3 addr4 contact notes);
+
+  $form->{terms} *= 1;
+  $form->{taxincluded} *= 1;
+  
+  my $query;
+  
+  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);
+  } else {
+    my $uid = time;
+    $uid .= $form->{login};
+    
+    $query = qq|INSERT INTO vendor (name)
+                VALUES ('$uid')|;
+    $dbh->do($query) || $form->dberror($query);
+   
+    $query = qq|SELECT id FROM vendor
+                WHERE name = '$uid'|;
+    $sth = $dbh->prepare($query);
+    $sth->execute || $form->dberror($query);
+
+    ($form->{id}) = $sth->fetchrow_array;
+    $sth->finish;
+
+  }
+   
+
+  $query = qq|UPDATE vendor SET
+              vendornumber = '$form->{vendornumber}',
+             name = '$form->{name}',
+             addr1 = '$form->{addr1}',
+             addr2 = '$form->{addr2}',
+             addr3 = '$form->{addr3}',
+             addr4 = '$form->{addr4}',
+             contact = '$form->{contact}',
+             phone = '$form->{phone}',
+             fax = '$form->{fax}',
+             email = '$form->{email}',
+             cc = '$form->{cc}',
+             bcc = '$form->{bcc}',
+             notes = '$form->{notes}',
+             terms = $form->{terms},
+             taxincluded = '$form->{taxincluded}'
+             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->disconnect;
+
+}
+
+
+
+sub delete_vendor {
+  my ($self, $myconfig, $form) = @_;
+
+  # connect to database, turn AutoCommit off
+  my $dbh = $form->dbconnect_noauto($myconfig);
+
+  # check if there are any transactions on file
+  my $query = qq|SELECT id FROM ap
+                 WHERE vendor_id = $form->{id}
+                UNION
+                SELECT id FROM oe
+                WHERE vendor_id = $form->{id}|;
+  my $sth = $dbh->prepare($query) || $form->dberror($query);
+  $sth->execute;
+  
+  my ($rc) = $sth->fetchrow_array;
+  $sth->finish;
+  
+  if ($rc) {
+    $dbh->disconnect;
+    $rc = -1;
+  } else {
+    
+    # delete vendor
+    $query = qq|DELETE FROM vendor
+                WHERE 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|DELETE FROM vendortax
+                WHERE vendor_id = $form->{id}|;
+    $dbh->do($query) || $form->dberror($query);
+
+    # commit and redirect
+    $rc = $dbh->commit;
+    $dbh->disconnect;
+
+  }
+
+  $rc;
+
+}
+
+
+sub search {
+  my ($self, $myconfig, $form) = @_;
+
+  # connect to database
+  my $dbh = $form->dbconnect($myconfig);
+
+  my $where = "1 = 1";
+  $form->{sort} = "name" unless ($form->{sort});
+  
+  if ($form->{"$form->{db}number"}) {
+    my $companynumber = $form->like(lc $form->{"$form->{db}number"});
+    $where .= " AND lower($form->{db}number) LIKE '$companynumber'";
+  }
+  if ($form->{name}) {
+    my $name = $form->like(lc $form->{name});
+    $where .= " AND lower(name) LIKE '$name'";
+  }
+  if ($form->{contact}) {
+    my $contact = $form->like(lc $form->{contact});
+    $where .= " AND lower(contact) LIKE '$contact'";
+  }
+  if ($form->{email}) {
+    my $email = $form->like(lc $form->{email});
+    $where .= " AND lower(email) LIKE '$email'";
+  }
+
+  if ($form->{status} eq 'orphaned') {
+    $where .= qq| AND id NOT IN (SELECT o.$form->{db}_id
+                                 FROM oe o, $form->{db} ct
+                                WHERE ct.id = o.$form->{db}_id)|;
+    if ($form->{db} eq 'customer') {
+      $where .= qq| AND id NOT IN (SELECT a.customer_id
+                                   FROM ar a, customer ct
+                                  WHERE ct.id = a.customer_id)|;
+    }
+    if ($form->{db} eq 'vendor') {
+      $where .= qq| AND id NOT IN (SELECT a.vendor_id
+                                   FROM ap a, vendor ct
+                                  WHERE ct.id = a.vendor_id)|;
+    }
+  }
+  
+  my $query = qq~SELECT id, name, $form->{db}number, 
+                 addr1 || ' ' || addr2 || ' ' || addr3 || ' ' || addr4 AS address,
+                 contact, phone, fax, email, cc, terms
+                 FROM $form->{db}
+                 WHERE $where
+                ORDER BY $form->{sort}~;
+
+  my $sth = $dbh->prepare($query);
+  $sth->execute || $form->dberror($query);
+
+
+  while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
+    push @{ $form->{CT} }, $ref;
+  }
+
+  $sth->finish;
+  $dbh->disconnect;
+
+}
+
+
+1;
+
diff --git a/sql-ledger/SL/Form.pm b/sql-ledger/SL/Form.pm
new file mode 100644 (file)
index 0000000..ef5f2ca
--- /dev/null
@@ -0,0 +1,1397 @@
+#=====================================================================
+# SQL-Ledger Accounting
+# Copyright (C) 1998-2003
+#
+#  Author: Dieter Simader
+#   Email: dsimader@sql-ledger.org
+#     Web: http://www.sql-ledger.org
+#
+# Contributors: Thomas Bayen <bayen@gmx.de>
+#               Antti Kaihola <akaihola@siba.fi>
+#               Moritz Bunkus (tex code)
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#======================================================================
+# Utilities for parsing forms
+# and supporting routines for linking account numbers
+# used in AR, AP and IS, IR modules
+#
+#======================================================================
+
+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];
+  }
+
+  foreach $item (split(/&/)) {
+    ($key, $value) = split(/=/, $item);
+    $self->{$key} = &unescape("",$value);
+  }
+
+  $self->{action} = lc $self->{action};
+  $self->{action} =~ s/( |-|,)/_/g;
+
+  $self->{version} = "2.0.8";
+  $self->{dbversion} = "2.0.8";
+
+  bless $self, $type;
+  
+}
+
+
+sub debug {
+  my ($self) = @_;
+  
+  print "\n";
+  
+  map { print "$_ = $self->{$_}\n" } (sort keys %{$self});
+  
+} 
+
+  
+sub escape {
+  my ($self, $str, $beenthere) = @_;
+
+  # for Apache 2 we escape strings twice
+  if (($ENV{SERVER_SOFTWARE} =~ /Apache\/2/) && !$beenthere) {
+    $str = $self->escape($str, 1);
+  }
+           
+  $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;
+
+}
+
+
+sub error {
+  my ($self, $msg) = @_;
+
+  if ($ENV{HTTP_USER_AGENT}) {
+    $msg =~ s/\n/<br>/g;
+
+    print qq|Content-Type: text/html
+
+    <body bgcolor=ffffff>
+
+    <h2><font color=red>Error!</font></h2>
+
+    <p><b>$msg</b>
+    
+    </body>
+    </html>
+    |;
+
+    die "Error: $msg\n";
+
+  } 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;
+
+    if (!$self->{header}) {
+      $self->header;
+      print qq|
+      <body>|;
+    }
+
+    print qq|
+
+    <p><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;
+
+  map { $rows += int ((length $_)/$cols) + 1 } (split /\r/, $str);
+
+  $rows = $maxrows if (defined $maxrows && ($rows > $maxrows));
+  
+  $rows;
+
+}
+
+
+sub dberror {
+  my ($self, $msg) = @_;
+
+  $self->error("$msg\n".$DBI::errstr);
+  
+}
+
+
+sub isblank {
+  my ($self, $name, $msg) = @_;
+
+  if ($self->{$name} =~ /^\s*$/) {
+    $self->error($msg);
+  }
+}
+  
+
+sub header {
+  my ($self) = @_;
+
+  my ($nocache, $stylesheet, $charset);
+  
+  # use expire tag to prevent caching
+#  $nocache = qq|<META HTTP-EQUIV="Expires" CONTENT="Tue, 01 Jan 1980 1:00:00 GMT">
+#  <META HTTP-EQUIV="Pragma" CONTENT="no-cache">
+#|;
+
+  if ($self->{stylesheet} && (-f "css/$self->{stylesheet}")) {
+    $stylesheet = qq|<LINK REL="stylesheet" HREF="css/$self->{stylesheet}" TYPE="text/css" TITLE="SQL-Ledger style sheet">
+|;
+  }
+
+  if ($self->{charset}) {
+    $charset = qq|<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=$self->{charset}">
+|;
+  }
+
+  $self->{titlebar} = ($self->{title}) ? "$self->{title} - $self->{titlebar}" : $self->{titlebar};
+  
+  print qq|Content-Type: text/html
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
+<head>
+  <title>$self->{titlebar}</title>
+  $nocache
+  $stylesheet
+  $charset
+</head>
+
+|;
+
+}
+
+
+sub redirect {
+  my ($self, $msg) = @_;
+
+  if ($self->{callback}) {
+
+    ($script, $argv) = split(/\?/, $self->{callback});
+
+    exec ("perl", "$script", $argv);
+   
+  } else {
+    
+    if ($ENV{HTTP_USER_AGENT}) {
+      $msg =~ s/\n/<br>/g;
+
+      print qq|Content-Type: text/html
+
+<body bgcolor=ffffff>
+
+<h2>$msg</h2>
+
+</body>
+</html>
+|;
+
+    } else {
+      print "$msg\n";
+    }
+
+    exit;
+    
+  }
+
+}
+
+
+sub sort_columns {
+  my ($self, @columns) = @_;
+
+  @columns = grep !/^$self->{sort}$/, @columns;
+  splice @columns, 0, 0, $self->{sort};
+
+  @columns;
+  
+}
+
+
+sub format_amount {
+  my ($self, $myconfig, $amount, $places, $dash) = @_;
+
+  if ($places =~ /\d/) {
+    $amount = $self->round_amount($amount, $places);
+  }
+
+  # is the amount negative
+  my $negative = ($amount < 0);
+  
+  if ($amount != 0) {
+    if ($myconfig->{numberformat} && ($myconfig->{numberformat} ne '1000.00')) {
+      my ($whole, $dec) = split /\./, "$amount";
+      $whole =~ s/-//;
+      $amount = join '', reverse split //, $whole;
+      
+      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 ($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/,/\./;
+  }
+
+  $amount =~ s/,//g;
+  
+  return ($amount * 1);
+
+}
+
+
+sub round_amount {
+  my ($self, $amount, $places) = @_;
+
+#  $places = 3 if $places == 2;
+  
+  if (($places * 1) >= 0) {
+    # compensate for perl behaviour, add 1/10^$places+3
+    sprintf("%.${places}f", $amount + (1 / (10 ** ($places + 3))) * (($amount > 0) ? 1 : -1));
+  } else {
+    $places *= -1;
+    sprintf("%.f", $amount / (10 ** $places) + (($amount > 0) ? 0.1 : -0.1)) * (10 ** $places);
+  }
+
+}
+
+
+sub parse_template {
+  my ($self, $myconfig, $userspath) = @_;
+
+  # { Moritz Bunkus
+  # Some variables used for page breaks
+  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;
+  # } Moritz Bunkus
+
+  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;
+  $self->{tmpfile} = "$userspath/${fileid}.$self->{IN}";
+  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 = $_;
+
+
+    # { Moritz Bunkus
+    # 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 .= $_;
+      }
+    }
+    # } Moritz Bunkus
+
+    
+    if (/<%foreach /) {
+      
+      # this one we need for the count
+      chomp $var;
+      $var =~ s/<%foreach (.+?)%>/$1/;
+      while ($_ = shift) {
+       last if (/<%end /);
+
+       # store line in $par
+       $par .= $_;
+      }
+      
+      # display contents of $self->{number}[] array
+      for $i (0 .. $#{ $self->{$var} }) {
+
+        # { Moritz Bunkus
+        # Try to detect whether a manual page break is necessary
+        # but only if there was a <%pagebreak ...%> block before
+       
+        if ($chars_per_line) {
+          my $lines = int(length($self->{"description"}[$i]) / $chars_per_line + 0.95);
+          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;
+          }
+          $current_line += $lines;
+        }
+        $sum += $self->parse_amount($myconfig, $self->{"linetotal"}[$i]);
+        # } Moritz Bunkus
+
+
+       # don't parse par, we need it for each line
+       $_ = $par;
+       s/<%(.+?)%>/$self->{$1}[$i]/mg;
+       print OUT;
+      }
+      next;
+    }
+
+    # if not comes before if!
+    if (/<%if not /) {
+      # check if it is not set and display
+      chop;
+      s/<%if not (.+?)%>/$1/;
+
+      unless ($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 ($self->{$_}) {
+       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/;
+
+      # mangle filename if someone tries to be cute
+      $var =~ s/\///g;
+
+      # prevent the infinite loop!
+      next if ($self->{"$var"});
+
+      open(INC, "$self->{templates}/$var") or $self->error($self->cleanup."$self->{templates}/$var : $!");
+      unshift(@_, <INC>);
+      close(INC);
+
+      $self->{"$var"} = 1;
+
+      next;
+    }
+    
+    s/<%(.+?)%>/$self->{$1}/g;
+    print OUT;
+  }
+
+  close(OUT);
+
+
+  # { Moritz Bunkus
+  # Convert the tex file to postscript
+  if ($self->{format} =~ /(postscript|pdf)/) {
+
+    use Cwd;
+    $self->{cwd} = cwd();
+    chdir("$userspath") or $self->error($self->cleanup."chdir : $!");
+
+    $self->{tmpfile} =~ s/$userspath\///g;
+
+    # DS. added screen and email option in addition to printer
+    # screen
+    if ($self->{format} eq 'postscript') {
+      system("latex --interaction=nonstopmode $self->{tmpfile} > $self->{tmpfile}.err");
+      $self->error($self->cleanup) if ($?);
+      
+      $self->{tmpfile} =~ s/tex$/dvi/;
+
+      system("dvips $self->{tmpfile} -o -q > /dev/null");
+      $self->error($self->cleanup."dvips : $!") if ($?);
+      $self->{tmpfile} =~ s/dvi$/ps/;
+    }
+    if ($self->{format} eq 'pdf') {
+      system("pdflatex --interaction=nonstopmode $self->{tmpfile} > $self->{tmpfile}.err");
+      $self->error($self->cleanup) if ($?);
+      $self->{tmpfile} =~ s/tex$/pdf/;
+    }
+
+  }
+
+  if ($self->{format} =~ /(postscript|pdf)/ || $self->{media} eq 'email') {
+
+    if ($self->{media} eq 'email') {
+      
+      use SL::Mailer;
+
+      my $mail = new Mailer;
+      
+      $self->{email} =~ s/,/>,</g;
+      
+      map { $mail->{$_} = $self->{$_} } qw(cc bcc subject message version format charset);
+      $mail->{to} = qq|"$self->{name}" <$self->{email}>|;
+      $mail->{from} = qq|"$myconfig->{name}" <$myconfig->{email}>|;
+      $mail->{fileid} = "$fileid.";
+
+      # if we send html or plain text inline
+      if (($self->{format} eq 'html') && ($self->{sendmode} eq 'inline')) {
+       $mail->{contenttype} = "text/html";
+
+        $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>";
+       
+       open(IN, $self->{tmpfile}) or $self->error($self->cleanup."$self->{tmpfile} : $!");
+       while (<IN>) {
+         $mail->{message} .= $_;
+       }
+
+       close(IN);
+
+      } else {
+       
+       @{ $mail->{attachments} } = ($self->{tmpfile});
+
+       $myconfig->{signature} =~ s/\\n/\r\n/g;
+       $mail->{message} .= "\r\n--\r\n$myconfig->{signature}";
+
+      }
+      my $err = $mail->send($out);
+      $self->error($self->cleanup."$err") if ($err);
+      
+    } else {
+      
+      $self->{OUT} = $out;
+      open(IN, $self->{tmpfile}) or $self->error($self->cleanup."$self->{tmpfile} : $!");
+
+      $self->{copies} = 1 unless $self->{media} eq 'printer';
+      
+      for my $i (1 .. $self->{copies}) {
+         
+       if ($self->{OUT}) {
+         open(OUT, $self->{OUT}) or $self->error($self->cleanup."$self->{OUT} : $!");
+       } else {
+         open(OUT, ">-") or $self->error($self->cleanup."$!: STDOUT");
+         
+         # launch application
+         print qq|Content-Type: application/$self->{format}; name="$self->{tmpfile}"
+  Content-Disposition: filename="$self->{tmpfile}"
+
+  |;
+       }
+       
+       while (<IN>) {
+         print OUT $_;
+       }
+       close(OUT);
+       seek IN, 0, 0;
+      }
+
+      close(IN);
+    }
+
+    $self->cleanup;
+
+  }
+  # } Moritz Bunkus
+
+}
+
+
+sub cleanup {
+  my $self = shift;
+
+  my @err = ();
+  if (-f "$self->{tmpfile}.err") {
+    open(FH, "$self->{tmpfile}.err");
+    @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 format_string {
+  my ($self, @fields) = @_;
+
+  my $format = $self->{format};
+  if ($self->{format} =~ /(postscript|pdf)/) {
+    $format = 'tex';
+  }
+
+  my %replace = ( 'order' => { 'html' => [ quotemeta('\n'), '\r' ],
+                               'tex'  => [ '&', quotemeta('\n'), '\r',
+                                          '\$', '%', '_', '#', quotemeta('^'),
+                                          '{', '}', '<', '>', '£' ] },
+                  'html' => {
+                quotemeta('\n') => '<br>', '\r' => '<br>'
+                           },
+                  'tex' => {
+               '&' => '\&', '\$' => '\$', '%' => '\%', '_' => '\_',
+               '#' => '\#', quotemeta('^') => '\^\\', '{' => '\{', '}' => '\}',
+               '<' => '$<$', '>' => '$>$',
+               quotemeta('\n') => '\newline ', '\r' => '\newline ',
+               '£' => '\pounds ',
+                            }
+               );
+
+  foreach my $key (@{ $replace{order}{$format} }) {
+    map { $self->{$_} =~ s/$key/$replace{$format}{$key}/g; } @fields;
+  }
+
+}
+
+
+sub datetonum {
+  my ($self, $date, $myconfig) = @_;
+
+  if ($date) {
+    # get separator
+    my $spc = $myconfig->{dateformat};
+    $spc =~ s/\w//g;
+    $spc = substr($spc, 1, 1);
+
+    if ($spc eq '.') {
+      $spc = '\.';
+    }
+    if ($spc eq '/') {
+      $spc = '\/';
+    }
+
+    if ($myconfig->{dateformat} =~ /^yy/) {
+      ($yy, $mm, $dd) = split /$spc/, $date;
+    }
+    if ($myconfig->{dateformat} =~ /^mm/) {
+      ($mm, $dd, $yy) = split /$spc/, $date;
+    }
+    if ($myconfig->{dateformat} =~ /^dd/) {
+      ($dd, $mm, $yy) = split /$spc/, $date;
+    }
+    
+    $dd *= 1;
+    $mm *= 1;
+    $yy = ($yy < 70) ? $yy + 2000 : $yy;
+    $yy = ($yy >= 70 && $yy <= 99) ? $yy + 1900 : $yy;
+
+    $dd = "0$dd" if ($dd < 10);
+    $mm = "0$mm" if ($mm < 10);
+    
+    $date = "$yy$mm$dd";
+  }
+
+  $date;
+  
+}
+
+
+
+# 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}) || $self->dberror($myconfig->{dboptions});
+  }
+
+  $dbh;
+
+}
+
+
+sub update_balance {
+  my ($self, $dbh, $table, $field, $where, $value) = @_;
+
+  # if we have a value, go do it
+  if ($value != 0) {
+    # retrieve balance from table
+    my $query = "SELECT $field FROM $table WHERE $where";
+    my $sth = $dbh->prepare($query);
+
+    $sth->execute || $self->dberror($query);
+    my ($balance) = $sth->fetchrow_array;
+    $sth->finish;
+
+    $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'|;
+  my $sth = $dbh->prepare($query);
+  $sth->execute || $self->dberror($query);
+  
+  my $set;
+  if ($buy != 0 && $sell != 0) {
+    $set = "buy = $buy, sell = $sell";
+  } elsif ($buy != 0) {
+    $set = "buy = $buy";
+  } elsif ($sell != 0) {
+    $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 get_exchangerate {
+  my ($self, $dbh, $curr, $transdate, $fld) = @_;
+  
+  my $query = qq|SELECT $fld FROM exchangerate
+                 WHERE curr = '$curr'
+                AND transdate = '$transdate'|;
+  my $sth = $dbh->prepare($query);
+  $sth->execute || $self->dberror($query);
+
+  my ($exchangerate) = $sth->fetchrow_array;
+  $sth->finish;
+
+  $exchangerate;
+
+}
+
+
+sub delete_exchangerate {
+  my ($self, $dbh) = @_;
+
+  my @transdate = ();
+  my $transdate;
+
+  my $query = qq|SELECT DISTINCT transdate
+                 FROM acc_trans
+                WHERE trans_id = $self->{id}|;
+  my $sth = $dbh->prepare($query);
+  $sth->execute || $self->dberror($query);
+
+  while ($transdate = $sth->fetchrow_array) {
+    push @transdate, $transdate;
+  }
+  $sth->finish;
+
+  $query = qq|SELECT transdate FROM acc_trans
+              WHERE ar.id = trans_id
+             AND ar.curr = '$self->{currency}'
+             AND transdate IN
+                 (SELECT transdate FROM acc_trans
+                 WHERE trans_id = $self->{id})
+              AND trans_id != $self->{id}
+        UNION SELECT transdate FROM acc_trans
+             WHERE ap.id = trans_id
+             AND ap.curr = '$self->{currency}'
+             AND transdate IN
+                 (SELECT transdate FROM acc_trans
+                 WHERE trans_id = $self->{id})
+              AND trans_id != $self->{id}
+        UNION SELECT transdate FROM oe
+               WHERE oe.curr = '$self->{currency}'
+               AND transdate IN
+                   (SELECT transdate FROM acc_trans
+                   WHERE trans_id = $self->{id})|;
+  $sth = $dbh->prepare($query);
+  $sth->execute || $self->dberror($query);
+
+  while ($transdate = $sth->fetchrow_array) {
+    @transdate = grep !/^$transdate$/, @transdate;
+  }
+  $sth->finish;
+
+  foreach $transdate (@transdate) {
+    $query = qq|DELETE FROM exchangerate
+                WHERE curr = '$self->{currency}'
+               AND transdate = '$transdate'|;
+    $dbh->do($query) || $self->dberror($query);
+  }
+  
+}
+
+
+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 $sth = $dbh->prepare($query);
+  $sth->execute || $self->dberror($query);
+
+  my ($exchangerate) = $sth->fetchrow_array;
+  $sth->finish;
+  $dbh->disconnect;
+  
+  $exchangerate;
+  
+}
+
+
+sub add_shipto {
+  my ($self, $dbh, $id) = @_;
+
+  my $shipto;
+  foreach my $item (qw(name addr1 addr2 addr3 addr4 contact phone fax email)) {
+    if ($self->{"shipto$item"}) {
+      $shipto = 1 if ($self->{$item} ne $self->{"shipto$item"});
+    }
+    $self->{"shipto$item"} =~ s/'/''/g;
+  }
+
+  if ($shipto) {
+    my $query = qq|INSERT INTO shipto (trans_id, shiptoname, shiptoaddr1,
+                   shiptoaddr2, shiptoaddr3, shiptoaddr4, shiptocontact,
+                  shiptophone, shiptofax, shiptoemail) VALUES ($id,
+                  '$self->{shiptoname}', '$self->{shiptoaddr1}',
+                  '$self->{shiptoaddr2}', '$self->{shiptoaddr3}',
+                  '$self->{shiptoaddr4}', '$self->{shiptocontact}',
+                  '$self->{shiptophone}', '$self->{shiptofax}',
+                  '$self->{shiptoemail}')|;
+    $dbh->do($query) || $self->dberror($query);
+  }
+
+}
+
+
+sub get_employee {
+  my ($self, $dbh) = @_;
+
+  my $query = qq|SELECT name FROM employee 
+                 WHERE login = '$self->{login}'|; 
+  my $sth = $dbh->prepare($query); 
+  $sth->execute || $self->dberror($query); 
+
+  ($self->{employee}) = $sth->fetchrow_array;
+  $sth->finish; 
+
+}
+
+
+# this sub gets the id and name from $table
+sub get_name {
+  my ($self, $myconfig, $table) = @_;
+
+  # connect to database
+  my $dbh = $self->dbconnect($myconfig);
+  
+  my $name = $self->like(lc $self->{$table});
+  my $query = qq~SELECT id, name,
+                 addr1 || ' ' || addr2 || ' ' || addr3 || ' ' || addr4 AS address
+                 FROM $table
+                WHERE lower(name) LIKE '$name'
+                ORDER BY name~;
+  my $sth = $dbh->prepare($query);
+
+  $sth->execute || $self->dberror($query);
+
+  my $i = 0;
+  while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
+    push(@{ $self->{name_list} }, $ref);
+    $i++;
+  }
+  $sth->finish;
+  $dbh->disconnect;
+
+  $i;
+  
+}
+
+
+# the selection sub is used in the AR, AP, IS, IR and OE module
+#
+sub all_vc {
+  my ($self, $myconfig, $table) = @_;
+  
+  my $dbh = $self->dbconnect($myconfig);
+  
+  my $query = qq|SELECT count(*) FROM $table|;
+  my $sth = $dbh->prepare($query);
+  $sth->execute || $self->dberror($query);
+  my ($count) = $sth->fetchrow_array;
+  $sth->finish;
+  
+  # build selection list
+  if ($count < $myconfig->{vclimit}) {
+    $query = qq|SELECT id, name
+               FROM $table
+               ORDER BY name|;
+    $sth = $dbh->prepare($query);
+    $sth->execute || $self->dberror($query);
+
+    while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
+      push @{ $self->{"all_$table"} }, $ref;
+    }
+    
+    $sth->finish;
+    
+  }
+
+  $dbh->disconnect;
+
+}
+
+
+sub create_links {
+  my ($self, $module, $myconfig, $table) = @_;
+
+  $self->all_vc($myconfig, $table);
+  
+  # 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;
+  
+  if ($self->{id}) {
+    my $arap = ($table eq 'customer') ? 'ar' : 'ap';
+    
+    $query = qq|SELECT a.invnumber, a.transdate,
+                a.${table}_id, a.datepaid, a.duedate, a.ordnumber,
+               a.taxincluded, a.curr AS currency, a.notes, c.name AS $table,
+               a.amount AS oldinvtotal, a.paid AS oldtotalpaid
+               FROM $arap a, $table c
+               WHERE a.${table}_id = c.id
+               AND 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 amounts from individual entries
+    $query = qq|SELECT c.accno, c.description, a.source, a.amount,
+                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 (a.project_id = p.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 = ($table eq 'customer') ? 'buy' : 'sell';
+    # get exchangerate for currency
+    $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,
+                  (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
+               FROM defaults d|;
+    $sth = $dbh->prepare($query);
+    $sth->execute || $self->dberror($query);
+
+    $ref = $sth->fetchrow_hashref(NAME_lc);
+    map { $self->{$_} = $ref->{$_} } keys %$ref;
+    $sth->finish;
+
+  } else {
+   
+    # get date
+    $query = qq|SELECT current_date AS transdate,
+                d.curr AS currencies, d.closedto, d.revtrans,
+                  (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
+               FROM defaults d|;
+    $sth = $dbh->prepare($query);
+    $sth->execute || $self->dberror($query);
+
+    $ref = $sth->fetchrow_hashref(NAME_lc);
+    map { $self->{$_} = $ref->{$_} } keys %$ref;
+    $sth->finish;
+
+    if ($self->{"$self->{vc}_id"}) {
+      # only setup currency
+      ($self->{currency}) = split /:/, $self->{currencies};
+      
+    } else {
+      
+      $self->lastname_used($dbh, $myconfig, $table, $module);
+    
+      my $fld = ($table eq 'customer') ? 'buy' : 'sell';
+      # get exchangerate for currency
+      $self->{exchangerate} = $self->get_exchangerate($dbh, $self->{currency}, $self->{transdate}, $fld);
+   
+    }
+
+  }
+
+  $dbh->disconnect;
+
+}
+
+
+sub lastname_used {
+  my ($self, $dbh, $myconfig, $table, $module) = @_;
+
+  my $arap = ($table eq 'customer') ? "ar" : "ap";
+  $arap = 'oe' if ($self->{type} =~ /_order/);
+
+  my $query = qq|SELECT id FROM $arap
+                 WHERE id IN (SELECT MAX(id) FROM $arap
+                             WHERE ${table}_id > 0)|;
+  my $sth = $dbh->prepare($query);
+  $sth->execute || $self->dberror($query);
+  
+  my ($trans_id) = $sth->fetchrow_array;
+  $sth->finish;
+  
+  $trans_id *= 1;
+  $query = qq|SELECT ct.name, a.curr, a.${table}_id,
+              current_date + ct.terms AS duedate
+             FROM $arap a
+             JOIN $table ct ON (a.${table}_id = ct.id)
+             WHERE a.id = $trans_id|;
+  $sth = $dbh->prepare($query);
+  $sth->execute || $self->dberror($query);
+
+  ($self->{$table}, $self->{currency}, $self->{"${table}_id"}, $self->{duedate}) = $sth->fetchrow_array;
+  $sth->finish;
+
+}
+
+
+
+sub current_date {
+  my ($self, $myconfig, $thisdate, $days) = @_;
+  
+  my $dbh = $self->dbconnect($myconfig);
+  my ($sth, $query);
+
+  $days *= 1;
+  if ($thisdate) {
+    my $dateformat = $myconfig->{dateformat};
+    $dateformat .= "yy" if $myconfig->{dateformat} !~ /^y/;
+    
+    $query = qq|SELECT to_date('$thisdate', '$dateformat') + $days AS thisdate
+                FROM defaults|;
+     $sth = $dbh->prepare($query);
+     $sth->execute || $self->dberror($query);
+  } else {
+    $query = qq|SELECT current_date AS thisdate
+                FROM defaults|;
+     $sth = $dbh->prepare($query);
+     $sth->execute || $self->dberror($query);
+  }
+
+  ($thisdate) = $sth->fetchrow_array;
+  $sth->finish;
+
+  $dbh->disconnect;
+
+  $thisdate;
+
+}
+
+
+sub like {
+  my ($self, $string) = @_;
+  
+  unless ($string =~ /%/) {
+    $string = "%$string%";
+  }
+
+  $string =~ s/'/''/g;
+  $string;
+  
+}
+
+
+sub redo_rows {
+  my ($self, $flds, $new, $count, $numrows) = @_;
+
+  my @ndx = ();
+
+  map { push @ndx, { num => $new->[$_-1]->{runningnumber}, ndx => $_ } } (1 .. $count);
+
+  my $i = 0;
+  # fill rows
+  foreach my $item (sort { $a->{num} <=> $b->{num} } @ndx) {
+    $i++;
+    $j = $item->{ndx} - 1;
+    map { $self->{"${_}_$i"} = $new->[$j]->{$_} } @{$flds};
+  }
+
+  # delete empty rows
+  for $i ($count + 1 .. $numrows) {
+    map { delete $self->{"${_}_$i"} } @{$flds}; 
+  }
+
+}
+
+  
+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;
+  
+  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, 1, 1);
+
+    if ($spc eq '.') {
+      $spc = '\.';
+    }
+    if ($spc eq '/') {
+      $spc = '\/';
+    }
+
+    if ($myconfig->{dateformat} =~ /^yy/) {
+      ($yy, $mm, $dd) = split /$spc/, $date;
+    }
+    if ($myconfig->{dateformat} =~ /^mm/) {
+      ($mm, $dd, $yy) = split /$spc/, $date;
+    }
+    if ($myconfig->{dateformat} =~ /^dd/) {
+      ($dd, $mm, $yy) = split /$spc/, $date;
+    }
+    
+    $dd *= 1;
+    $mm--;
+    $yy = ($yy < 70) ? $yy + 2000 : $yy;
+    $yy = ($yy >= 70 && $yy <= 99) ? $yy + 1900 : $yy;
+
+    if ($myconfig->{dateformat} =~ /^dd/) {
+      $longdate = "$dd. ".&text($self, $self->{$longmonth}[$mm])." $yy";
+    } else {
+      $longdate = &text($self, $self->{$longmonth}[$mm])." $dd, $yy";
+    }
+
+  }
+
+  $longdate;
+
+}
+
+
+1;
+
diff --git a/sql-ledger/SL/GL.pm b/sql-ledger/SL/GL.pm
new file mode 100644 (file)
index 0000000..5bceb07
--- /dev/null
@@ -0,0 +1,462 @@
+#=====================================================================
+# SQL-Ledger Accounting
+# Copyright (C) 2001
+#
+#  Author: Dieter Simader
+#   Email: dsimader@sql-ledger.org
+#     Web: http://www.sql-ledger.org
+#
+#  Contributors:
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#======================================================================
+#
+# General ledger backend code
+#
+# CHANGE LOG:
+#   DS. 2000-07-04  Created
+#   DS. 2001-06-12  Changed relations from accno to chart_id
+#
+#======================================================================
+
+package GL;
+
+
+sub delete_transaction {
+  my ($self, $myconfig, $form) = @_;
+  
+  # connect to database
+  my $dbh = $form->dbconnect_noauto($myconfig);
+
+  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 ($debit, $credit) = (0, 0);
+  my $project_id;
+
+  my $i;
+  # check if debit and credit balances
+  for $i (1 .. $form->{rowcount}) {
+    if ($form->{"debit_$i"} && $form->{"credit_$i"}) {
+      return -1;
+    }
+
+    $form->{"debit_$i"} = $form->parse_amount($myconfig, $form->{"debit_$i"});
+    $form->{"credit_$i"} = $form->parse_amount($myconfig, $form->{"credit_$i"});
+
+    $debit += $form->{"debit_$i"};
+    $credit += $form->{"credit_$i"};
+  }
+
+  $debit = $form->round_amount($debit, 2);
+  $credit = $form->round_amount($credit, 2);
+  
+  if ($debit != $credit) {
+    return -2;
+  }
+
+  if (($debit + $credit) == 0) {
+    return -3;
+  }
+  
+  # connect to database, turn off AutoCommit
+  my $dbh = $form->dbconnect_noauto($myconfig);
+
+  # post the transaction
+  # make up a unique handle and store in reference field
+  # then retrieve the record based on the unique handle to get the id
+  # replace the reference field with the actual variable
+  # add records to acc_trans
+
+  # if there is a $form->{id} replace the old transaction
+  # delete all acc_trans entries and add the new ones
+
+  # escape '
+  map { $form->{$_} =~ s/'/''/g } qw(reference description);
+
+
+  my ($query, $sth);
+  
+  if ($form->{id}) {
+    # delete individual transactions
+    $query = qq|DELETE FROM acc_trans 
+                WHERE trans_id = $form->{id}|;
+    $dbh->do($query) || $form->dberror($query);
+    
+  } else {
+    my $uid = time;
+    $uid .= $form->{login};
+
+    $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'|;
+    $sth = $dbh->prepare($query);
+    $sth->execute || $form->dberror($query);
+
+    ($form->{id}) = $sth->fetchrow_array;
+    $sth->finish;
+
+  }
+  
+  $query = qq|UPDATE gl SET 
+             reference = '$form->{reference}',
+             description = '$form->{description}',
+             notes = '$form->{notes}',
+             transdate = '$form->{transdate}'
+             WHERE id = $form->{id}|;
+          
+  $dbh->do($query) || $form->dberror($query);
+
+  # insert acc_trans transactions
+  for $i (1 .. $form->{rowcount}) {
+    # extract accno
+    ($accno) = split(/--/, $form->{"accno_$i"});
+    my $amount = 0;
+    
+    if ($form->{"credit_$i"} != 0) {
+      $amount = $form->{"credit_$i"};
+    }
+    if ($form->{"debit_$i"} != 0) {
+      $amount = $form->{"debit_$i"} * -1;
+    }
+
+
+    # if there is an amount, add the record
+    if ($amount != 0) {
+      $project_id = ($form->{"project_id_$i"}) ? $form->{"project_id_$i"} : 'NULL'; 
+      $query = qq|INSERT INTO acc_trans (trans_id, chart_id, amount, transdate,
+                  source, project_id)
+                 VALUES
+                 ($form->{id}, (SELECT id
+                                FROM chart
+                                WHERE accno = '$accno'),
+                  $amount, '$form->{transdate}', '$form->{reference}',
+                 $project_id)|;
+    
+      $dbh->do($query) || $form->dberror($query);
+    }
+  }
+
+  # 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 ($glwhere, $arwhere, $apwhere) = ("1 = 1", "1 = 1", "1 = 1");
+  
+  if ($form->{reference}) {
+    my $source = $form->like(lc $form->{reference});
+    $glwhere .= " AND lower(g.reference) LIKE '$source'";
+    $arwhere .= " AND lower(a.invnumber) LIKE '$source'";
+    $apwhere .= " AND lower(a.invnumber) LIKE '$source'";
+  }
+  if ($form->{source}) {
+    my $source = $form->like(lc $form->{source});
+    $glwhere .= " AND lower(ac.source) LIKE '$source'";
+    $arwhere .= " AND lower(ac.source) LIKE '$source'";
+    $apwhere .= " AND lower(ac.source) LIKE '$source'";
+  }
+  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->{description}) {
+    my $description = $form->like(lc $form->{description});
+    $glwhere .= " AND lower(g.description) LIKE '$description'";
+    $arwhere .= " AND lower(ct.name) LIKE '$description'";
+    $apwhere .= " AND lower(ct.name) LIKE '$description'";
+  }
+  if ($form->{notes}) {
+    my $notes = $form->like(lc $form->{notes});
+    $glwhere .= " AND lower(g.notes) LIKE '$notes'";
+    $arwhere .= " AND lower(a.notes) LIKE '$notes'";
+    $apwhere .= " AND lower(a.notes) LIKE '$notes'";
+  }
+  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
+                FROM chart
+               WHERE accno = '$form->{accno}'|;
+    $sth = $dbh->prepare($query); 
+
+    $sth->execute || $form->dberror($query); 
+    ($form->{ml}) = $sth->fetchrow_array; 
+    $sth->finish; 
+    
+    if ($form->{datefrom}) {
+      $query = qq|SELECT SUM(ac.amount)
+                 FROM acc_trans ac, chart c
+                 WHERE ac.chart_id = c.id
+                 AND c.accno = '$form->{accno}'
+                 AND ac.transdate < date '$form->{datefrom}'
+                 |;
+      $sth = $dbh->prepare($query);
+      $sth->execute || $form->dberror($query);
+
+      ($form->{balance}) = $sth->fetchrow_array;
+      $sth->finish;
+    }
+  }
+  
+  if ($form->{gifi_accno}) {
+    # get category for account
+    $query = qq|SELECT category
+                FROM chart
+               WHERE gifi_accno = '$form->{gifi_accno}'|;
+    $sth = $dbh->prepare($query); 
+
+    $sth->execute || $form->dberror($query); 
+    ($form->{ml}) = $sth->fetchrow_array; 
+    $sth->finish; 
+   
+    if ($form->{datefrom}) {
+      $query = qq|SELECT SUM(ac.amount)
+                 FROM acc_trans ac, chart c
+                 WHERE ac.chart_id = c.id
+                 AND c.gifi_accno = '$form->{gifi_accno}'
+                 AND ac.transdate < date '$form->{datefrom}'
+                 |;
+      $sth = $dbh->prepare($query);
+      $sth->execute || $form->dberror($query);
+
+      ($form->{balance}) = $sth->fetchrow_array;
+      $sth->finish;
+    }
+  }
+
+  my $false = ($myconfig->{dbdriver} eq 'Pg') ? FALSE : q|'0'|;
+
+  my $sortorder = join ', ', $form->sort_columns(qw(transdate reference source description accno));
+  my %ordinal = ( transdate => 6,
+                  reference => 4,
+                  source => 7,
+                 description => 5 );
+  map { $sortorder =~ s/$_/$ordinal{$_}/ } keys %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
+                 FROM gl g, acc_trans ac, chart c
+                 WHERE $glwhere
+                AND ac.chart_id = c.id
+                AND g.id = ac.trans_id
+       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
+                FROM ar a, acc_trans ac, chart c, customer ct
+                WHERE $arwhere
+                AND ac.chart_id = c.id
+                AND a.customer_id = ct.id
+                AND a.id = ac.trans_id
+       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
+                FROM ap a, acc_trans ac, chart c, vendor ct
+                WHERE $apwhere
+                AND ac.chart_id = c.id
+                AND a.vendor_id = ct.id
+                AND a.id = ac.trans_id
+                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} = "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;
+
+  if ($form->{accno}) {
+    $query = qq|SELECT description FROM chart WHERE accno = '$form->{accno}'|;
+    $sth = $dbh->prepare($query);
+    $sth->execute || $form->dberror($query);
+
+    ($form->{account_description}) = $sth->fetchrow_array;
+    $sth->finish;
+  }
+  if ($form->{gifi_accno}) {
+    $query = qq|SELECT description FROM gifi WHERE accno = '$form->{gifi_accno}'|;
+    $sth = $dbh->prepare($query);
+    $sth->execute || $form->dberror($query);
+
+    ($form->{gifi_account_description}) = $sth->fetchrow_array;
+    $sth->finish;
+  }
+  $dbh->disconnect;
+
+}
+
+
+sub transaction {
+  my ($self, $myconfig, $form) = @_;
+  
+  my ($query, $sth);
+  
+  # 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 = "SELECT reference, description, notes, transdate
+              FROM gl
+             WHERE id = $form->{id}";
+    $sth = $dbh->prepare($query);
+    $sth->execute || $form->dberror($query);
+
+    ($form->{reference}, $form->{description}, $form->{notes}, $form->{transdate}) = $sth->fetchrow_array;
+    $sth->finish;
+  
+    # retrieve individual rows
+    $query = "SELECT c.accno, a.amount, project_id,
+                (SELECT p.projectnumber FROM project p
+                WHERE a.project_id = p.id) AS projectnumber
+             FROM acc_trans a, chart c
+             WHERE a.chart_id = c.id
+             AND a.trans_id = $form->{id}
+             ORDER BY accno";
+    $sth = $dbh->prepare($query);
+    $sth->execute || $form->dberror($query);
+    
+    while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
+      push @{ $form->{GL} }, $ref;
+    }
+  } 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);
+  $form->{chart} = "";
+  while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
+    push @{ $form->{chart} }, $ref;
+  }
+  $sth->finish;
+  
+  $dbh->disconnect;
+
+}
+
+
+1;
+
diff --git a/sql-ledger/SL/IC.pm b/sql-ledger/SL/IC.pm
new file mode 100644 (file)
index 0000000..f4a2f75
--- /dev/null
@@ -0,0 +1,936 @@
+#=====================================================================
+# SQL-Ledger Accounting
+# Copyright (C) 2001
+#
+#  Author: Dieter Simader
+#   Email: dsimader@sql-ledger.org
+#     Web: http://www.sql-ledger.org
+#
+#  Contributors:
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#======================================================================
+#
+# Inventory Control backend
+#
+#======================================================================
+
+package IC;
+
+
+sub get_part {
+  my ($self, $myconfig, $form) = @_;
+
+  # connect to db
+  my $dbh = $form->dbconnect($myconfig);
+
+  my $query = qq|SELECT p.*,
+                 c1.accno AS inventory_accno,
+                 c2.accno AS income_accno,
+                 c3.accno AS expense_accno,
+                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
+  map { $form->{$_} = $ref->{$_} } ( keys %{ $ref } );
+  
+  $sth->finish;
+  
+  my %oid = ('Pg'      => 'a.oid',
+             'Oracle'  => 'a.rowid'
+           );
+  
+  
+  # part or service item
+  $form->{item} = ($form->{inventory_accno}) ? 'part' : 'service';
+  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, p.unit,
+               pg.partsgroup
+                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
+  $form->{amount}{IC} = $form->{inventory_accno};
+  $form->{amount}{IC_income} = $form->{income_accno};
+  $form->{amount}{IC_sale} = $form->{income_accno};
+  $form->{amount}{IC_expense} = $form->{expense_accno};
+  $form->{amount}{IC_cogs} = $form->{expense_accno};
+  
+
+  if ($form->{item} ne 'service') {
+    # get makes
+    if ($form->{makemodel}) {
+      $query = qq|SELECT name FROM makemodel
+                  WHERE parts_id = $form->{id}|;
+
+      $sth = $dbh->prepare($query);
+      $sth->execute || $form->dberror($query);
+      
+      my $i = 1;
+      while (($form->{"make_$i"}, $form->{"model_$i"}) = split(/:/, $sth->fetchrow_array)) {
+       $i++;
+      }
+      $sth->finish;
+      $form->{makemodel_rows} = $i - 1;
+
+    }
+  }
+
+  # 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}|;
+  $sth = $dbh->prepare($query);
+  $sth->execute || $form->dberror($query);
+
+  ($form->{orphaned}) = $sth->fetchrow_array;
+  $form->{orphaned} = !$form->{orphaned};
+  $sth->finish;
+  
+  $dbh->disconnect;
+  
+}
+
+
+
+sub save {
+  my ($self, $myconfig, $form) = @_;
+
+  ($form->{inventory_accno}) = split(/--/, $form->{IC});
+  ($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
+
+  # escape '
+  map { $form->{$_} =~ s/'/''/g } qw(partnumber description notes unit bin);
+
+  # undo amount formatting
+  map { $form->{$_} = $form->parse_amount($myconfig, $form->{$_}) } qw(rop weight listprice sellprice lastcost stock);
+  
+  # set date to NULL if nothing entered
+  $form->{priceupdate} = ($form->{priceupdate}) ? qq|'$form->{priceupdate}'| : "NULL";
+  
+  $form->{makemodel} = (($form->{make_1}) || ($form->{model_1})) ? 1 : 0;
+
+  $form->{alternate} = 0;
+  $form->{assembly} = ($form->{item} eq 'assembly') ? 1 : 0;
+  $form->{obsolete} *= 1;
+  $form->{onhand} *= 1;
+
+  my ($query, $sth);
+  
+  if ($form->{id}) {
+
+    # get old price
+    $query = qq|SELECT sellprice, weight
+                FROM parts
+               WHERE id = $form->{id}|;
+    $sth = $dbh->prepare($query);
+    $sth->execute || $form->dberror($query);
+
+    my ($sellprice, $weight) = $sth->fetchrow_array;
+    $sth->finish;
+
+    # if item is part of an assembly adjust all assemblies
+    $query = qq|SELECT id, qty
+                FROM assembly
+               WHERE parts_id = $form->{id}|;
+    $sth = $dbh->prepare($query);
+    $sth->execute || $form->dberror($query);
+
+    while (my ($id, $qty) = $sth->fetchrow_array) {
+      &update_assembly($dbh, $form, $id, $qty * 1, $sellprice * 1, $weight * 1);
+    }
+    $sth->finish;
+
+
+    if ($form->{item} ne 'service') {
+      # 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} != 0) {
+       &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 {
+        # update BOM only
+       $query = qq|UPDATE assembly
+                   SET bom = ?
+                   WHERE id = ?
+                   AND parts_id = ?|;
+       $sth = $dbh->prepare($query);
+       
+       for $i (1 .. $form->{assembly_rows} - 1) {
+          $sth->execute(($form->{"bom_$i"}) ? '1' : '0', $form->{id}, $form->{"id_$i"}) || $form->dberror($query);
+       }
+       $sth->finish;
+      }
+      
+      $form->{onhand} += $form->{stock};
+    }
+    
+    # delete tax records
+    $query = qq|DELETE FROM partstax
+               WHERE parts_id = $form->{id}|;
+    $dbh->do($query) || $form->dberror($query);
+
+  } else {
+    my $uid = time;
+    $uid .= $form->{login};
+
+    $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 = 0;
+  if ($form->{partsgroup}) {
+    my $partsgroup = lc $form->{partsgroup};
+    $query = qq|SELECT DISTINCT id FROM partsgroup
+               WHERE lower(partsgroup) = '$partsgroup'|;
+    $sth = $dbh->prepare($query);
+    $sth->execute || $form->dberror($query);
+
+    ($partsgroup_id) = $sth->fetchrow_array;
+    $sth->finish;
+
+    if (!$partsgroup_id) {
+      $query = qq|INSERT INTO partsgroup (partsgroup)
+                  VALUES ('$form->{partsgroup}')|;
+      $dbh->do($query) || $form->dberror($query);
+
+      $query = qq|SELECT id FROM partsgroup
+                  WHERE partsgroup = '$form->{partsgroup}'|;
+      $sth = $dbh->prepare($query);
+      $sth->execute || $form->dberror($query);
+
+      ($partsgroup_id) = $sth->fetchrow_array;
+      $sth->finish;
+    }
+  }
+  
+  
+  $query = qq|UPDATE parts SET 
+             partnumber = '$form->{partnumber}',
+             description = '$form->{description}',
+             makemodel = '$form->{makemodel}',
+             alternate = '$form->{alternate}',
+             assembly = '$form->{assembly}',
+             listprice = $form->{listprice},
+             sellprice = $form->{sellprice},
+             lastcost = $form->{lastcost},
+             weight = $form->{weight},
+             priceupdate = $form->{priceupdate},
+             unit = '$form->{unit}',
+             notes = '$form->{notes}',
+             rop = $form->{rop},
+             bin = '$form->{bin}',
+             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
+  unless ($form->{item} eq 'service') {
+    for my $i (1 .. $form->{makemodel_rows}) {
+      # put make and model together
+      if (($form->{"make_$i"}) || ($form->{"model_$i"})) {
+       map { $form->{"${_}_$i"} =~ s/'/''/g } qw(make model);
+       
+       $query = qq|INSERT INTO makemodel (parts_id, name)
+                   VALUES ($form->{id},
+                   '$form->{"make_$i"}:$form->{"model_$i"}')|;
+       $dbh->do($query) || $form->dberror($query);
+      }
+    }
+  }
+
+
+  # insert taxes
+  foreach $item (split / /, $form->{taxaccounts}) {
+    if ($form->{"IC_tax_$item"}) {
+      $query = qq|INSERT INTO partstax (parts_id, chart_id)
+                  VALUES ($form->{id}, 
+                         (SELECT id
+                          FROM chart
+                          WHERE accno = '$item'))|;
+      $dbh->do($query) || $form->dberror($query);
+    }
+  }
+
+  # add assembly records
+  if ($form->{item} eq 'assembly') {
+    
+    if ($form->{orphaned}) {
+      for my $i (1 .. $form->{assembly_rows}) {
+       $form->{"qty_$i"} = $form->parse_amount($myconfig, $form->{"qty_$i"});
+       
+       if ($form->{"qty_$i"} != 0) {
+         $form->{"bom_$i"} *= 1;
+         $query = qq|INSERT INTO assembly (id, parts_id, qty, bom)
+                     VALUES ($form->{id}, $form->{"id_$i"},
+                     $form->{"qty_$i"}, '$form->{"bom_$i"}')|;
+         $dbh->do($query) || $form->dberror($query);
+       }
+      }
+    }
+    
+    # adjust onhand for the assembly
+    if ($form->{onhand} != 0) {
+      &adjust_inventory($dbh, $form, $form->{id}, $form->{onhand});
+    }
+    
+  }
+
+  # commit
+  my $rc = $dbh->commit;
+  $dbh->disconnect;
+
+  $rc;
+  
+}
+
+
+
+sub update_assembly {
+  my ($dbh, $form, $id, $qty, $sellprice, $weight) = @_;
+
+  my $query = qq|SELECT id, qty
+                 FROM assembly
+                WHERE parts_id = $id|;
+  my $sth = $dbh->prepare($query);
+  $sth->execute || $form->dberror($query);
+
+  while (my ($pid, $aqty) = $sth->fetchrow_array) {
+    &update_assembly($dbh, $form, $pid, $aqty * $qty, $sellprice, $weight);
+  }
+  $sth->finish;
+
+  $query = qq|UPDATE parts
+              SET sellprice = sellprice +
+                 $qty * ($form->{sellprice} - $sellprice),
+                  weight = weight +
+                 $qty * ($form->{weight} - $weight)
+             WHERE id = $id|;
+  $dbh->do($query) || $form->dberror($query);
+
+}
+
+
+
+sub retrieve_assemblies {
+  my ($self, $myconfig, $form) = @_;
+  
+  # connect to database
+  my $dbh = $form->dbconnect($myconfig);
+
+  my $where = '1 = 1';
+  
+  if ($form->{partnumber}) {
+    my $partnumber = $form->like(lc $form->{partnumber});
+    $where .= " AND lower(p.partnumber) LIKE '$partnumber'";
+  }
+  
+  if ($form->{description}) {
+    my $description = $form->like(lc $form->{description});
+    $where .= " AND lower(p.description) LIKE '$description'";
+  }
+  $where .= " AND NOT p.obsolete = '1'";
+
+  # retrieve assembly items
+  my $query = qq|SELECT p.id, p.partnumber, p.description,
+                 p.bin, p.onhand, p.rop,
+                  (SELECT sum(p2.inventory_accno_id)
+                   FROM parts p2, assembly a
+                   WHERE p2.id = a.parts_id
+                   AND a.id = p.id) AS inventory
+                 FROM parts p
+                WHERE $where
+                AND assembly = '1'|;
+
+  my $sth = $dbh->prepare($query);
+  $sth->execute || $form->dberror($query);
+  
+  while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
+    push @{ $form->{assembly_items} }, $ref if $ref->{inventory};
+  }
+  $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"} != 0) {
+      &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)) {
+
+    my $allocate = $qty * $ref->{qty};
+    
+    # is it a service item, then loop
+    $ref->{inventory_accno_id} *= 1;
+    next if (($ref->{inventory_accno_id} == 0) && !$ref->{assembly});
+    
+    # adjust parts onhand
+    $form->update_balance($dbh,
+                         "parts",
+                         "onhand",
+                         qq|id = $ref->{id}|,
+                         $allocate * -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);
+  
+  if ($form->{item} eq 'assembly' && $form->{onhand} != 0) {
+    # adjust onhand for the assembly
+    &adjust_inventory($dbh, $form, $form->{id}, $form->{onhand} * -1);
+  }
+
+  my $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);
+
+  # check if it is a part, assembly or service
+  if ($form->{item} eq 'part') {
+    $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);
+  }
+
+  # commit
+  my $rc = $dbh->commit;
+  $dbh->disconnect;
+
+  $rc;
+  
+}
+
+
+sub assembly_item {
+  my ($self, $myconfig, $form) = @_;
+
+  my $i = $form->{assembly_rows};
+  my $var;
+  my $where = "1 = 1";
+
+  if ($form->{"partnumber_$i"}) {
+    $var = $form->like(lc $form->{"partnumber_$i"});
+    $where .= " AND lower(p.partnumber) LIKE '$var'";
+  }
+  if ($form->{"description_$i"}) {
+    $var = $form->like(lc $form->{"description_$i"});
+    $where .= " AND lower(p.description) LIKE '$var'";
+  }
+  if ($form->{"partsgroup_$i"}) {
+    $var = $form->like(lc $form->{"partsgroup_$i"});
+    $where .= " AND lower(pg.partsgroup) LIKE '$var'";
+  }
+  
+  if ($form->{id}) {
+    $where .= " AND NOT p.id = $form->{id}";
+  }
+
+  if ($partnumber) {
+    $where .= " ORDER BY p.partnumber";
+  } else {
+    $where .= " ORDER BY p.description";
+  }
+
+  # 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,
+                pg.partsgroup
+                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 $var;
+  
+  foreach my $item (qw(partnumber drawing microfiche)) {
+    if ($form->{$item}) {
+      $var = $form->like(lc $form->{$item});
+      $where .= " AND lower(p.$item) LIKE '$var'";
+    }
+  }
+  # special case for description
+  if ($form->{description}) {
+    unless ($form->{bought} || $form->{sold} || $form->{onorder} || $form->{ordered}) {
+      $var = $form->like(lc $form->{description});
+      $where .= " AND lower(p.description) LIKE '$var'";
+    }
+  }
+
+  if ($form->{searchitems} eq 'part') {
+    $where .= " AND p.inventory_accno_id > 0";
+  }
+  if ($form->{searchitems} eq 'assembly') {
+    $form->{bought} = "";
+    $where .= " AND p.assembly = '1'";
+  }
+  if ($form->{searchitems} eq 'service') {
+    $where .= " AND p.inventory_accno_id IS NULL AND NOT p.assembly = '1'";
+    # irrelevant for services
+    $form->{make} = $form->{model} = "";
+  }
+
+  # items which were never bought, sold or on an order
+  if ($form->{itemstatus} eq 'orphaned') {
+    $form->{onhand} = $form->{short} = 0;
+    $form->{bought} = $form->{sold} = 0;
+    $form->{onorder} = $form->{ordered} = 0;
+    $form->{transdatefrom} = $form->{transdateto} = "";
+    
+    $where .= " AND p.onhand = 0
+                AND p.id NOT IN (SELECT p.id FROM parts p, invoice i
+                                WHERE p.id = i.parts_id)
+               AND p.id NOT IN (SELECT p.id FROM parts p, assembly a
+                                WHERE p.id = a.parts_id)
+                AND p.id NOT IN (SELECT p.id FROM parts p, orderitems o
+                                WHERE p.id = o.parts_id)";
+  }
+  
+  if ($form->{itemstatus} eq 'active') {
+    $where .= " AND p.obsolete = '0'";
+  }
+  if ($form->{itemstatus} eq 'obsolete') {
+    $where .= " AND p.obsolete = '1'";
+    $form->{onhand} = $form->{short} = 0;
+  }
+  if ($form->{itemstatus} eq 'onhand') {
+    $where .= " AND p.onhand > 0";
+  }
+  if ($form->{itemstatus} eq 'short') {
+    $where .= " AND p.onhand < 0";
+  }
+
+  if ($form->{make}) {
+    $var = $form->like(lc $form->{make}).":%";
+    $where .= " AND p.id IN (SELECT DISTINCT ON (m.parts_id) m.parts_id
+                           FROM makemodel m WHERE lower(m.name) LIKE '$var')";
+  }
+  if ($form->{model}) {
+    $var = "%:".$form->like($form->{model});
+    $where .= " AND p.id IN (SELECT DISTINCT ON (m.parts_id) m.parts_id
+                           FROM makemodel m WHERE lower(m.name) LIKE '$var')";
+  }
+  if ($form->{partsgroup}) {
+    $var = $form->like(lc $form->{partsgroup});
+    $where .= " AND lower(pg.partsgroup) LIKE '$var'";
+    
+  }
+
+  # connect to database
+  my $dbh = $form->dbconnect($myconfig);
+
+  
+  my $sortorder = join ', ', $form->sort_columns(qw(partnumber description bin priceupdate partsgroup));
+  $sortorder = $form->{sort} unless $sortorder;
+
+  my $query = qq|SELECT p.id, p.partnumber, p.description, p.onhand, p.unit,
+                 p.bin, p.sellprice, p.listprice, p.lastcost, p.rop, p.weight,
+                p.priceupdate, p.image, p.drawing, p.microfiche,
+                pg.partsgroup
+                 FROM parts p
+                 LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
+                WHERE $where
+                ORDER BY $sortorder|;
+
+  # rebuild query for bought and sold items
+  if ($form->{bought} || $form->{sold} || $form->{onorder} || $form->{ordered}) {
+    
+    my $union = "";
+    $query = "";
+  
+    if ($form->{bought} || $form->{sold}) {
+      
+      my $invwhere = "$where";
+      $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}) {
+       $var = $form->like(lc $form->{description});
+       $invwhere .= " AND lower(i.description) LIKE '$var'";
+      }
+
+      my $flds = qq|p.id, p.partnumber, i.description,
+                    i.qty AS onhand, i.unit, p.bin, i.sellprice,
+                   p.listprice, p.lastcost, p.rop, p.weight,
+                   p.priceupdate, p.image, p.drawing, p.microfiche,
+                   pg.partsgroup,
+                   a.invnumber, a.ordnumber, i.trans_id|;
+
+      if ($form->{bought}) {
+       $query = qq|
+                   SELECT $flds, 'ir' AS module, '' AS type,
+                   1 AS exchangerate
+                   FROM parts p
+                   JOIN invoice i ON (i.parts_id = p.id)
+                   JOIN ap a ON (i.trans_id = a.id)
+                    LEFT JOIN partsgroup pg ON (p.partsgroup_id = pg.id)
+                   WHERE $invwhere|;
+       $union = "
+                 UNION";
+      }
+
+      if ($form->{sold}) {
+       $query .= qq|$union
+