This commit was manufactured by cvs2svn to create branch 'BESTPRACTICAL'.
authorcvs2git <cvs2git>
Mon, 12 Aug 2002 06:17:10 +0000 (06:17 +0000)
committercvs2git <cvs2git>
Mon, 12 Aug 2002 06:17:10 +0000 (06:17 +0000)
648 files changed:
ANNOUCE.1.4.0 [deleted file]
Artistic [deleted file]
CREDITS [deleted file]
FS/Changes [deleted file]
FS/FS.pm [deleted file]
FS/FS/CGI.pm [deleted file]
FS/FS/ClientAPI.pm [deleted file]
FS/FS/ClientAPI/MyAccount.pm [deleted file]
FS/FS/ClientAPI/passwd.pm [deleted file]
FS/FS/Conf.pm [deleted file]
FS/FS/ConfItem.pm [deleted file]
FS/FS/InitHandler.pm [deleted file]
FS/FS/Msgcat.pm [deleted file]
FS/FS/Record.pm [deleted file]
FS/FS/SearchCache.pm [deleted file]
FS/FS/UI/Base.pm [deleted file]
FS/FS/UI/CGI.pm [deleted file]
FS/FS/UI/Gtk.pm [deleted file]
FS/FS/UI/agent.pm [deleted file]
FS/FS/UID.pm [deleted file]
FS/FS/agent.pm [deleted file]
FS/FS/agent_type.pm [deleted file]
FS/FS/cust_bill.pm [deleted file]
FS/FS/cust_bill_event.pm [deleted file]
FS/FS/cust_bill_pay.pm [deleted file]
FS/FS/cust_bill_pkg.pm [deleted file]
FS/FS/cust_credit.pm [deleted file]
FS/FS/cust_credit_bill.pm [deleted file]
FS/FS/cust_credit_refund.pm [deleted file]
FS/FS/cust_main.pm [deleted file]
FS/FS/cust_main_county.pm [deleted file]
FS/FS/cust_main_invoice.pm [deleted file]
FS/FS/cust_pay.pm [deleted file]
FS/FS/cust_pay_batch.pm [deleted file]
FS/FS/cust_pkg.pm [deleted file]
FS/FS/cust_refund.pm [deleted file]
FS/FS/cust_svc.pm [deleted file]
FS/FS/cust_tax_exempt.pm [deleted file]
FS/FS/domain_record.pm [deleted file]
FS/FS/export_svc.pm [deleted file]
FS/FS/msgcat.pm [deleted file]
FS/FS/nas.pm [deleted file]
FS/FS/part_bill_event.pm [deleted file]
FS/FS/part_export.pm [deleted file]
FS/FS/part_export/bind.pm [deleted file]
FS/FS/part_export/bind_slave.pm [deleted file]
FS/FS/part_export/bsdshell.pm [deleted file]
FS/FS/part_export/cp.pm [deleted file]
FS/FS/part_export/cyrus.pm [deleted file]
FS/FS/part_export/http.pm [deleted file]
FS/FS/part_export/infostreet.pm [deleted file]
FS/FS/part_export/null.pm [deleted file]
FS/FS/part_export/shellcommands.pm [deleted file]
FS/FS/part_export/shellcommands_withdomain.pm [deleted file]
FS/FS/part_export/sqlmail.pm [deleted file]
FS/FS/part_export/sqlradius.pm [deleted file]
FS/FS/part_export/sysvshell.pm [deleted file]
FS/FS/part_export/textradius.pm [deleted file]
FS/FS/part_export/vpopmail.pm [deleted file]
FS/FS/part_export/www_shellcommands.pm [deleted file]
FS/FS/part_export_option.pm [deleted file]
FS/FS/part_pkg.pm [deleted file]
FS/FS/part_pop_local.pm [deleted file]
FS/FS/part_referral.pm [deleted file]
FS/FS/part_svc.pm [deleted file]
FS/FS/part_svc_column.pm [deleted file]
FS/FS/pkg_svc.pm [deleted file]
FS/FS/port.pm [deleted file]
FS/FS/prepay_credit.pm [deleted file]
FS/FS/queue.pm [deleted file]
FS/FS/queue_arg.pm [deleted file]
FS/FS/queue_depend.pm [deleted file]
FS/FS/raddb.pm [deleted file]
FS/FS/radius_usergroup.pm [deleted file]
FS/FS/session.pm [deleted file]
FS/FS/svc_Common.pm [deleted file]
FS/FS/svc_acct.pm [deleted file]
FS/FS/svc_acct_pop.pm [deleted file]
FS/FS/svc_acct_sm.pm [deleted file]
FS/FS/svc_domain.pm [deleted file]
FS/FS/svc_forward.pm [deleted file]
FS/FS/svc_www.pm [deleted file]
FS/FS/type_pkgs.pm [deleted file]
FS/MANIFEST [deleted file]
FS/MANIFEST.SKIP [deleted file]
FS/Makefile.PL [deleted file]
FS/README [deleted file]
FS/bin/freeside-adduser [deleted file]
FS/bin/freeside-apply-credits [deleted file]
FS/bin/freeside-bill [deleted file]
FS/bin/freeside-cc-receipts-report [deleted file]
FS/bin/freeside-credit-report [deleted file]
FS/bin/freeside-daily [deleted file]
FS/bin/freeside-email [deleted file]
FS/bin/freeside-expiration-alerter [deleted file]
FS/bin/freeside-overdue [deleted file]
FS/bin/freeside-queued [deleted file]
FS/bin/freeside-receivables-report [deleted file]
FS/bin/freeside-reexport [deleted file]
FS/bin/freeside-setinvoice [deleted file]
FS/bin/freeside-sqlradius-reset [deleted file]
FS/bin/freeside-tax-report [deleted file]
FS/t/CGI.t [deleted file]
FS/t/ClientAPI.t [deleted file]
FS/t/Conf.t [deleted file]
FS/t/ConfItem.t [deleted file]
FS/t/InitHandler.t [deleted file]
FS/t/Msgcat.t [deleted file]
FS/t/Record.t [deleted file]
FS/t/SearchCache.t [deleted file]
FS/t/UID.t [deleted file]
FS/t/agent.t [deleted file]
FS/t/agent_type.t [deleted file]
FS/t/cust_bill.t [deleted file]
FS/t/cust_bill_event.t [deleted file]
FS/t/cust_bill_pay.t [deleted file]
FS/t/cust_bill_pkg.t [deleted file]
FS/t/cust_credit.t [deleted file]
FS/t/cust_credit_bill.t [deleted file]
FS/t/cust_credit_refund.t [deleted file]
FS/t/cust_main.t [deleted file]
FS/t/cust_main_county.t [deleted file]
FS/t/cust_main_invoice.t [deleted file]
FS/t/cust_pay.t [deleted file]
FS/t/cust_pay_batch.t [deleted file]
FS/t/cust_pkg.t [deleted file]
FS/t/cust_refund.t [deleted file]
FS/t/cust_svc.t [deleted file]
FS/t/cust_tax_exempt.pm [deleted file]
FS/t/cust_tax_exempt.t [deleted file]
FS/t/domain_record.t [deleted file]
FS/t/export_svc.t [deleted file]
FS/t/msgcat.t [deleted file]
FS/t/nas.t [deleted file]
FS/t/part_bill_event.t [deleted file]
FS/t/part_export-bind.t [deleted file]
FS/t/part_export-bind_slave.t [deleted file]
FS/t/part_export-bsdshell.t [deleted file]
FS/t/part_export-cp.t [deleted file]
FS/t/part_export-cyrus.t [deleted file]
FS/t/part_export-http.t [deleted file]
FS/t/part_export-infostreet.t [deleted file]
FS/t/part_export-null.t [deleted file]
FS/t/part_export-shellcommands.t [deleted file]
FS/t/part_export-shellcommands_withdomain.t [deleted file]
FS/t/part_export-sqlmail.t [deleted file]
FS/t/part_export-sqlradius.t [deleted file]
FS/t/part_export-sysvshell.t [deleted file]
FS/t/part_export-textradius.t [deleted file]
FS/t/part_export-vpopmail.t [deleted file]
FS/t/part_export-www_shellcommands.t [deleted file]
FS/t/part_export.t [deleted file]
FS/t/part_export_option.t [deleted file]
FS/t/part_pkg.t [deleted file]
FS/t/part_pop_local.t [deleted file]
FS/t/part_referral.t [deleted file]
FS/t/part_svc.t [deleted file]
FS/t/part_svc_column.t [deleted file]
FS/t/pkg_svc.t [deleted file]
FS/t/port.t [deleted file]
FS/t/prepay_credit.t [deleted file]
FS/t/queue.t [deleted file]
FS/t/queue_arg.t [deleted file]
FS/t/queue_depend.t [deleted file]
FS/t/raddb.t [deleted file]
FS/t/radius_usergroup.t [deleted file]
FS/t/session.t [deleted file]
FS/t/svc_Common.t [deleted file]
FS/t/svc_acct.t [deleted file]
FS/t/svc_acct_pop.t [deleted file]
FS/t/svc_acct_sm.t [deleted file]
FS/t/svc_domain.t [deleted file]
FS/t/svc_forward.t [deleted file]
FS/t/svc_www.t [deleted file]
FS/t/type_pkgs.t [deleted file]
GPL [deleted file]
INSTALL [deleted file]
Makefile [deleted file]
README [deleted file]
TODO [deleted file]
bin/bind.export [deleted file]
bin/bind.import [deleted file]
bin/bsdshell.export [deleted file]
bin/create-history-tables [deleted file]
bin/dbdef-create [deleted file]
bin/freeside-init [deleted file]
bin/freeside-session-kill [deleted file]
bin/fs-migrate-part_svc [deleted file]
bin/fs-migrate-payref [deleted file]
bin/fs-migrate-svc_acct_sm [deleted file]
bin/fs-radius-add-check [deleted file]
bin/fs-radius-add-reply [deleted file]
bin/fs-setup [deleted file]
bin/generate-prepay [deleted file]
bin/generate-raddb [deleted file]
bin/generate-tests [deleted file]
bin/masonize [deleted file]
bin/passwd.import [deleted file]
bin/pod2x [deleted file]
bin/populate-msgcat [deleted file]
bin/svc_acct.export [deleted file]
bin/svc_acct.import [deleted file]
bin/svc_acct_sm.import [deleted file]
bin/svc_domain.erase [deleted file]
bin/sysvshell.export [deleted file]
conf/agent_defaultpkg [deleted file]
conf/alerter_template [deleted file]
conf/declinetemplate [deleted file]
conf/home [deleted file]
conf/invoice_from [deleted file]
conf/invoice_template [deleted file]
conf/locale [deleted file]
conf/lpr [deleted file]
conf/maxsearchrecordsperpage [deleted file]
conf/report_template [deleted file]
conf/shells [deleted file]
conf/show-msgcat-codes [deleted file]
conf/smtpmachine [deleted file]
debian/README.Debian [deleted file]
debian/changelog [deleted file]
debian/conffiles.ex [deleted file]
debian/control [deleted file]
debian/copyright [deleted file]
debian/cron.d.ex [deleted file]
debian/dirs [deleted file]
debian/docs [deleted file]
debian/ex.doc-base.package [deleted file]
debian/freeside-doc.docs [deleted file]
debian/freeside-doc.files [deleted file]
debian/init.d.ex [deleted file]
debian/manpage.1.ex [deleted file]
debian/manpage.sgml.ex [deleted file]
debian/menu.ex [deleted file]
debian/postinst.ex [deleted file]
debian/postrm.ex [deleted file]
debian/preinst.ex [deleted file]
debian/prerm.ex [deleted file]
debian/rules [deleted file]
debian/watch.ex [deleted file]
eg/TEMPLATE_cust_main.import [deleted file]
eg/export_template.pm [deleted file]
eg/table_template-svc.pm [deleted file]
eg/table_template.pm [deleted file]
eg/vpopmailrestart [deleted file]
etc/acp_logfile-parse [deleted file]
etc/countries.txt [deleted file]
etc/domain-template.txt [deleted file]
etc/example-direct-cardin [deleted file]
etc/megapop.pl [deleted file]
etc/sql-reserved-words.txt [deleted file]
fs_passwd/fs_passwd [deleted file]
fs_passwd/fs_passwd.cgi [deleted file]
fs_passwd/fs_passwd.html [deleted file]
fs_passwd/fs_passwd_server [deleted file]
fs_passwd/fs_passwdd [deleted file]
fs_radlog/fs_radlogd [deleted file]
fs_selfadmin/FS-MailAdminServer/MailAdminClient.pm [deleted file]
fs_selfadmin/FS-MailAdminServer/cgi/mailadmin.cgi [deleted file]
fs_selfadmin/FS-MailAdminServer/fs_mailadmind [deleted file]
fs_selfadmin/README [deleted file]
fs_selfadmin/fs_mailadmin_server [deleted file]
fs_selfservice/DEPLOY [deleted file]
fs_selfservice/FS-SelfService/Changes [deleted file]
fs_selfservice/FS-SelfService/MANIFEST [deleted file]
fs_selfservice/FS-SelfService/Makefile.PL [deleted file]
fs_selfservice/FS-SelfService/SelfService.pm [deleted file]
fs_selfservice/FS-SelfService/cgi/login.html [deleted file]
fs_selfservice/FS-SelfService/cgi/myaccount.html [deleted file]
fs_selfservice/FS-SelfService/cgi/passwd.html [deleted file]
fs_selfservice/FS-SelfService/cgi/selfservice.cgi [deleted file]
fs_selfservice/FS-SelfService/cgi/view_invoice.html [deleted file]
fs_selfservice/FS-SelfService/freeside-selfservice-clientd [deleted file]
fs_selfservice/FS-SelfService/test.pl [deleted file]
fs_selfservice/freeside-selfservice-server [deleted file]
fs_selfservice/fs_passwd_test [deleted file]
fs_sesmon/FS-SessionClient/Changes [deleted file]
fs_sesmon/FS-SessionClient/MANIFEST [deleted file]
fs_sesmon/FS-SessionClient/MANIFEST.SKIP [deleted file]
fs_sesmon/FS-SessionClient/Makefile.PL [deleted file]
fs_sesmon/FS-SessionClient/SessionClient.pm [deleted file]
fs_sesmon/FS-SessionClient/bin/freeside-login [deleted file]
fs_sesmon/FS-SessionClient/bin/freeside-logout [deleted file]
fs_sesmon/FS-SessionClient/cgi/login.cgi [deleted file]
fs_sesmon/FS-SessionClient/cgi/logout.cgi [deleted file]
fs_sesmon/FS-SessionClient/fs_sessiond [deleted file]
fs_sesmon/FS-SessionClient/test.pl [deleted file]
fs_sesmon/fs_session_server [deleted file]
fs_signup/FS-SignupClient/Changes [deleted file]
fs_signup/FS-SignupClient/MANIFEST [deleted file]
fs_signup/FS-SignupClient/MANIFEST.SKIP [deleted file]
fs_signup/FS-SignupClient/Makefile.PL [deleted file]
fs_signup/FS-SignupClient/SignupClient.pm [deleted file]
fs_signup/FS-SignupClient/cgi/decline.html [deleted file]
fs_signup/FS-SignupClient/cgi/signup-alternate.html [deleted file]
fs_signup/FS-SignupClient/cgi/signup.cgi [deleted file]
fs_signup/FS-SignupClient/cgi/signup.html [deleted file]
fs_signup/FS-SignupClient/cgi/success.html [deleted file]
fs_signup/FS-SignupClient/fs_signupd [deleted file]
fs_signup/FS-SignupClient/test.pl [deleted file]
fs_signup/cck.template [deleted file]
fs_signup/fs_signup_server [deleted file]
fs_signup/ieak.template [deleted file]
fs_webdemo/register.cgi [deleted file]
fs_webdemo/register.html [deleted file]
fs_webdemo/registerd [deleted file]
fs_webdemo/registerd.Pg [deleted file]
htetc/global.asa [deleted file]
htetc/handler.pl [deleted file]
httemplate/.htaccess [deleted file]
httemplate/browse/agent.cgi [deleted file]
httemplate/browse/agent_type.cgi [deleted file]
httemplate/browse/cust_main_county.cgi [deleted file]
httemplate/browse/cust_pay_batch.cgi [deleted file]
httemplate/browse/msgcat.cgi [deleted file]
httemplate/browse/nas.cgi [deleted file]
httemplate/browse/part_bill_event.cgi [deleted file]
httemplate/browse/part_export.cgi [deleted file]
httemplate/browse/part_pkg.cgi [deleted file]
httemplate/browse/part_referral.cgi [deleted file]
httemplate/browse/part_svc.cgi [deleted file]
httemplate/browse/queue.cgi [deleted file]
httemplate/browse/svc_acct_pop.cgi [deleted file]
httemplate/config/config-process.cgi [deleted file]
httemplate/config/config-view.cgi [deleted file]
httemplate/config/config.cgi [deleted file]
httemplate/docs/admin.html [deleted file]
httemplate/docs/billing.html [deleted file]
httemplate/docs/config.html [deleted file]
httemplate/docs/export.html [deleted file]
httemplate/docs/index.html [deleted file]
httemplate/docs/install.html [deleted file]
httemplate/docs/legacy.html [deleted file]
httemplate/docs/overview.dia [deleted file]
httemplate/docs/overview.png [deleted file]
httemplate/docs/passwd.html [deleted file]
httemplate/docs/schema.dia [deleted file]
httemplate/docs/schema.html [deleted file]
httemplate/docs/schema.png [deleted file]
httemplate/docs/session.html [deleted file]
httemplate/docs/signup.html [deleted file]
httemplate/docs/ssh.html [deleted file]
httemplate/docs/trouble.html [deleted file]
httemplate/docs/upgrade4.html [deleted file]
httemplate/docs/upgrade5.html [deleted file]
httemplate/docs/upgrade6.html [deleted file]
httemplate/docs/upgrade7.html [deleted file]
httemplate/docs/upgrade8.html [deleted file]
httemplate/edit/REAL_cust_pkg.cgi [deleted file]
httemplate/edit/agent.cgi [deleted file]
httemplate/edit/agent_type.cgi [deleted file]
httemplate/edit/cust_bill_pay.cgi [deleted file]
httemplate/edit/cust_credit.cgi [deleted file]
httemplate/edit/cust_credit_bill.cgi [deleted file]
httemplate/edit/cust_main.cgi [deleted file]
httemplate/edit/cust_main_county-expand.cgi [deleted file]
httemplate/edit/cust_main_county.cgi [deleted file]
httemplate/edit/cust_pay.cgi [deleted file]
httemplate/edit/cust_pkg.cgi [deleted file]
httemplate/edit/msgcat.cgi [deleted file]
httemplate/edit/part_bill_event.cgi [deleted file]
httemplate/edit/part_export.cgi [deleted file]
httemplate/edit/part_pkg.cgi [deleted file]
httemplate/edit/part_referral.cgi [deleted file]
httemplate/edit/part_svc.cgi [deleted file]
httemplate/edit/process/REAL_cust_pkg.cgi [deleted file]
httemplate/edit/process/agent.cgi [deleted file]
httemplate/edit/process/agent_type.cgi [deleted file]
httemplate/edit/process/cust_bill_pay.cgi [deleted file]
httemplate/edit/process/cust_credit.cgi [deleted file]
httemplate/edit/process/cust_credit_bill.cgi [deleted file]
httemplate/edit/process/cust_main.cgi [deleted file]
httemplate/edit/process/cust_main_county-collapse.cgi [deleted file]
httemplate/edit/process/cust_main_county-expand.cgi [deleted file]
httemplate/edit/process/cust_main_county.cgi [deleted file]
httemplate/edit/process/cust_pay.cgi [deleted file]
httemplate/edit/process/cust_pkg.cgi [deleted file]
httemplate/edit/process/domain_record.cgi [deleted file]
httemplate/edit/process/msgcat.cgi [deleted file]
httemplate/edit/process/part_bill_event.cgi [deleted file]
httemplate/edit/process/part_export.cgi [deleted file]
httemplate/edit/process/part_pkg.cgi [deleted file]
httemplate/edit/process/part_referral.cgi [deleted file]
httemplate/edit/process/part_svc.cgi [deleted file]
httemplate/edit/process/quick-charge.cgi [deleted file]
httemplate/edit/process/quick-cust_pkg.cgi [deleted file]
httemplate/edit/process/svc_acct.cgi [deleted file]
httemplate/edit/process/svc_acct_pop.cgi [deleted file]
httemplate/edit/process/svc_acct_sm.cgi [deleted file]
httemplate/edit/process/svc_domain.cgi [deleted file]
httemplate/edit/process/svc_forward.cgi [deleted file]
httemplate/edit/process/svc_www.cgi [deleted file]
httemplate/edit/svc_acct.cgi [deleted file]
httemplate/edit/svc_acct_pop.cgi [deleted file]
httemplate/edit/svc_acct_sm.cgi [deleted file]
httemplate/edit/svc_domain.cgi [deleted file]
httemplate/edit/svc_forward.cgi [deleted file]
httemplate/edit/svc_www.cgi [deleted file]
httemplate/images/mid-logo.png [deleted file]
httemplate/images/small-logo.png [deleted file]
httemplate/index.html [deleted file]
httemplate/misc/bill.cgi [deleted file]
httemplate/misc/cancel-unaudited.cgi [deleted file]
httemplate/misc/cancel_pkg.cgi [deleted file]
httemplate/misc/catchall.cgi [deleted file]
httemplate/misc/delete-cust_pay.cgi [deleted file]
httemplate/misc/delete-customer.cgi [deleted file]
httemplate/misc/delete-domain_record.cgi [deleted file]
httemplate/misc/delete-part_export.cgi [deleted file]
httemplate/misc/expire_pkg.cgi [deleted file]
httemplate/misc/link.cgi [deleted file]
httemplate/misc/print-invoice.cgi [deleted file]
httemplate/misc/process/catchall.cgi [deleted file]
httemplate/misc/process/delete-customer.cgi [deleted file]
httemplate/misc/process/link.cgi [deleted file]
httemplate/misc/queue.cgi [deleted file]
httemplate/misc/susp_pkg.cgi [deleted file]
httemplate/misc/unsusp_pkg.cgi [deleted file]
httemplate/search/cust_bill.cgi [deleted file]
httemplate/search/cust_bill.html [deleted file]
httemplate/search/cust_bill_event.cgi [deleted file]
httemplate/search/cust_bill_event.html [deleted file]
httemplate/search/cust_main-otaker.cgi [deleted file]
httemplate/search/cust_main-payinfo.html [deleted file]
httemplate/search/cust_main-quickpay.html [deleted file]
httemplate/search/cust_main.cgi [deleted file]
httemplate/search/cust_main.html [deleted file]
httemplate/search/cust_pay.cgi [deleted file]
httemplate/search/cust_pay.html [deleted file]
httemplate/search/cust_pkg.cgi [deleted file]
httemplate/search/cust_pkg.html [deleted file]
httemplate/search/report_cc.cgi [deleted file]
httemplate/search/report_cc.html [deleted file]
httemplate/search/report_credit.cgi [deleted file]
httemplate/search/report_credit.html [deleted file]
httemplate/search/report_receivables.cgi [deleted file]
httemplate/search/report_tax.cgi [deleted file]
httemplate/search/report_tax.html [deleted file]
httemplate/search/sql.cgi [deleted file]
httemplate/search/svc_acct.cgi [deleted file]
httemplate/search/svc_acct.html [deleted file]
httemplate/search/svc_acct_sm.cgi [deleted file]
httemplate/search/svc_acct_sm.html [deleted file]
httemplate/search/svc_domain.cgi [deleted file]
httemplate/search/svc_domain.html [deleted file]
httemplate/view/cust_bill.cgi [deleted file]
httemplate/view/cust_main.cgi [deleted file]
httemplate/view/cust_pkg.cgi [deleted file]
httemplate/view/svc_acct.cgi [deleted file]
httemplate/view/svc_acct_sm.cgi [deleted file]
httemplate/view/svc_domain.cgi [deleted file]
httemplate/view/svc_forward.cgi [deleted file]
httemplate/view/svc_www.cgi [deleted file]
init.d/freeside-init [deleted file]
rt/ChangeLog [deleted file]
rt/TODO [deleted file]
rt/bin/initacls.Oracle [deleted file]
rt/bin/initacls.Pg [deleted file]
rt/bin/initacls.mysql [deleted file]
rt/bin/rtadmin [deleted file]
rt/docs/design_docs/basic-definitions.txt [deleted file]
rt/docs/design_docs/local_hacking [deleted file]
rt/docs/rt.gif [deleted file]
rt/etc/config.pm [deleted file]
rt/etc/schema.pm [deleted file]
rt/lib/MANIFEST [deleted file]
rt/lib/MANIFEST.SKIP [deleted file]
rt/lib/Makefile.PL [deleted file]
rt/lib/RT/Action/OpenDependent.pm [deleted file]
rt/lib/RT/Action/SendPasswordEmail.pm [deleted file]
rt/lib/RT/Action/StallDependent.pm [deleted file]
rt/lib/RT/Condition/NewDependency.pm [deleted file]
rt/lib/RT/EasySearch.pm [deleted file]
rt/lib/RT/Keyword.pm [deleted file]
rt/lib/RT/KeywordSelect.pm [deleted file]
rt/lib/RT/KeywordSelects.pm [deleted file]
rt/lib/RT/Keywords.pm [deleted file]
rt/lib/RT/ObjectKeyword.pm [deleted file]
rt/lib/RT/ObjectKeywords.pm [deleted file]
rt/lib/RT/TestHarness.pm [deleted file]
rt/lib/RT/Watcher.pm [deleted file]
rt/lib/RT/Watchers.pm [deleted file]
rt/lib/test.pl [deleted file]
rt/tools/cpan2rpm [deleted file]
rt/tools/initdb [deleted file]
rt/tools/insertdata [deleted file]
rt/tools/testdeps [deleted file]
rt/webrt/Admin/Elements/CreateQueueCalled [deleted file]
rt/webrt/Admin/Elements/CreateUserCalled [deleted file]
rt/webrt/Admin/Elements/EditUserComments [deleted file]
rt/webrt/Admin/Elements/GrantQueueRightsTo [deleted file]
rt/webrt/Admin/Elements/GroupTabs [deleted file]
rt/webrt/Admin/Elements/Header [deleted file]
rt/webrt/Admin/Elements/ListGlobalKeywordSelects [deleted file]
rt/webrt/Admin/Elements/ListGlobalScrips [deleted file]
rt/webrt/Admin/Elements/ModifyKeyword [deleted file]
rt/webrt/Admin/Elements/ModifyKeywordSelect [deleted file]
rt/webrt/Admin/Elements/ModifyQueue [deleted file]
rt/webrt/Admin/Elements/ModifyTemplate [deleted file]
rt/webrt/Admin/Elements/ModifyUser [deleted file]
rt/webrt/Admin/Elements/QueueRightsForUser [deleted file]
rt/webrt/Admin/Elements/QueueTabs [deleted file]
rt/webrt/Admin/Elements/SelectKeywordSelect [deleted file]
rt/webrt/Admin/Elements/SelectModifyGroup [deleted file]
rt/webrt/Admin/Elements/SelectModifyKeyword [deleted file]
rt/webrt/Admin/Elements/SelectModifyKeywordSelect [deleted file]
rt/webrt/Admin/Elements/SelectModifyQueue [deleted file]
rt/webrt/Admin/Elements/SelectModifyUser [deleted file]
rt/webrt/Admin/Elements/SelectQueueRights [deleted file]
rt/webrt/Admin/Elements/SelectRights [deleted file]
rt/webrt/Admin/Elements/SelectScrip [deleted file]
rt/webrt/Admin/Elements/SelectScripAction [deleted file]
rt/webrt/Admin/Elements/SelectScripCondition [deleted file]
rt/webrt/Admin/Elements/SelectSingleOrMultiple [deleted file]
rt/webrt/Admin/Elements/SelectTemplate [deleted file]
rt/webrt/Admin/Elements/SelectUsers [deleted file]
rt/webrt/Admin/Elements/SystemTabs [deleted file]
rt/webrt/Admin/Elements/Tabs [deleted file]
rt/webrt/Admin/Elements/UserTabs [deleted file]
rt/webrt/Admin/Global/GroupRights.html [deleted file]
rt/webrt/Admin/Global/Keywords.html [deleted file]
rt/webrt/Admin/Global/Scrips.html [deleted file]
rt/webrt/Admin/Global/Template.html [deleted file]
rt/webrt/Admin/Global/Templates.html [deleted file]
rt/webrt/Admin/Global/UserRights.html [deleted file]
rt/webrt/Admin/Global/index.html [deleted file]
rt/webrt/Admin/Groups/Members.html [deleted file]
rt/webrt/Admin/Groups/Modify.html [deleted file]
rt/webrt/Admin/Groups/Rights.html [deleted file]
rt/webrt/Admin/Groups/index.html [deleted file]
rt/webrt/Admin/KeywordSelects/Modify.html [deleted file]
rt/webrt/Admin/KeywordSelects/index.html [deleted file]
rt/webrt/Admin/Keywords/Modify.html [deleted file]
rt/webrt/Admin/Keywords/index.html [deleted file]
rt/webrt/Admin/Queues/Create.html [deleted file]
rt/webrt/Admin/Queues/GroupRights.html [deleted file]
rt/webrt/Admin/Queues/Keywords.html [deleted file]
rt/webrt/Admin/Queues/Modify.html [deleted file]
rt/webrt/Admin/Queues/People.html [deleted file]
rt/webrt/Admin/Queues/Scrips.html [deleted file]
rt/webrt/Admin/Queues/Template.html [deleted file]
rt/webrt/Admin/Queues/Templates.html [deleted file]
rt/webrt/Admin/Queues/UserRights.html [deleted file]
rt/webrt/Admin/Queues/index.html [deleted file]
rt/webrt/Admin/Users/Modify.html [deleted file]
rt/webrt/Admin/Users/Prefs.html [deleted file]
rt/webrt/Admin/Users/Rights.html [deleted file]
rt/webrt/Admin/Users/index.html [deleted file]
rt/webrt/Admin/index.html [deleted file]
rt/webrt/Elements/Checkbox [deleted file]
rt/webrt/Elements/CreateTicket [deleted file]
rt/webrt/Elements/CustomHomepageHeader [deleted file]
rt/webrt/Elements/Error [deleted file]
rt/webrt/Elements/Footer [deleted file]
rt/webrt/Elements/GotoTicket [deleted file]
rt/webrt/Elements/Header [deleted file]
rt/webrt/Elements/ListActions [deleted file]
rt/webrt/Elements/Login [deleted file]
rt/webrt/Elements/MessageBox [deleted file]
rt/webrt/Elements/MyRequests [deleted file]
rt/webrt/Elements/MyTickets [deleted file]
rt/webrt/Elements/Quicksearch [deleted file]
rt/webrt/Elements/Refresh [deleted file]
rt/webrt/Elements/Section [deleted file]
rt/webrt/Elements/SelectBoolean [deleted file]
rt/webrt/Elements/SelectDate [deleted file]
rt/webrt/Elements/SelectDateRelation [deleted file]
rt/webrt/Elements/SelectDateType [deleted file]
rt/webrt/Elements/SelectEqualityOperator [deleted file]
rt/webrt/Elements/SelectKeyword [deleted file]
rt/webrt/Elements/SelectKeywordOptions [deleted file]
rt/webrt/Elements/SelectLinkType [deleted file]
rt/webrt/Elements/SelectMatch [deleted file]
rt/webrt/Elements/SelectNewTicketQueue [deleted file]
rt/webrt/Elements/SelectOwner [deleted file]
rt/webrt/Elements/SelectQueue [deleted file]
rt/webrt/Elements/SelectResultsPerPage [deleted file]
rt/webrt/Elements/SelectSortOrder [deleted file]
rt/webrt/Elements/SelectStatus [deleted file]
rt/webrt/Elements/SelectTicketSortBy [deleted file]
rt/webrt/Elements/SelectUsers [deleted file]
rt/webrt/Elements/SelectWatcherType [deleted file]
rt/webrt/Elements/ShadedBox [deleted file]
rt/webrt/Elements/Submit [deleted file]
rt/webrt/Elements/Tabs [deleted file]
rt/webrt/Elements/TitleBoxEnd [deleted file]
rt/webrt/Elements/TitleBoxStart [deleted file]
rt/webrt/Elements/ViewUser [deleted file]
rt/webrt/Elements/dayMenu [deleted file]
rt/webrt/Elements/monthMenu [deleted file]
rt/webrt/Elements/yearMenu [deleted file]
rt/webrt/NoAuth/Logout.html [deleted file]
rt/webrt/NoAuth/Reminder.html [deleted file]
rt/webrt/NoAuth/images/rt.jpg [deleted file]
rt/webrt/NoAuth/images/spacer.gif [deleted file]
rt/webrt/NoAuth/webrt.css [deleted file]
rt/webrt/Search/Bulk.html [deleted file]
rt/webrt/Search/Listing.html [deleted file]
rt/webrt/Search/PickRestriction [deleted file]
rt/webrt/Search/RestrictSearch.html [deleted file]
rt/webrt/Search/TicketCell [deleted file]
rt/webrt/SelfService/Attachment/dhandler [deleted file]
rt/webrt/SelfService/Closed.html [deleted file]
rt/webrt/SelfService/Create.html [deleted file]
rt/webrt/SelfService/Display.html [deleted file]
rt/webrt/SelfService/Elements/GotoTicket [deleted file]
rt/webrt/SelfService/Elements/Header [deleted file]
rt/webrt/SelfService/Elements/MyRequests [deleted file]
rt/webrt/SelfService/Elements/Tabs [deleted file]
rt/webrt/SelfService/Error.html [deleted file]
rt/webrt/SelfService/Prefs.html [deleted file]
rt/webrt/SelfService/Update.html [deleted file]
rt/webrt/SelfService/index.html [deleted file]
rt/webrt/Ticket/Attachment/dhandler [deleted file]
rt/webrt/Ticket/Create.html [deleted file]
rt/webrt/Ticket/Display.html [deleted file]
rt/webrt/Ticket/Elements/AddWatchers [deleted file]
rt/webrt/Ticket/Elements/EditBasics [deleted file]
rt/webrt/Ticket/Elements/EditDates [deleted file]
rt/webrt/Ticket/Elements/EditKeywordSelects [deleted file]
rt/webrt/Ticket/Elements/EditLinks [deleted file]
rt/webrt/Ticket/Elements/EditPeople [deleted file]
rt/webrt/Ticket/Elements/EditWatchers [deleted file]
rt/webrt/Ticket/Elements/ShowBasics [deleted file]
rt/webrt/Ticket/Elements/ShowDates [deleted file]
rt/webrt/Ticket/Elements/ShowDependencies [deleted file]
rt/webrt/Ticket/Elements/ShowHistory [deleted file]
rt/webrt/Ticket/Elements/ShowKeywordSelects [deleted file]
rt/webrt/Ticket/Elements/ShowLinks [deleted file]
rt/webrt/Ticket/Elements/ShowMemberOf [deleted file]
rt/webrt/Ticket/Elements/ShowMembers [deleted file]
rt/webrt/Ticket/Elements/ShowPeople [deleted file]
rt/webrt/Ticket/Elements/ShowReferences [deleted file]
rt/webrt/Ticket/Elements/ShowRequestor [deleted file]
rt/webrt/Ticket/Elements/ShowSummary [deleted file]
rt/webrt/Ticket/Elements/ShowTransaction [deleted file]
rt/webrt/Ticket/Elements/Tabs [deleted file]
rt/webrt/Ticket/Elements/ToolBar [deleted file]
rt/webrt/Ticket/History.html [deleted file]
rt/webrt/Ticket/Modify.html [deleted file]
rt/webrt/Ticket/ModifyAll.html [deleted file]
rt/webrt/Ticket/ModifyDates.html [deleted file]
rt/webrt/Ticket/ModifyLinks.html [deleted file]
rt/webrt/Ticket/ModifyPeople.html [deleted file]
rt/webrt/Ticket/Update.html [deleted file]
rt/webrt/User/Prefs.html [deleted file]
rt/webrt/autohandler [deleted file]
rt/webrt/index.html [deleted file]
test/cgi-test [deleted file]

diff --git a/ANNOUCE.1.4.0 b/ANNOUCE.1.4.0
deleted file mode 100644 (file)
index d110c6f..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-Hi,
-
-I'm pleased to announce the first beta release of Freeside 1.4.0.
-Freeside is a web-based, open-source billing and account administration
-package for ISPs, web hosts, and similar businesses.
-
-You can see a web demo, read the documentation, and download the new beta
-at <http://www.sisd.com/freeside/>.
-
-Although Freeside is free software, it is supported commercially with
-installation, customization, training and support services.  Please
-consider our services and help support the development of the software!
-
-
-Major new features in 1.4.0 include:
-
-- Billing engine has been rewritten and now has support for easily added
-  "price plans".  Included price plans include anniversary billing,
-  1st-of-the-month billing (pro-rated and subscription), free for N days,
-  commissions for referrals and per-minute/per-hour charges.
-
-- Customer-to-customer referrals, tracking and commissions.
-
-- Configurable invoice events triggered for delinquent customers can
-  re-send invoices, suspend accounts, charge late fees, and so on.
-
-- Export and provisioning system has been rewritten.  New provisioning
-  methods can now be "plugged-in" for any service type.  Included exports
-  include BSD and Linux password files, configurable shell commands,
-  RADIUS (both text and SQL, including groups), BIND configuration files,
-  Cyrus, vpopmail, and many others.
-
-- Complete set of history tables tracking all changes to the database.
-
-- Job queue with display and retry for provisioning tasks.
-
-- UI overhaul - easier to navigate and use.  Quick package order and
-  one-time charges.  Separate billing and service contact information.
-  Customer comments.
-
-- Performance optimizations.
-
-- Financials have been rewritten.  Apply payments and credits against
-  specific invoices (in whole or in part), or have the system apply
-  automatically.
-
-- Texas tax.
-
-- Improved documentation and easier install.
-
diff --git a/Artistic b/Artistic
deleted file mode 100644 (file)
index 4ffc78e..0000000
--- a/Artistic
+++ /dev/null
@@ -1,125 +0,0 @@
-                      The "Artistic License"
-
-                             Preamble
-
-The intent of this document is to state the conditions under which a
-Package may be copied, such that the Copyright Holder maintains some
-semblance of artistic control over the development of the Package,
-while giving the users of the package the right to use and distribute
-the Package in a more-or-less customary fashion, plus the right to make
-reasonable modifications.
-
-It also grants you the rights to reuse parts of a Package in your own
-programs without transferring this License to those programs, provided
-that you meet some reasonable requirements.
-
-Definitions:
-
-        "Package" refers to the collection of files distributed by the
-        Copyright Holder, and derivatives of that collection of files
-        created through textual modification.
-
-        "Standard Version" refers to such a Package if it has not been
-        modified, or has been modified in accordance with the wishes
-        of the Copyright Holder as specified below.
-
-        "Copyright Holder" is whoever is named in the copyright or
-        copyrights for the package.
-
-        "You" is you, if you're thinking about copying or distributing
-        this Package.
-
-        "Reasonable copying fee" is whatever you can justify on the
-        basis of media cost, duplication charges, time of people involved,
-        and so on.  (You will not be required to justify it to the
-        Copyright Holder, but only to the computing community at large
-        as a market that must bear the fee.)
-
-        "Freely Available" means that no fee is charged for the item
-        itself, though there may be fees involved in handling the item.
-        It also means that recipients of the item may redistribute it
-        under the same conditions they received it.
-
-1. You may make and give away verbatim copies of the source form of the
-Standard Version of this Package without restriction, provided that you
-duplicate all of the original copyright notices and associated disclaimers.
-
-2. You may apply bug fixes, portability fixes and other modifications
-derived from the Public Domain or from the Copyright Holder.  A Package
-modified in such a way shall still be considered the Standard Version.
-
-3. You may otherwise modify your copy of this Package in any way, provided
-that you insert a prominent notice in each changed file stating how and
-when you changed that file, and provided that you do at least ONE of the
-following:
-
-    a) place your modifications in the Public Domain or otherwise make them
-    Freely Available, such as by posting said modifications to Usenet or
-    an equivalent medium, or placing the modifications on a major archive
-    site such as uunet.uu.net, or by allowing the Copyright Holder to include
-    your modifications in the Standard Version of the Package.
-
-    b) use the modified Package only within your corporation or organization.
-
-    c) rename any non-standard executables so the names do not conflict
-    with standard executables, which must also be provided, and provide
-    a separate manual page for each non-standard executable that clearly
-    documents how it differs from the Standard Version.
-
-    d) make other distribution arrangements with the Copyright Holder.
-
-4. You may distribute the programs of this Package in object code or
-executable form, provided that you do at least ONE of the following:
-
-    a) distribute a Standard Version of the executables and library files,
-    together with instructions (in the manual page or equivalent) on where
-    to get the Standard Version.
-
-    b) accompany the distribution with the machine-readable source of
-    the Package with your modifications.
-
-    c) give non-standard executables non-standard names, and clearly
-    document the differences in manual pages (or equivalent), together
-    with instructions on where to get the Standard Version.
-
-    d) make other distribution arrangements with the Copyright Holder.
-
-5. You may charge a reasonable copying fee for any distribution of this
-Package.  You may charge any fee you choose for support of this
-Package.  You may not charge a fee for this Package itself.  However,
-you may distribute this Package in aggregate with other (possibly
-commercial) programs as part of a larger (possibly commercial) software
-distribution provided that you do not advertise this Package as a
-product of your own.
-
-6. The scripts and library files supplied as input to or produced as
-output from the programs of this Package do not automatically fall
-under the copyright of this Package, but belong to whomever generated
-them, and may be sold commercially, and may be aggregated with this
-Package.  If such scripts or library files are aggregated with this
-Package via the so-called "undump" or "unexec" methods of producing a
-binary executable image, then distribution of such an image shall
-neither be construed as a distribution of this Package nor shall it
-fall under the restrictions of Paragraphs 3 and 4, provided that you do
-not represent such an executable image as a Standard Version of this
-Package.
-
-7. You may reuse parts of this Package in your own programs, provided that
-you explicitly state where you got them from, in the source code (and, left
-to your courtesy, in the documentation), duplicating all the associated
-copyright notices and disclaimers. Besides your changes, if any, must be
-clearly marked as such. Parts reused that way will no longer fall under this
-license if, and only if, the name of your program(s) have no immediate
-connection with the name of the Package itself or its associated programs.
-You may then apply whatever restrictions you wish on the reused parts or
-choose to place them in the Public Domain--this will apply only within the
-context of your package.
-
-8. The name of the Copyright Holder may not be used to endorse or promote
-products derived from this software without specific prior written permission.
-
-9. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
-IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
-WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
-
-                                The End
diff --git a/CREDITS b/CREDITS
deleted file mode 100644 (file)
index d89f4f5..0000000
--- a/CREDITS
+++ /dev/null
@@ -1,110 +0,0 @@
-Thanks to Matt Simerson <matt@michweb.net> of MichWeb Inc. for documentation
-and pre-release testing.  Without his help the documentation in 1.0.0
-release would have consisted of a single screenfull of text.
-(To clear up some misunderstanding, Matt did not write the current
-documentation.)
-
-Steve Cleff <cleff@yahoo.com> did the default background image in 1.0.x and
-is also the creator of Freeside's elusive mascot, Snakeman, who we hope will
-make an appearance in an upcoming version.
-
-Jerry St. Pierre <jstpi@city.timmins.on.ca> did the "SISD" graphic used in
-1.0.x and most of 1.1.x.
-
-Mark Norris of Urban Design, Inc. <http://www.urban.com/> did the red "S"
-logo for later 1.1.x versions and 1.2.x
-
-Brian McCane? <bmccane@maxbaud.net> contributed PostgreSQL support, HTML
-style enhancements and many, many bugfixes.
-
-Cerkit <cerkit@alfheim.net> contributed rsync support and desynced hosts.
-His changes will hopefully be included in an upcoming version.
-
-CompleteHOST, Inc. (http://www.completehost.com) funded the development of the
-following features:
-  - Multiple, separate databases and configurations on one box.
-  - Per-customer pricing (custom packages)
-  - Internationalization wrt addresses (cust_main, cust_main_county)
-Thanks!
-
-Mark Williamson <mark.williamson@ebbs.com.au> and Roger Mangraviti
-<rem@atu.com.au> contributed state/provence listings for Australia.
-
-Peter Wemm <peter@netplex.com.au> sent in a bunch of bugfixes for the 1.2
-release.
-
-Greg Kuhnert <gregk@no1.com.au> sent some documentation updates.
-
-Joel Griffiths <griff@aver-computer.com> contribued many bugfixes as well as
-the print-batch script.
-
-NetLoud <http://www.netloud.com/> funded the development of the following
-features:
-  - IEAK support for the signup server
-  - Pre-payment support
-
-NetAcces.Net (not netaccess.net) funded the development of the following
-features:
-  - DNS tracking and export to BIND configuration files
-  - Web site virtual host tracking and export to Apache configuration files
-
-Kristian Hoffmann <khoff@pc-intouch.com> contributed Netscape CCK
-autoconfiguration support for the signup server, lots of great mailing
-lists posts which I shamelessly made into documentation, fixes to get rid of
-the embarassing and non-database-normal "owed" field, and many other things
-I'm forgetting.
-
-Jeff Finucane <jeff@cmh.net> send in a bunch of bugfixes (for the sendmail
-export, cancel-unaudited.cgi), patches to support billing date modification,
-and probably other things too (sorry if I forgot them).  And yet even more
-bug squashing, thanks!  *and* he single-handedly implemented all the necessary
-work to get rid of svc_acct_sm and the "default domain"  thanks!!  and rewrote
-the financials!  wow, thanks jeff!  and contributed financial reports!
-
-Kenny Elliott <kenny@neoserve.com> contributed ICRADIUS radreply table support,
-allowing attributes with ICRADIUS, helped fix many bugs, and some
-other stuff I can't recall (sorry).
-
-Stephen Amadei <amadei@dandy.net> contribued portability cleanups for the
-low-level DBI stuff.
-
-Jason Spence <thalakan@frys.com> contributed admin.html and other
-documentation, autocapnames javascript, bugfixes & other neat stuff I can't
-remember.
-
-Brad Dameron <bdameron@tscnet.com> contributed code to do configurable state
-and referral defaults.
-
-Surf and Sip, Inc., <http://www.surfandsip.com> sponsored a long-requested
-feature - the session monitor and time-based prepaid cards.
-Matt Peterson <matt@peterson.org> and Mack ? <mackn@mackn.net> tested
-the new features and contributed many bugfixes.
-
-Landel Telecom <http://www.landel.com/> sponsored shipping addresses and
-customer notes, as well as an update of the CP provisioning.
-
-nikotel, Inc. <http://www.nikojet.com> sponsored the inclusion of
-customer-to-customer referrals in the web interface and signup server.
-
-Three Bubba's Innanet <http://www.inna.net> sponsored expedited check entry,
-the "similar names warning" feature, and a number of other enhancements.
-
-Dave Burgess <burgess@neonramp.com> sent in a bunch of fixes and small changes
-and will doubtless send more once he's got his tree under control.
-
-Luke Pfeifer <freeside@globalli.com> contributed the "subscription" price plan.
-
-Noment Networks, LLC <http://www.noment.com/> sponsored ICRADIUS/FreeRADIUS
-groups, message catalogs, and signup server enhancements.
-
-Donald Greer <dgreer@austintx.com> provided the SQL to work around MySQL's lack
-of subqueries, and Dale Hege <fhege@lumenexus.net> provided the patches.
-Thanks!
-
-<baloo@gimpgirl.com> sent in several documentation patches.
-
-"Stephen Bechard" <steve@destek.net> sent in patches for svc_www services and
-other fixes.
-
-Everything else is my (Ivan Kohler <ivan@420.am>) fault.
-
diff --git a/FS/Changes b/FS/Changes
deleted file mode 100644 (file)
index c94ef10..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-Revision history for Perl extension FS.
-
-0.01  Wed Aug  4 00:13:45 1999
-       - original version; created by h2xs 1.19
-
diff --git a/FS/FS.pm b/FS/FS.pm
deleted file mode 100644 (file)
index 963c735..0000000
--- a/FS/FS.pm
+++ /dev/null
@@ -1,231 +0,0 @@
-package FS;
-
-use strict;
-use vars qw($VERSION);
-
-$VERSION = '0.01';
-
-#find missing entries in this file with:
-# for a in `ls *pm | cut -d. -f1`; do grep 'L<FS::'$a'>' ../FS.pm >/dev/null || echo "missing $a" ; done
-
-1;
-__END__
-
-=head1 NAME
-
-FS - Freeside Perl modules
-
-=head1 SYNOPSIS
-
-Freeside perl modules and CLI utilities.
-
-=head2 Utility classes
-
-L<FS::Conf> - Freeside configuration values
-
-L<FS::ConfItem> - Freeside configuration option meta-data.
-
-L<FS::UID> - User class (not yet OO)
-
-L<FS::CGI> - Non OO-subroutines for the web interface.
-
-L<FS::Msgcat> - Message catalog
-
-L<FS::SearchCache> - Search cache
-
-L<FS::raddb> - RADIUS dictionary
-
-=head2 Database record classes
-
-L<FS::Record> - Database record base class
-
-L<FS::svc_acct_pop> - POP (Point of Presence, not Post
-Office Protocol) class
-
-L<FS::part_pop_local> - Local calling area class
-
-L<FS::part_referral> - Referral class
-
-L<FS::cust_main_county> - Locale (tax rate) class
-
-L<FS::cust_tax_exempt> - Tax exemption record class
-
-L<FS::svc_Common> - Service base class
-
-L<FS::svc_acct> - Account (shell, RADIUS, POP3) class
-
-L<FS::radius_usergroup> - RADIUS groups
-
-L<FS::svc_domain> - Domain class
-
-L<FS::domain_record> - DNS zone entries
-
-L<FS::svc_forward> - Mail forwarding class
-
-L<FS::svc_acct_sm> - (Depreciated) Vitual mail alias class
-
-L<FS::svc_www> - Web virtual host class.
-
-L<FS::part_svc> - Service definition class
-
-L<FS::part_svc_column> - Column constraint class
-
-L<FS::export_svc> - Class linking service definitions (see L<FS::part_svc>)
-with exports (see L<FS::part_export>)
-
-L<FS::part_export> - External provisioning export class
-
-L<FS::part_export_option> - Export option class
-
-L<FS::part_pkg> - Package (billing item) definition class
-
-L<FS::pkg_svc> - Class linking package (billing item)
-definitions (see L<FS::part_pkg>) with service definitions
-(see L<FS::part_svc>)
-
-L<FS::agent> - Agent (reseller) class
-
-L<FS::agent_type> - Agent type class
-
-L<FS::type_pkgs> - Class linking agent types (see
-L<FS::agent_type>) with package (billing item) definitions
-(see L<FS::part_pkg>)
-
-L<FS::cust_svc> - Service class
-
-L<FS::cust_pkg> - Package (billing item) class
-
-L<FS::cust_main> - Customer class
-
-L<FS::cust_main_invoice> - Invoice destination
-class
-
-L<FS::cust_bill> - Invoice class
-
-L<FS::cust_bill_pkg> - Invoice line item class
-
-L<FS::part_bill_event> - Invoice event definition class
-
-L<FS::cust_bill_event> - Completed invoice event class
-
-L<FS::cust_pay> - Payment class
-
-L<FS::cust_bill_pay> - Payment application class
-
-L<FS::cust_credit> - Credit class
-
-L<FS::cust_refund> - Refund class
-
-L<FS::cust_credit_refund> - Refund application class
-
-L<FS::cust_credit_bill> - Credit invoice application class
-
-L<FS::cust_pay_batch> - Credit card transaction queue class
-
-L<FS::prepay_credit> - Prepaid "calling card" credit class.
-
-L<FS::nas> - Network Access Server class
-
-L<FS::port> - NAS port class
-
-L<FS::session> - User login session class
-
-L<FS::queue> - Job queue
-
-L<FS::queue_arg> - Job arguments
-
-L<FS::queue_depend> - Job dependencies
-
-L<FS::msgcat> - Message catalogs
-
-=head1 Remote API modules
-
-L<FS::SignupClient>
-
-L<FS::SessionClient>
-
-L<FS::MailAdminServer>
-
-=head2 Command-line utilities
-
-L<freeside-adduser>
-
-L<freeside-queued>
-
-L<freeside-daily>
-
-L<freeside-expiration-alerter>
-
-L<freeside-email>
-
-L<freeside-cc-receipts-report>
-
-L<freeside-credit-report>
-
-L<freeside-receivables-report>
-
-L<freeside-tax-report>
-
-L<freeside-bill>
-
-L<freeside-overdue>
-
-=head2 User Interface classes (under (stalled) development; not yet usable)
-
-L<FS::UI::Base> - User-interface base class
-
-L<FS::UI::Gtk> - Gtk user-interface class
-
-L<FS::UI::CGI> - CGI (HTML) user-interface class
-
-L<FS::UI::agent> - agent table user-interface class
-
-=head2 Notes
-
-To quote perl(1), "If you're intending to read these straight through for the
-first time, the suggested order will tend to reduce the number of forward
-references."
-
-If you've never used OO modules before,
-http://www.cpan.org/doc/FMTEYEWTK/easy_objects.html might help you out.
-
-=head1 DESCRIPTION
-
-Freeside is a billing and administration package for Internet Service
-Providers.
-
-The Freeside home page is at <http://www.sisd.com/freeside>.
-
-The main documentation is in httemplate/docs.
-
-=head1 SUPPORT
-
-A mailing list for users is available.  Send a blank message to
-<ivan-freeside-subscribe@sisd.com> to subscribe.
-
-A mailing list for developers is available.  It is intended to be lower volume
-and higher SNR than the users list.  Send a blank message to
-<ivan-freeside-devel-subscribe@sisd.com> to subscribe.
-
-Commercial support is available; see
-<http://www.sisd.com/freeside/commercial.html>.
-
-=head1 AUTHOR
-
-Primarily Ivan Kohler <ivan@sisd.com>, with help from many kind folks.
-
-See the CREDITS file in the Freeside distribution for a (hopefully) complete
-list and the individal files for details.
-
-=head1 SEE ALSO
-
-perl(1), main Freeside documentation in htdocs/docs/
-
-=head1 BUGS
-
-Those modules which would be useful separately should be pulled out, 
-renamed appropriately and uploaded to CPAN.  So far: DBIx::DBSchema, Net::SSH
-and Net::SCP...
-
-=cut
-
diff --git a/FS/FS/CGI.pm b/FS/FS/CGI.pm
deleted file mode 100644 (file)
index e44ebcc..0000000
+++ /dev/null
@@ -1,314 +0,0 @@
-package FS::CGI;
-
-use strict;
-use vars qw(@EXPORT_OK @ISA);
-use Exporter;
-use CGI;
-use URI::URL;
-#use CGI::Carp qw(fatalsToBrowser);
-use FS::UID;
-
-@ISA = qw(Exporter);
-@EXPORT_OK = qw(header menubar idiot eidiot popurl table itable ntable
-                small_custview myexit);
-
-=head1 NAME
-
-FS::CGI - Subroutines for the web interface
-
-=head1 SYNOPSIS
-
-  use FS::CGI qw(header menubar idiot eidiot popurl);
-
-  print header( 'Title', '' );
-  print header( 'Title', menubar('item', 'URL', ... ) );
-
-  idiot "error message"; 
-  eidiot "error message";
-
-  $url = popurl; #returns current url
-  $url = popurl(3); #three levels up
-
-=head1 DESCRIPTION
-
-Provides a few common subroutines for the web interface.
-
-=head1 SUBROUTINES
-
-=over 4
-
-=item header TITLE, MENUBAR
-
-Returns an HTML header.
-
-=cut
-
-sub header {
-  my($title,$menubar,$etc)=@_; #$etc is for things like onLoad= etc.
-  #use Carp;
-  $etc = '' unless defined $etc;
-
-  my $x =  <<END;
-    <HTML>
-      <HEAD>
-        <TITLE>
-          $title
-        </TITLE>
-        <META HTTP-Equiv="Cache-Control" Content="no-cache">
-        <META HTTP-Equiv="Pragma" Content="no-cache">
-        <META HTTP-Equiv="Expires" Content="0"> 
-      </HEAD>
-      <BODY BGCOLOR="#e8e8e8"$etc>
-          <FONT SIZE=7>
-            $title
-          </FONT>
-          <BR><BR>
-END
-  $x .=  $menubar. "<BR><BR>" if $menubar;
-  $x;
-}
-
-=item menubar ITEM, URL, ...
-
-Returns an HTML menubar.
-
-=cut
-
-sub menubar { #$menubar=menubar('Main Menu', '../', 'Item', 'url', ... );
-  my($item,$url,@html);
-  while (@_) {
-    ($item,$url)=splice(@_,0,2);
-    push @html, qq!<A HREF="$url">$item</A>!;
-  }
-  join(' | ',@html);
-}
-
-=item idiot ERROR
-
-This is depriciated.  Don't use it.
-
-Sends an HTML error message.
-
-=cut
-
-sub idiot {
-  #warn "idiot depriciated";
-  my($error)=@_;
-#  my $cgi = &FS::UID::cgi();
-#  if ( $cgi->isa('CGI::Base') ) {
-#    no strict 'subs';
-#    &CGI::Base::SendHeaders;
-#  } else {
-#    print $cgi->header( @FS::CGI::header );
-#  }
-  print <<END;
-<HTML>
-  <HEAD>
-    <TITLE>Error processing your request</TITLE>
-    <META HTTP-Equiv="Cache-Control" Content="no-cache">
-    <META HTTP-Equiv="Pragma" Content="no-cache">
-    <META HTTP-Equiv="Expires" Content="0"> 
-  </HEAD>
-  <BODY>
-    <CENTER>
-    <H4>Error processing your request</H4>
-    </CENTER>
-    Your request could not be processed because of the following error:
-    <P><B>$error</B>
-  </BODY>
-</HTML>
-END
-
-}
-
-=item eidiot ERROR
-
-This is depriciated.  Don't use it.
-
-Sends an HTML error message, then exits.
-
-=cut
-
-sub eidiot {
-  warn "eidiot depriciated";
-  $HTML::Mason::Commands::r->send_http_header
-    if defined $HTML::Mason::Commands::r;
-  idiot(@_);
-  &myexit();
-}
-
-=item myexit
-
-You probably shouldn't use this; but if you must:
-
-If running under mod_perl, calles Apache::exit, otherwise, calls exit.
-
-=cut
-
-sub myexit {
-  if (exists $ENV{MOD_PERL}) {
-
-    if ( defined $main::Response
-         && $main::Response->isa('Apache::ASP::Response') ) {  #Apache::ASP
-      $main::Response->End();
-      require Apache;
-      Apache::exit();
-    } elsif ( defined $HTML::Mason::Commands::m  ) { #Mason
-      #$HTML::Mason::Commands::m->flush_buffer();
-      $HTML::Mason::Commands::m->abort();
-      die "shouldn't fall through to here (mason \$m->abort didn't)";
-    } else {
-      #??? well, it is $ENV{MOD_PERL}
-      warn "running under unknown mod_perl environment; trying Apache::exit()";
-      require Apache;
-      Apache::exit();
-    }
-  } else {
-    exit;
-  }
-}
-
-=item popurl LEVEL
-
-Returns current URL with LEVEL levels of path removed from the end (default 0).
-
-=cut
-
-sub popurl {
-  my($up)=@_;
-  my $cgi = &FS::UID::cgi;
-  my $url = new URI::URL ( $cgi->isa('Apache') ? $cgi->uri : $cgi->url );
-  my(@path)=$url->path_components;
-  splice @path, 0-$up;
-  $url->path_components(@path);
-  my $x = $url->as_string;
-  $x .= '/' unless $x =~ /\/$/;
-  $x;
-}
-
-=item table
-
-Returns HTML tag for beginning a table.
-
-=cut
-
-sub table {
-  my $col = shift;
-  if ( $col ) {
-    qq!<TABLE BGCOLOR="$col" BORDER=1 WIDTH="100%" CELLSPACING=0 CELLPADDING=2 BORDERCOLOR="#999999">!;
-  } else { 
-    '<TABLE BORDER=1 CELLSPACING=0 CELLPADDING=2 BORDERCOLOR="#999999">';
-  }
-}
-
-=item itable
-
-Returns HTML tag for beginning an (invisible) table.
-
-=cut
-
-sub itable {
-  my $col = shift;
-  my $cellspacing = shift || 0;
-  if ( $col ) {
-    qq!<TABLE BGCOLOR="$col" BORDER=0 CELLSPACING=$cellspacing WIDTH="100%">!;
-  } else {
-    qq!<TABLE BORDER=0 CELLSPACING=$cellspacing WIDTH="100%">!;
-  }
-}
-
-=item ntable
-
-This is getting silly.
-
-=cut
-
-sub ntable {
-  my $col = shift;
-  my $cellspacing = shift || 0;
-  if ( $col ) {
-    qq!<TABLE BGCOLOR="$col" BORDER=0 CELLSPACING=$cellspacing>!;
-  } else {
-    '<TABLE BORDER CELLSPACING=0 CELLPADDING=2 BORDERCOLOR="#999999">';
-  }
-
-}
-
-=item small_custview CUSTNUM || CUST_MAIN_OBJECT, COUNTRYDEFAULT
-
-Sheesh. I should just switch to Mason.
-
-=cut
-
-sub small_custview {
-  use FS::Record qw(qsearchs);
-  use FS::cust_main;
-
-  my $arg = shift;
-  my $countrydefault = shift || 'US';
-
-  my $cust_main = ref($arg) ? $arg
-                  : qsearchs('cust_main', { 'custnum' => $arg } )
-    or die "unknown custnum $arg";
-
-  my $html = 'Customer #<B>'. $cust_main->custnum. '</B>'.
-    ntable('#e8e8e8'). '<TR><TD>'. ntable("#cccccc",2).
-    '<TR><TD ALIGN="right" VALIGN="top">Billing<BR>Address</TD><TD BGCOLOR="#ffffff">'.
-    $cust_main->getfield('last'). ', '. $cust_main->first. '<BR>';
-
-  $html .= $cust_main->company. '<BR>' if $cust_main->company;
-  $html .= $cust_main->address1. '<BR>';
-  $html .= $cust_main->address2. '<BR>' if $cust_main->address2;
-  $html .= $cust_main->city. ', '. $cust_main->state. '  '. $cust_main->zip. '<BR>';
-  $html .= $cust_main->country. '<BR>'
-    if $cust_main->country && $cust_main->country ne $countrydefault;
-
-  $html .= '</TD></TR></TABLE></TD>';
-
-  if ( defined $cust_main->dbdef_table->column('ship_last') ) {
-
-    my $pre = $cust_main->ship_last ? 'ship_' : '';
-
-    $html .= '<TD>'. ntable("#cccccc",2).
-      '<TR><TD ALIGN="right" VALIGN="top">Service<BR>Address</TD><TD BGCOLOR="#ffffff">'.
-      $cust_main->get("${pre}last"). ', '.
-      $cust_main->get("${pre}first"). '<BR>';
-    $html .= $cust_main->get("${pre}company"). '<BR>'
-      if $cust_main->get("${pre}company");
-    $html .= $cust_main->get("${pre}address1"). '<BR>';
-    $html .= $cust_main->get("${pre}address2"). '<BR>'
-      if $cust_main->get("${pre}address2");
-    $html .= $cust_main->get("${pre}city"). ', '.
-             $cust_main->get("${pre}state"). '  '.
-             $cust_main->get("${pre}ship_zip"). '<BR>';
-    $html .= $cust_main->get("${pre}country"). '<BR>'
-      if $cust_main->get("${pre}country")
-         && $cust_main->get("${pre}country") ne $countrydefault;
-
-    $html .= '</TD></TR></TABLE></TD>';
-  }
-
-  $html .= '</TR></TABLE>';
-
-  $html;
-}
-
-=back
-
-=head1 BUGS
-
-Not OO.
-
-Not complete.
-
-small_custview sooooo doesn't belong here.  i should just switch to Mason.
-
-=head1 SEE ALSO
-
-L<CGI>, L<CGI::Base>
-
-=cut
-
-1;
-
-
diff --git a/FS/FS/ClientAPI.pm b/FS/FS/ClientAPI.pm
deleted file mode 100644 (file)
index f7b8eb0..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-package FS::ClientAPI;
-
-use strict;
-use vars qw(%handler);
-
-%handler = ();
-
-#find modules
-foreach my $INC ( @INC ) {
-  foreach my $file ( glob("$INC/FS/ClientAPI/*") ) {
-    $file =~ /\/(\w+)\.pm$/ or do {
-      warn "unrecognized ClientAPI file: $file";
-      next
-    };
-    my $mod = $1;
-    #warn "using FS::ClientAPI::$mod";
-    eval "use FS::ClientAPI::$mod;";
-    die "error using FS::ClientAPI::$mod: $@" if $@;
-  }
-}
-
-#(sub for modules)
-sub register_handlers {
-  my $self = shift;
-  my %new_handlers = @_;
-  foreach my $key ( keys %new_handlers ) {
-    warn "WARNING: redefining sub $key" if exists $handler{$key};
-    #warn "registering $key";
-    $handler{$key} = $new_handlers{$key};
-  }
-}
-
-#---
-
-sub dispatch {
-  my ( $self, $name ) = ( shift, shift );
-  my $sub = $handler{$name}
-    or die "unknown FS::ClientAPI sub $name (known: ". join(" ", keys %handler );
-    #or die "unknown FS::ClientAPI sub $name";
-  &{$sub}(@_);
-}
-
-1;
-
diff --git a/FS/FS/ClientAPI/MyAccount.pm b/FS/FS/ClientAPI/MyAccount.pm
deleted file mode 100644 (file)
index 6747855..0000000
+++ /dev/null
@@ -1,136 +0,0 @@
-package FS::ClientAPI::MyAccount;
-
-use strict;
-use vars qw($cache);
-use Digest::MD5 qw(md5_hex);
-use Date::Format;
-use Cache::SharedMemoryCache; #store in db?
-use FS::CGI qw(small_custview); #doh
-use FS::Conf;
-use FS::Record qw(qsearchs);
-use FS::svc_acct;
-use FS::svc_domain;
-use FS::cust_main;
-use FS::cust_bill;
-
-use FS::ClientAPI; #hmm
-FS::ClientAPI->register_handlers(
-  'MyAccount/login'         => \&login,
-  'MyAccount/customer_info' => \&customer_info,
-  'MyAccount/invoice'       => \&invoice,
-);
-
-#store in db?
-my $cache = new Cache::SharedMemoryCache();
-
-#false laziness w/FS::ClientAPI::passwd::passwd (needs to handle encrypted pw)
-sub login {
-  my $p = shift;
-
-  my $svc_domain = qsearchs('svc_domain', { 'domain' => $p->{'domain'} } )
-    or return { error => "Domain not found" };
-
-  my $svc_acct =
-    ( length($p->{'password'}) < 13
-      && qsearchs( 'svc_acct', { 'username'  => $p->{'username'},
-                                 'domsvc'    => $svc_domain->svcnum,
-                                 '_password' => $p->{'password'}     } )
-    )
-    || qsearchs( 'svc_acct', { 'username'  => $p->{'username'},
-                               'domsvc'    => $svc_domain->svcnum,
-                               '_password' => $p->{'password'}     } );
-
-  unless ( $svc_acct ) { return { error => 'Incorrect password.' } }
-
-  my $session = {
-    'svcnum' => $svc_acct->svcnum,
-  };
-
-  my $cust_pkg = $svc_acct->cust_svc->cust_pkg;
-  if ( $cust_pkg ) {
-    my $cust_main = $cust_pkg->cust_main;
-    $session->{'custnum'} = $cust_main->custnum;
-  }
-
-  my $session_id;
-  do {
-    $session_id = md5_hex(md5_hex(time(). {}. rand(). $$))
-  } until ( ! defined $cache->get($session_id) ); #just in case
-
-  $cache->set( $session_id, $session, '1 hour' );
-
-  return { 'error'      => '',
-           'session_id' => $session_id,
-         };
-}
-
-sub customer_info {
-  my $p = shift;
-  my $session = $cache->get($p->{'session_id'})
-    or return { 'error' => "Can't resume session" }; #better error message
-
-  my %return;
-
-  my $custnum = $session->{'custnum'};
-
-  if ( $custnum ) { #customer record
-
-    my $cust_main = qsearchs('cust_main', { 'custnum' => $custnum } )
-      or return { 'error' => "unknown custnum $custnum" };
-
-    $return{balance} = $cust_main->balance;
-
-    my @open = map {
-                     {
-                       invnum => $_->invnum,
-                       date   => time2str("%b %o, %Y", $_->_date),
-                       owed   => $_->owed,
-                     };
-                   } $cust_main->open_cust_bill;
-    $return{open_invoices} = \@open;
-
-    my $conf = new FS::Conf;
-    $return{small_custview} =
-      small_custview( $cust_main, $conf->config('defaultcountry') );
-
-    $return{name} = $cust_main->first. ' '. $cust_main->get('last');
-
-  } else { #no customer record
-
-    my $svc_acct = qsearchs('svc_acct', { 'svcnum' => $session->{'svcnum'} } )
-      or die "unknown svcnum";
-    $return{name} = $svc_acct->email;
-
-  }
-
-
-  return { 'error'          => '',
-           'custnum'        => $custnum,
-           %return,
-         };
-
-}
-
-sub invoice {
-  my $p = shift;
-  my $session = $cache->get($p->{'session_id'})
-    or return { 'error' => "Can't resume session" }; #better error message
-
-  my $custnum = $session->{'custnum'};
-
-  my $invnum = $p->{'invnum'};
-
-  my $cust_bill = qsearchs('cust_bill', { 'invnum'  => $invnum,
-                                          'custnum' => $custnum } )
-    or return { 'error' => "Can't find invnum" };
-
-  #my %return;
-
-  return { 'error'        => '',
-           'invnum'       => $invnum,
-           'invoice_text' => join('', $cust_bill->print_text ),
-         };
-
-}
-
-
diff --git a/FS/FS/ClientAPI/passwd.pm b/FS/FS/ClientAPI/passwd.pm
deleted file mode 100644 (file)
index 2960622..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-package FS::ClientAPI::passwd;
-
-use strict;
-use FS::Record qw(qsearchs);
-use FS::svc_acct;
-#use FS::svc_domain;
-
-use FS::ClientAPI; #hmm
-FS::ClientAPI->register_handlers(
-  'passwd/passwd' => \&passwd,
-  'passwd/chfn' => \&chfn,
-  'passwd/chsh' => \&chsh,
-);
-
-sub passwd {
-  my $packet = shift;
-
-  #my $domain = qsearchs('svc_domain', { 'domain' => $packet->{'domain'} } )
-  #  or return { error => "Domain $domain not found" };
-
-  my $old_password = $packet->{'old_password'};
-  my $new_password = $packet->{'new_password'};
-  my $new_gecos = $packet->{'new_gecos'};
-  my $new_shell = $packet->{'new_shell'};
-
-#false laziness w/FS::ClientAPI::MyAccount::login (needs to handle encrypted pw)
-  my $svc_acct =
-    ( length($old_password) < 13
-      && qsearchs( 'svc_acct', { 'username'  => $packet->{'username'},
-                                 #'domsvc'    => $svc_domain->svcnum,
-                                 '_password' => $old_password } )
-    )
-    || qsearchs( 'svc_acct', { 'username'  => $packet->{'username'},
-                               #'domsvc'    => $svc_domain->svcnum,
-                               '_password' => $old_password } );
-
-  unless ( $svc_acct ) { return { error => 'Incorrect password.' } }
-
-  my %hash = $svc_acct->hash;
-  my $new_svc_acct = new FS::svc_acct ( \%hash );
-  $new_svc_acct->setfield('_password', $new_password ) 
-    if $new_password && $new_password ne $old_password;
-  $new_svc_acct->setfield('finger',$new_gecos) if $new_gecos;
-  $new_svc_acct->setfield('shell',$new_shell) if $new_shell;
-  my $error = $new_svc_acct->replace($svc_acct);
-
-  return { error => $error };
-
-}
-
-sub chfn {}
-
-sub chsh {}
-
-1;
-
diff --git a/FS/FS/Conf.pm b/FS/FS/Conf.pm
deleted file mode 100644 (file)
index e93eaf3..0000000
+++ /dev/null
@@ -1,972 +0,0 @@
-package FS::Conf;
-
-use vars qw($default_dir @config_items $DEBUG );
-use IO::File;
-use File::Basename;
-use FS::ConfItem;
-
-$DEBUG = 0;
-
-=head1 NAME
-
-FS::Conf - Freeside configuration values
-
-=head1 SYNOPSIS
-
-  use FS::Conf;
-
-  $conf = new FS::Conf "/config/directory";
-
-  $FS::Conf::default_dir = "/config/directory";
-  $conf = new FS::Conf;
-
-  $dir = $conf->dir;
-
-  $value = $conf->config('key');
-  @list  = $conf->config('key');
-  $bool  = $conf->exists('key');
-
-  $conf->touch('key');
-  $conf->set('key' => 'value');
-  $conf->delete('key');
-
-  @config_items = $conf->config_items;
-
-=head1 DESCRIPTION
-
-Read and write Freeside configuration values.  Keys currently map to filenames,
-but this may change in the future.
-
-=head1 METHODS
-
-=over 4
-
-=item new [ DIRECTORY ]
-
-Create a new configuration object.  A directory arguement is required if
-$FS::Conf::default_dir has not been set.
-
-=cut
-
-sub new {
-  my($proto,$dir) = @_;
-  my($class) = ref($proto) || $proto;
-  my($self) = { 'dir' => $dir || $default_dir } ;
-  bless ($self, $class);
-}
-
-=item dir
-
-Returns the directory.
-
-=cut
-
-sub dir {
-  my($self) = @_;
-  my $dir = $self->{dir};
-  -e $dir or die "FATAL: $dir doesn't exist!";
-  -d $dir or die "FATAL: $dir isn't a directory!";
-  -r $dir or die "FATAL: Can't read $dir!";
-  -x $dir or die "FATAL: $dir not searchable (executable)!";
-  $dir =~ /^(.*)$/;
-  $1;
-}
-
-=item config KEY
-
-Returns the configuration value or values (depending on context) for key.
-
-=cut
-
-sub config {
-  my($self,$file)=@_;
-  my($dir)=$self->dir;
-  my $fh = new IO::File "<$dir/$file" or return;
-  if ( wantarray ) {
-    map {
-      /^(.*)$/
-        or die "Illegal line (array context) in $dir/$file:\n$_\n";
-      $1;
-    } <$fh>;
-  } else {
-    <$fh> =~ /^(.*)$/
-      or die "Illegal line (scalar context) in $dir/$file:\n$_\n";
-    $1;
-  }
-}
-
-=item exists KEY
-
-Returns true if the specified key exists, even if the corresponding value
-is undefined.
-
-=cut
-
-sub exists {
-  my($self,$file)=@_;
-  my($dir) = $self->dir;
-  -e "$dir/$file";
-}
-
-=item touch KEY
-
-Creates the specified configuration key if it does not exist.
-
-=cut
-
-sub touch {
-  my($self, $file) = @_;
-  my $dir = $self->dir;
-  unless ( $self->exists($file) ) {
-    warn "[FS::Conf] TOUCH $file\n" if $DEBUG;
-    system('touch', "$dir/$file");
-  }
-}
-
-=item set KEY VALUE
-
-Sets the specified configuration key to the given value.
-
-=cut
-
-sub set {
-  my($self, $file, $value) = @_;
-  my $dir = $self->dir;
-  $value =~ /^(.*)$/s;
-  $value = $1;
-  unless ( join("\n", @{[ $self->config($file) ]}) eq $value ) {
-    warn "[FS::Conf] SET $file\n" if $DEBUG;
-#    warn "$dir" if is_tainted($dir);
-#    warn "$dir" if is_tainted($file);
-    chmod 0644, "$dir/$file";
-    my $fh = new IO::File ">$dir/$file" or return;
-    chmod 0644, "$dir/$file";
-    print $fh "$value\n";
-  }
-}
-#sub is_tainted {
-#             return ! eval { join('',@_), kill 0; 1; };
-#         }
-
-=item delete KEY
-
-Deletes the specified configuration key.
-
-=cut
-
-sub delete {
-  my($self, $file) = @_;
-  my $dir = $self->dir;
-  if ( $self->exists($file) ) {
-    warn "[FS::Conf] DELETE $file\n";
-    unlink "$dir/$file";
-  }
-}
-
-=item config_items
-
-Returns all of the possible configuration items as FS::ConfItem objects.  See
-L<FS::ConfItem>.
-
-=cut
-
-sub config_items {
-  my $self = shift; 
-  #quelle kludge
-  @config_items,
-  map { 
-        my $basename = basename($_);
-        $basename =~ /^(.*)$/;
-        $basename = $1;
-        new FS::ConfItem {
-                           'key'         => $basename,
-                           'section'     => 'billing',
-                           'description' => 'Alternate template file for invoices.  See the <a href="../docs/billing.html">billing documentation</a> for details.',
-                           'type'        => 'textarea',
-                         }
-      } glob($self->dir. '/invoice_template_*')
-  ;
-}
-
-=back
-
-=head1 BUGS
-
-If this was more than just crud that will never be useful outside Freeside I'd
-worry that config_items is freeside-specific and icky.
-
-=head1 SEE ALSO
-
-"Configuration" in the web interface (config/config.cgi).
-
-httemplate/docs/config.html
-
-=cut
-
-@config_items = map { new FS::ConfItem $_ } (
-
-  {
-    'key'         => 'address',
-    'section'     => 'deprecated',
-    'description' => 'This configuration option is no longer used.  See <a href="#invoice_template">invoice_template</a> instead.',
-    'type'        => 'text',
-  },
-
-  {
-    'key'         => 'alerter_template',
-    'section'     => 'billing',
-    'description' => 'Template file for billing method expiration alerts.  See the <a href="../docs/billing.html#invoice_template">billing documentation</a> for details.',
-    'type'        => 'textarea',
-  },
-
-  {
-    'key'         => 'apacheroot',
-    'section'     => 'deprecated',
-    'description' => '<b>DEPRECATED</b>, add a <i>www_shellcommands</i> <a href="../browse/part_export.cgi">export</a> instead.  The directory containing Apache virtual hosts',
-    'type'        => 'text',
-  },
-
-  {
-    'key'         => 'apacheip',
-    'section'     => 'apache',
-    'description' => 'The current IP address to assign to new virtual hosts',
-    'type'        => 'text',
-  },
-
-  {
-    'key'         => 'apachemachine',
-    'section'     => 'deprecated',
-    'description' => '<b>DEPRECATED</b>, add a <i>www_shellcommands</i> <a href="../browse/part_export.cgi">export</a> instead.  A machine with the apacheroot directory and user home directories.  The existance of this file enables setup of virtual host directories, and, in conjunction with the `home\' configuration file, symlinks into user home directories.',
-    'type'        => 'text',
-  },
-
-  {
-    'key'         => 'apachemachines',
-    'section'     => 'apache',
-    'description' => 'Your Apache machines, one per line.  This enables export of `/etc/apache/vhosts.conf\', which can be included in your Apache configuration via the <a href="http://www.apache.org/docs/mod/core.html#include">Include</a> directive.',
-    'type'        => 'textarea',
-  },
-
-  {
-    'key'         => 'bindprimary',
-    'section'     => 'deprecated',
-    'description' => '<b>DEPRECATED</b>, add a <i>bind</i> <a href="../browse/part_export.cgi">export</a> instead.  Your BIND primary nameserver.  This enables export of /var/named/named.conf and zone files into /var/named',
-    'type'        => 'text',
-  },
-
-  {
-    'key'         => 'bindsecondaries',
-    'section'     => 'deprecated',
-    'description' => '<b>DEPRECATED</b>, add a <i>bind_slave</i> <a href="../browse/part_export.cgi">export</a> instead.  Your BIND secondary nameservers, one per line.  This enables export of /var/named/named.conf',
-    'type'        => 'textarea',
-  },
-
-  {
-    'key'         => 'business-onlinepayment',
-    'section'     => 'billing',
-    'description' => '<a href="http://search.cpan.org/search?mode=module&query=Business%3A%3AOnlinePayment">Business::OnlinePayment</a> support, at least three lines: processor, login, and password.  An optional fourth line specifies the action or actions (multiple actions are separated with `,\': for example: `Authorization Only, Post Authorization\').    Optional additional lines are passed to Business::OnlinePayment as %processor_options.',
-    'type'        => 'textarea',
-  },
-
-  {
-    'key'         => 'business-onlinepayment-description',
-    'section'     => 'billing',
-    'description' => 'String passed as the description field to <a href="http://search.cpan.org/search?mode=module&query=Business%3A%3AOnlinePayment">Business::OnlinePayment</a>.  Evaluated as a double-quoted perl string, with the following variables available: <code>$agent</code> (the agent name), and <code>$pkgs</code> (a comma-separated list of packages to which the invoiced being charged applies)',
-    'type'        => 'text',
-  },
-
-  {
-    'key'         => 'bsdshellmachines',
-    'section'     => 'deprecated',
-    'description' => '<b>DEPRECATED</b>, add a <i>bsdshell</i> <a href="../browse/part_export.cgi">export</a> instead.  Your BSD flavored shell (and mail) machines, one per line.  This enables export of `/etc/passwd\' and `/etc/master.passwd\'.',
-    'type'        => 'textarea',
-  },
-
-  {
-    'key'         => 'countrydefault',
-    'section'     => 'UI',
-    'description' => 'Default two-letter country code (if not supplied, the default is `US\')',
-    'type'        => 'text',
-  },
-
-  {
-    'key'         => 'cybercash3.2',
-    'section'     => 'billing',
-    'description' => '<a href="http://www.cybercash.com/cashregister/">CyberCash Cashregister v3.2</a> support.  Two lines: the full path and name of your merchant_conf file, and the transaction type (`mauthonly\' or `mauthcapture\').',
-    'type'        => 'textarea',
-  },
-
-  {
-    'key'         => 'cyrus',
-    'section'     => 'deprecated',
-    'description' => '<b>DEPRECATED</b>, add a <i>cyrus</i> <a href="../browse/part_export.cgi">export</a> instead.  This option used to integrate with <a href="http://asg.web.cmu.edu/cyrus/imapd/">Cyrus IMAP Server</a>, three lines: IMAP server, admin username, and admin password.  Cyrus::IMAP::Admin should be installed locally and the connection to the server secured.',
-    'type'        => 'textarea',
-  },
-
-  {
-    'key'         => 'cp_app',
-    'section'     => 'deprecated',
-    'description' => '<b>DEPRECATED</b>, add a <i>cp</i> <a href="../browse/part_export.cgi">export</a> instead.  This option used to integrate with <a href="http://www.cp.net/">Critial Path Account Provisioning Protocol</a>, four lines: "host:port", username, password, and workgroup (for new users).',
-    'type'        => 'textarea',
-  },
-
-  {
-    'key'         => 'deletecustomers',
-    'section'     => 'UI',
-    'description' => 'Enable customer deletions.  Be very careful!  Deleting a customer will remove all traces that this customer ever existed!  It should probably only be used when auditing a legacy database.  Normally, you cancel all of a customers\' packages if they cancel service.',
-    'type'        => 'checkbox',
-  },
-
-  {
-    'key'         => 'deletepayments',
-    'section'     => 'UI',
-    'description' => 'Enable deletion of unclosed payments.  Be very careful!  Only delete payments that were data-entry errors, not adjustments. Optionally specify one or more comma-separated email addresses to be notified when a payment is deleted.',
-    'type'        => [qw( checkbox text )],
-  },
-
-  {
-    'key'         => 'dirhash',
-    'section'     => 'shell',
-    'description' => 'Optional numeric value to control directory hashing.  If positive, hashes directories for the specified number of levels from the front of the username.  If negative, hashes directories for the specified number of levels from the end of the username.  Some examples: <ul><li>1: user -> <a href="#home">/home</a>/u/user<li>2: user -> <a href="#home">/home</a>/u/s/user<li>-1: user -> <a href="#home">/home</a>/r/user<li>-2: user -> <a href="#home">home</a>/r/e/user</ul>',
-    'type'        => 'text',
-  },
-
-  {
-    'key'         => 'disable_customer_referrals',
-    'section'     => 'UI',
-    'description' => 'Disable new customer-to-customer referrals in the web interface',
-    'type'        => 'checkbox',
-  },
-
-  {
-    'key'         => 'domain',
-    'section'     => 'deprecated',
-    'description' => 'Your domain name.',
-    'type'        => 'text',
-  },
-
-  {
-    'key'         => 'editreferrals',
-    'section'     => 'UI',
-    'description' => 'Enable advertising source modification for existing customers',
-    'type'       => 'checkbox',
-  },
-
-  {
-    'key'         => 'emailinvoiceonly',
-    'section'     => 'billing',
-    'description' => 'Disables postal mail invoices',
-    'type'       => 'checkbox',
-  },
-
-  {
-    'key'         => 'disablepostalinvoicedefault',
-    'section'     => 'billing',
-    'description' => 'Disables postal mail invoices as the default option in the UI.  Be careful not to setup customers which are not sent invoices.  See <a href ="#emailinvoiceauto">emailinvoiceauto</a>.',
-    'type'       => 'checkbox',
-  },
-
-  {
-    'key'         => 'emailinvoiceauto',
-    'section'     => 'billing',
-    'description' => 'Automatically adds new accounts to the email invoice list upon customer creation',
-    'type'       => 'checkbox',
-  },
-
-  {
-    'key'         => 'erpcdmachines',
-    'section'     => '',
-    'description' => 'Your ERPCD authenticaion machines, one per line.  This enables export of `/usr/annex/acp_passwd\' and `/usr/annex/acp_dialup\'',
-    'type'        => 'textarea',
-  },
-
-  {
-    'key'         => 'hidecancelledpackages',
-    'section'     => 'UI',
-    'description' => 'Prevent cancelled packages from showing up in listings (though they will still be in the database)',
-    'type'        => 'checkbox',
-  },
-
-  {
-    'key'         => 'hidecancelledcustomers',
-    'section'     => 'UI',
-    'description' => 'Prevent customers with only cancelled packages from showing up in listings (though they will still be in the database)',
-    'type'        => 'checkbox',
-  },
-
-  {
-    'key'         => 'home',
-    'section'     => 'required',
-    'description' => 'For new users, prefixed to username to create a directory name.  Should have a leading but not a trailing slash.',
-    'type'        => 'text',
-  },
-
-  {
-    'key'         => 'icradiusmachines',
-    'section'     => 'deprecated',
-    'description' => '<b>DEPRECATED</b>, add an <i>sqlradius</i> <a href="../browse/part_export.cgi">export</a> instead.  This option used to enable radcheck and radreply table population - by default in the Freeside database, or in the database specified by the <a href="http://rootwood.haze.st/aspside/config/config-view.cgi#icradius_secrets">icradius_secrets</a> config option (the radcheck and radreply tables needs to be created manually).  You do not need to use MySQL for your Freeside database to export to an ICRADIUS/FreeRADIUS MySQL database with this option.  <blockquote><b>ADDITIONAL DEPRECATED FUNCTIONALITY</b> (instead use <a href="http://www.mysql.com/documentation/mysql/bychapter/manual_MySQL_Database_Administration.html#Replication">MySQL replication</a> or point icradius_secrets to the external database) - your <a href="ftp://ftp.cheapnet.net/pub/icradius">ICRADIUS</a> machines or <a href="http://www.freeradius.org/">FreeRADIUS</a> (with MySQL authentication) machines, one per line.  Machines listed in this file will have the radcheck table exported to them.  Each line should contain four items, separted by whitespace: machine name, MySQL database name, MySQL username, and MySQL password.  For example: <CODE>"radius.isp.tld&nbsp;radius_db&nbsp;radius_user&nbsp;passw0rd"</CODE></blockquote>',
-    'type'        => [qw( checkbox textarea )],
-  },
-
-  {
-    'key'         => 'icradius_mysqldest',
-    'section'     => 'deprecated',
-    'description' => '<b>DEPRECATED</b>, add an <i>sqlradius</i> https://billing.crosswind.net/freeside/browse/part_export.cgi">export</a> instead.  Used to be the destination directory for the MySQL databases, on the ICRADIUS/FreeRADIUS machines.  Defaults to "/usr/local/var/".',
-    'type'        => 'text',
-  },
-
-  {
-    'key'         => 'icradius_mysqlsource',
-    'section'     => 'deprecated',
-    'description' => '<b>DEPRECATED</b>, add an <i>sqlradius</i> https://billing.crosswind.net/freeside/browse/part_export.cgi">export</a> instead.  Used to be the source directory for for the MySQL radcheck table files, on the Freeside machine.  Defaults to "/usr/local/var/freeside".',
-    'type'        => 'text',
-  },
-
-  {
-    'key'         => 'icradius_secrets',
-    'section'     => 'deprecated',
-    'description' => '<b>DEPRECATED</b>, add an <i>sqlradius</i> https://billing.crosswind.net/freeside/browse/part_export.cgi">export</a> instead.  This option used to specify a database for ICRADIUS/FreeRADIUS export.  Three lines: DBI data source, username and password.',
-    'type'        => 'textarea',
-  },
-
-  {
-    'key'         => 'invoice_from',
-    'section'     => 'required',
-    'description' => 'Return address on email invoices',
-    'type'        => 'text',
-  },
-
-  {
-    'key'         => 'invoice_template',
-    'section'     => 'required',
-    'description' => 'Required template file for invoices.  See the <a href="../docs/billing.html">billing documentation</a> for details.',
-    'type'        => 'textarea',
-  },
-
-  {
-    'key'         => 'lpr',
-    'section'     => 'required',
-    'description' => 'Print command for paper invoices, for example `lpr -h\'',
-    'type'        => 'text',
-  },
-
-  {
-    'key'         => 'maildisablecatchall',
-    'section'     => 'deprecated',
-    'description' => '<b>DEPRECATED</b>, now the default.  Turning this option on used to disable the requirement that each virtual domain have a catch-all mailbox.',
-    'type'        => 'checkbox',
-  },
-
-  {
-    'key'         => 'money_char',
-    'section'     => '',
-    'description' => 'Currency symbol - defaults to `$\'',
-    'type'        => 'text',
-  },
-
-  {
-    'key'         => 'mxmachines',
-    'section'     => 'deprecated',
-    'description' => 'MX entries for new domains, weight and machine, one per line, with trailing `.\'',
-    'type'        => 'textarea',
-  },
-
-  {
-    'key'         => 'nsmachines',
-    'section'     => 'deprecated',
-    'description' => 'NS nameservers for new domains, one per line, with trailing `.\'',
-    'type'        => 'textarea',
-  },
-
-  {
-    'key'         => 'defaultrecords',
-    'section'     => 'BIND',
-    'description' => 'DNS entries to add automatically when creating a domain',
-    'type'        => 'editlist',
-    'editlist_parts' => [ { type=>'text' },
-                          { type=>'immutable', value=>'IN' },
-                          { type=>'select',
-                            select_enum=>{ map { $_=>$_ } qw(A CNAME MX NS)} },
-                          { type=> 'text' }, ],
-  },
-
-  {
-    'key'         => 'arecords',
-    'section'     => 'deprecated',
-    'description' => 'A list of tab seperated CNAME records to add automatically when creating a domain',
-    'type'        => 'textarea',
-  },
-
-  {
-    'key'         => 'cnamerecords',
-    'section'     => 'deprecated',
-    'description' => 'A list of tab seperated CNAME records to add automatically when creating a domain',
-    'type'        => 'textarea',
-  },
-
-  {
-    'key'         => 'nismachines',
-    'section'     => 'deprecated',
-    'description' => '<b>DEPRECATED</b>.  Your NIS master (not slave master) machines, one per line.  This enables export of `/etc/global/passwd\' and `/etc/global/shadow\'.',
-    'type'        => 'textarea',
-  },
-
-  {
-    'key'         => 'passwordmin',
-    'section'     => 'password',
-    'description' => 'Minimum password length (default 6)',
-    'type'        => 'text',
-  },
-
-  {
-    'key'         => 'passwordmax',
-    'section'     => 'password',
-    'description' => 'Maximum password length (default 8) (don\'t set this over 12 if you need to import or export crypt() passwords)',
-    'type'        => 'text',
-  },
-
-  {
-    'key'         => 'qmailmachines',
-    'section'     => 'mail',
-    'description' => 'Your qmail machines, one per line.  This enables export of `/var/qmail/control/virtualdomains\', `/var/qmail/control/recipientmap\', and `/var/qmail/control/rcpthosts\'.  Setting this option (even if empty) also turns on user `.qmail-extension\' file maintenance in conjunction with the <b>shellmachine</b> option.',
-    'type'        => [qw( checkbox textarea )],
-  },
-
-  {
-    'key'         => 'radiusmachines',
-    'section'     => 'deprecated',
-    'description' => '<b>DEPRECATED</b>, add an <i>sqlradius</i> <a href="../browse/part_export.cgi">export</a> instead.  This option used to export to be: your RADIUS authentication machines, one per line.  This enables export of `/etc/raddb/users\'.',
-    'type'        => 'textarea',
-  },
-
-  {
-    'key'         => 'referraldefault',
-    'section'     => 'UI',
-    'description' => 'Default referral, specified by refnum',
-    'type'        => 'text',
-  },
-
-#  {
-#    'key'         => 'registries',
-#    'section'     => 'required',
-#    'description' => 'Directory which contains domain registry information.  Each registry is a directory.',
-#  },
-
-  {
-    'key'         => 'report_template',
-    'section'     => 'required',
-    'description' => 'Required template file for reports.  See the <a href="../docs/billing.html">billing documentation</a> for details.',
-    'type'        => 'textarea',
-  },
-
-
-  {
-    'key'         => 'maxsearchrecordsperpage',
-    'section'     => 'UI',
-    'description' => 'If set, number of search records to return per page.',
-    'type'        => 'text',
-  },
-
-  {
-    'key'         => 'sendmailconfigpath',
-    'section'     => 'mail',
-    'description' => 'Sendmail configuration file path.  Defaults to `/etc\'.  Many newer distributions use `/etc/mail\'.',
-    'type'        => 'text',
-  },
-
-  {
-    'key'         => 'sendmailmachines',
-    'section'     => 'mail',
-    'description' => 'Your sendmail machines, one per line.  This enables export of `/etc/virtusertable\' and `/etc/sendmail.cw\'.',
-    'type'        => 'textarea',
-  },
-
-  {
-    'key'         => 'sendmailrestart',
-    'section'     => 'mail',
-    'description' => 'If defined, the command which is run on sendmail machines after files are copied.',
-    'type'        => 'text',
-  },
-
-  {
-    'key'         => 'session-start',
-    'section'     => 'session',
-    'description' => 'If defined, the command which is executed on the Freeside machine when a session begins.  The contents of the file are treated as a double-quoted perl string, with the following variables available: <code>$ip</code>, <code>$nasip</code> and <code>$nasfqdn</code>, which are the IP address of the starting session, and the IP address and fully-qualified domain name of the NAS this session is on.',
-    'type'        => 'text',
-  },
-
-  {
-    'key'         => 'session-stop',
-    'section'     => 'session',
-    'description' => 'If defined, the command which is executed on the Freeside machine when a session ends.  The contents of the file are treated as a double-quoted perl string, with the following variables available: <code>$ip</code>, <code>$nasip</code> and <code>$nasfqdn</code>, which are the IP address of the starting session, and the IP address and fully-qualified domain name of the NAS this session is on.',
-    'type'        => 'text',
-  },
-
-  {
-    'key'         => 'shellmachine',
-    'section'     => 'deprecated',
-    'description' => '<b>DEPRECATED</b>, add a <i>shellcommands</i> <a href="../browse/part_export.cgi">export</a> instead.  This option used to contain a single machine with user home directories mounted.  This enables home directory creation, renaming and archiving/deletion.  In conjunction with `qmailmachines\', it also enables `.qmail-extension\' file maintenance.',
-    'type'        => 'text',
-  },
-
-  {
-    'key'         => 'shellmachine-useradd',
-    'section'     => 'deprecated',
-    'description' => '<b>DEPRECATED</b>, add a <i>shellcommands</i> <a href="../browse/part_export.cgi">export</a> instead.  This option used to contain command(s) to run on shellmachine when an account is created.  If the <b>shellmachine</b> option is set but this option is not, <code>useradd -d $dir -m -s $shell -u $uid $username</code> is the default.  If this option is set but empty, <code>cp -pr /etc/skel $dir; chown -R $uid.$gid $dir</code> is the default instead.  Otherwise the value is evaluated as a double-quoted perl string, with the following variables available: <code>$username</code>, <code>$uid</code>, <code>$gid</code>, <code>$dir</code>, and <code>$shell</code>.',
-    'type'        => [qw( checkbox text )],
-  },
-
-  {
-    'key'         => 'shellmachine-userdel',
-    'section'     => 'deprecated',
-    'description' => '<b>DEPRECATED</b>, add a <i>shellcommands</i> <a href="../browse/part_export.cgi">export</a> instead.  This option used to contain command(s) to run on shellmachine when an account is deleted.  If the <b>shellmachine</b> option is set but this option is not, <code>userdel $username</code> is the default.  If this option is set but empty, <code>rm -rf $dir</code> is the default instead.  Otherwise the value is evaluated as a double-quoted perl string, with the following variables available: <code>$username</code> and <code>$dir</code>.',
-    'type'        => [qw( checkbox text )],
-  },
-
-  {
-    'key'         => 'shellmachine-usermod',
-    'section'     => 'deprecated',
-    'description' => '<b>DEPRECATED</b>, add a <i>shellcommands</i> <a href="../browse/part_export.cgi">export</a> instead.  This option used to contain command(s) to run on shellmachine when an account is modified.  If the <b>shellmachine</b> option is set but this option is empty, <code>[ -d $old_dir ] &amp;&amp; mv $old_dir $new_dir || ( chmod u+t $old_dir; mkdir $new_dir; cd $old_dir; find . -depth -print | cpio -pdm $new_dir; chmod u-t $new_dir; chown -R $uid.$gid $new_dir; rm -rf $old_dir )</code> is the default.  Otherwise the contents of the file are treated as a double-quoted perl string, with the following variables available: <code>$old_dir</code>, <code>$new_dir</code>, <code>$uid</code> and <code>$gid</code>.',
-    #'type'        => [qw( checkbox text )],
-    'type'        => 'text',
-  },
-
-  {
-    'key'         => 'shellmachines',
-    'section'     => 'deprecated',
-    'description' => '<b>DEPRECATED</b>, add a <i>sysvshell</i> <a href="../browse/part_export.cgi">export</a> instead.  Your Linux and System V flavored shell (and mail) machines, one per line.  This enables export of `/etc/passwd\' and `/etc/shadow\' files.',
-     'type'        => 'textarea',
- },
-
-  {
-    'key'         => 'shells',
-    'section'     => 'required',
-    'description' => 'Legal shells (think /etc/shells).  You probably want to `cut -d: -f7 /etc/passwd | sort | uniq\' initially so that importing doesn\'t fail with `Illegal shell\' errors, then remove any special entries afterwords.  A blank line specifies that an empty shell is permitted.',
-    'type'        => 'textarea',
-  },
-
-  {
-    'key'         => 'showpasswords',
-    'section'     => 'UI',
-    'description' => 'Display unencrypted user passwords in the web interface',
-    'type'        => 'checkbox',
-  },
-
-  {
-    'key'         => 'signupurl',
-    'section'     => 'UI',
-    'description' => 'if you are using customer-to-customer referrals, and you enter the URL of your <a href="../docs/signup.html">signup server CGI</a>, the customer view screen will display a customized link to the signup server with the appropriate customer as referral',
-    'type'        => 'text',
-  },
-
-  {
-    'key'         => 'smtpmachine',
-    'section'     => 'required',
-    'description' => 'SMTP relay for Freeside\'s outgoing mail',
-    'type'        => 'text',
-  },
-
-  {
-    'key'         => 'soadefaultttl',
-    'section'     => 'BIND',
-    'description' => 'SOA default TTL for new domains.',
-    'type'        => 'text',
-  },
-
-  {
-    'key'         => 'soaemail',
-    'section'     => 'BIND',
-    'description' => 'SOA email for new domains, in BIND form (`.\' instead of `@\'), with trailing `.\'',
-    'type'        => 'text',
-  },
-
-  {
-    'key'         => 'soaexpire',
-    'section'     => 'BIND',
-    'description' => 'SOA expire for new domains',
-    'type'        => 'text',
-  },
-
-  {
-    'key'         => 'soamachine',
-    'section'     => 'BIND',
-    'description' => 'SOA machine for new domains, with trailing `.\'',
-    'type'        => 'text',
-  },
-
-  {
-    'key'         => 'soarefresh',
-    'section'     => 'BIND',
-    'description' => 'SOA refresh for new domains',
-    'type'        => 'text',
-  },
-
-  {
-    'key'         => 'soaretry',
-    'section'     => 'BIND',
-    'description' => 'SOA retry for new domains',
-    'type'        => 'text',
-  },
-
-  {
-    'key'         => 'statedefault',
-    'section'     => 'UI',
-    'description' => 'Default state or province (if not supplied, the default is `CA\')',
-    'type'        => 'text',
-  },
-
-  {
-    'key'         => 'radiusprepend',
-    'section'     => 'deprecated',
-    'description' => '<b>DEPRECATED</b>, real-time text radius now edits an existing file in place - just (turn off freeside-queued and) edit your RADIUS users file directly.  The contents used to be be prepended to the top of the RADIUS users file (text exports only).',
-    'type'        => 'textarea',
-  },
-
-  {
-    'key'         => 'textradiusprepend',
-    'section'     => 'deprecated',
-    'description' => '<b>DEPRECATED</b>, use RADIUS check attributes instead.  The contents used to be prepended to the first line of a user\'s RADIUS entry in text exports.',
-    'type'        => 'text',
-  },
-
-  {
-    'key'         => 'unsuspendauto',
-    'section'     => 'billing',
-    'description' => 'Enables the automatic unsuspension of suspended packages when a customer\'s balance due changes from positive to zero or negative as the result of a payment or credit',
-    'type'        => 'checkbox',
-  },
-
-  {
-    'key'         => 'usernamemin',
-    'section'     => 'username',
-    'description' => 'Minimum username length (default 2)',
-    'type'        => 'text',
-  },
-
-  {
-    'key'         => 'usernamemax',
-    'section'     => 'username',
-    'description' => 'Maximum username length',
-    'type'        => 'text',
-  },
-
-  {
-    'key'         => 'username-ampersand',
-    'section'     => 'username',
-    'description' => 'Allow the ampersand character (&amp;) in usernames.  Be careful when using this option in conjunction with <a href="#shellmachine-useradd">shellmachine-useradd</a> and other configuration options which execute shell commands, as the ampersand will be interpreted by the shell if not quoted.',
-    'type'        => 'checkbox',
-  },
-
-  {
-    'key'         => 'username-letter',
-    'section'     => 'username',
-    'description' => 'Usernames must contain at least one letter',
-    'type'        => 'checkbox',
-  },
-
-  {
-    'key'         => 'username-letterfirst',
-    'section'     => 'username',
-    'description' => 'Usernames must start with a letter',
-    'type'        => 'checkbox',
-  },
-
-  {
-    'key'         => 'username-noperiod',
-    'section'     => 'username',
-    'description' => 'Disallow periods in usernames',
-    'type'        => 'checkbox',
-  },
-
-  {
-    'key'         => 'username-nounderscore',
-    'section'     => 'username',
-    'description' => 'Disallow underscores in usernames',
-    'type'        => 'checkbox',
-  },
-
-  {
-    'key'         => 'username-nodash',
-    'section'     => 'username',
-    'description' => 'Disallow dashes in usernames',
-    'type'        => 'checkbox',
-  },
-
-  {
-    'key'         => 'username-uppercase',
-    'section'     => 'username',
-    'description' => 'Allow uppercase characters in usernames',
-    'type'        => 'checkbox',
-  },
-
-  {
-    'key'         => 'username_policy',
-    'section'     => '',
-    'description' => 'This file controls the mechanism for preventing duplicate usernames in passwd/radius files exported from svc_accts.  This should be one of \'prepend domsvc\' \'append domsvc\' \'append domain\' or \'append @domain\'',
-    'type'        => 'select',
-    'select_enum' => [ 'prepend domsvc', 'append domsvc', 'append domain', 'append @domain' ],
-    #'type'        => 'text',
-  },
-
-  {
-    'key'         => 'vpopmailmachines',
-    'section'     => 'deprecated',
-    'description' => '<b>DEPRECATED</b>, add a <i>cp</i> <a href="../browse/part_export.cgi">export</a> instead.  This option used to contain your vpopmail pop toasters, one per line.  Each line is of the form "machinename vpopdir vpopuid vpopgid".  For example: <code>poptoaster.domain.tld /home/vpopmail 508 508</code>  Note: vpopuid and vpopgid are values taken from the vpopmail machine\'s /etc/passwd',
-    'type'        => 'textarea',
-  },
-
-  {
-    'key'         => 'vpopmailrestart',
-    'section'     => 'mail',
-    'description' => 'If defined, the shell commands to run on vpopmail machines after files are copied.  An example can be found in eg/vpopmailrestart of the source distribution.',
-    'type'        => 'textarea',
-  },
-
-  {
-    'key'         => 'safe-part_pkg',
-    'section'     => 'UI',
-    'description' => 'Validates package definition setup and recur expressions against a preset list.  Useful for webdemos, annoying to powerusers.',
-    'type'        => 'checkbox',
-  },
-
-  {
-    'key'         => 'safe-part_bill_event',
-    'section'     => 'UI',
-    'description' => 'Validates invoice event expressions against a preset list.  Useful for webdemos, annoying to powerusers.',
-    'type'        => 'checkbox',
-  },
-
-  {
-    'key'         => 'show_ss',
-    'section'     => 'UI',
-    'description' => 'Turns on display/collection of SS# in the web interface.',
-    'type'        => 'checkbox',
-  },
-
-  { 
-    'key'         => 'agent_defaultpkg',
-    'section'     => 'UI',
-    'description' => 'Setting this option will cause new packages to be available to all agent types by default.',
-    'type'        => 'checkbox',
-  },
-
-  {
-    'key'         => 'legacy_link',
-    'section'     => 'UI',
-    'description' => 'Display options in the web interface to link legacy pre-Freeside services.',
-    'type'        => 'checkbox',
-  },
-
-  {
-    'key'         => 'queue_dangerous_controls',
-    'section'     => 'UI',
-    'description' => 'Enable queue modification controls on account pages and for new jobs.  Unless you are a developer working on new export code, you should probably leave this off to avoid causing provisioning problems.',
-    'type'        => 'checkbox',
-  },
-
-  {
-    'key'         => 'security_phrase',
-    'section'     => 'password',
-    'description' => 'Enable the tracking of a "security phrase" with each account.  Not recommended, as it is vulnerable to social engineering.',
-    'type'        => 'checkbox',
-  },
-
-  {
-    'key'         => 'locale',
-    'section'     => 'UI',
-    'description' => 'Message locale',
-    'type'        => 'select',
-    'select_enum' => [ qw(en_US) ],
-  },
-
-  {
-    'key'         => 'signup_server-payby',
-    'section'     => '',
-    'description' => 'Acceptable payment types for the signup server',
-    'type'        => 'selectmultiple',
-    'select_enum' => [ qw(CARD PREPAY BILL COMP) ],
-  },
-
-  {
-    'key'         => 'signup_server-email',
-    'section'     => '',
-    'description' => 'Comma-separated list of email addresses to receive notification of signups via the signup server.',
-    'type'        => 'text',
-  },
-
-
-  {
-    'key'         => 'show-msgcat-codes',
-    'section'     => 'UI',
-    'description' => 'Show msgcat codes in error messages.  Turn this option on before reporting errors to the mailing list.',
-    'type'        => 'checkbox',
-  },
-
-  {
-    'key'         => 'signup_server-realtime',
-    'section'     => '',
-    'description' => 'Run billing for signup server signups immediately, and suspend accounts which subsequently have a balance.',
-    'type'        => 'checkbox',
-  },
-
-  {
-    'key'         => 'declinetemplate',
-    'section'     => 'billing',
-    'description' => 'Template file for credit card decline emails.',
-    'type'        => 'textarea',
-  },
-
-  {
-    'key'         => 'emaildecline',
-    'section'     => 'billing',
-    'description' => 'Enable emailing of credit card decline notices.',
-    'type'        => 'checkbox',
-  },
-
-  {
-    'key'         => 'require_cardname',
-    'section'     => 'billing',
-    'description' => 'Require an "Exact name on card" to be entered explicitly; don\'t default to using the first and last name.',
-    'type'        => 'checkbox',
-  },
-
-  {
-    'key'         => 'enable_taxclasses',
-    'section'     => 'billing',
-    'description' => 'Enable per-package tax classes',
-    'type'        => 'checkbox',
-  },
-
-  {
-    'key'         => 'welcome_email',
-    'section'     => '',
-    'description' => 'Template file for welcome email.  Welcome emails are sent to the customer email invoice destination(s) each time a svc_acct record is created.  See the <a href="http://search.cpan.org/doc/MJD/Text-Template-1.42/Template.pm">Text::Template</a> documentation for details on the template substitution language.  The following variables are available: <code>$username</code>, <code>$password</code>, <code>$first</code>, <code>$last</code> and <code>$pkg</code>.',
-    'type'        => 'textarea',
-  },
-
-  {
-    'key'         => 'welcome_email-from',
-    'section'     => '',
-    'description' => 'From: address header for welcome email',
-    'type'        => 'text',
-  },
-
-  {
-    'key'         => 'welcome_email-subject',
-    'section'     => '',
-    'description' => 'Subject: header for welcome email',
-    'type'        => 'text',
-  },
-  
-  {
-    'key'         => 'welcome_email-mimetype',
-    'section'     => '',
-    'description' => 'MIME type for welcome email',
-    'type'        => 'select',
-    'select_enum' => [ 'text/plain', 'text/html' ],
-  },
-
-);
-
-1;
-
diff --git a/FS/FS/ConfItem.pm b/FS/FS/ConfItem.pm
deleted file mode 100644 (file)
index 83295b4..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-package FS::ConfItem;
-
-=head1 NAME
-
-FS::ConfItem - Configutaion option meta-data.
-
-=head1 SYNOPSIS
-
-  use FS::Conf;
-  @config_items = $conf->config_items;
-
-  foreach $item ( @config_items ) {
-    $key = $item->key;
-    $section = $item->section;
-    $description = $item->description;
-  }
-
-=head1 DESCRIPTION
-
-=head1 METHODS
-
-=over 4
-
-=item new
-
-=cut
-
-sub new {
-  my $proto = shift;
-  my $class = ref($proto) || $proto;
-  my $self = @_ ? shift : {};
-  bless ($self, $class);
-}
-
-=item key
-
-=item section
-
-=item description
-
-=cut
-
-sub AUTOLOAD {
-  my $self = shift;
-  my $field = $AUTOLOAD;
-  $field =~ s/.*://;
-  $self->{$field};
-}
-
-=back
-
-=head1 BUGS
-
-Terse docs.
-
-=head1 SEE ALSO
-
-L<FS::Conf>
-
-=cut
-
-1;
-
diff --git a/FS/FS/InitHandler.pm b/FS/FS/InitHandler.pm
deleted file mode 100644 (file)
index 87f507c..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-package FS::InitHandler;
-
-use strict;
-use vars qw($DEBUG);
-use FS::UID qw(adminsuidsetup);
-use FS::Record;
-
-$DEBUG = 1;
-
-sub handler {
-
-  use Date::Format;
-  use Date::Parse;
-  use Tie::IxHash;
-  use HTML::Entities;
-  use IO::Handle;
-  use IO::File;
-  use String::Approx;
-  use HTML::Widgets::SelectLayers 0.02;
-  #use FS::UID;
-  #use FS::Record;
-  use FS::Conf;
-  use FS::CGI;
-  use FS::Msgcat;
-  
-  use FS::agent;
-  use FS::agent_type;
-  use FS::domain_record;
-  use FS::cust_bill;
-  use FS::cust_bill_pay;
-  use FS::cust_credit;
-  use FS::cust_credit_bill;
-  use FS::cust_main;
-  use FS::cust_main_county;
-  use FS::cust_pay;
-  use FS::cust_pkg;
-  use FS::cust_refund;
-  use FS::cust_svc;
-  use FS::nas;
-  use FS::part_bill_event;
-  use FS::part_pkg;
-  use FS::part_referral;
-  use FS::part_svc;
-  use FS::pkg_svc;
-  use FS::port;
-  use FS::queue;
-  use FS::raddb;
-  use FS::session;
-  use FS::svc_acct;
-  use FS::svc_acct_pop;
-  use FS::svc_acct_sm;
-  use FS::svc_domain;
-  use FS::svc_forward;
-  use FS::svc_www;
-  use FS::type_pkgs;
-  use FS::part_export;
-  use FS::part_export_option;
-  use FS::export_svc;
-  use FS::msgcat;
-
-  warn "[FS::InitHandler] handler called\n" if $DEBUG;
-
-  #this is sure to be broken on freebsd
-  $> = $FS::UID::freeside_uid;
-
-  open(MAPSECRETS,"<$FS::UID::conf_dir/mapsecrets")
-    or die "can't read $FS::UID::conf_dir/mapsecrets: $!";
-
-  my %seen;
-  while (<MAPSECRETS>) {
-    next if /^\s*(#|$)/;
-    /^([\w\-\.]+)\s(.*)$/
-      or do { warn "strange line in mapsecrets: $_"; next; };
-    my($user, $datasrc) = ($1, $2);
-    next if $seen{$datasrc}++;
-    warn "[FS::InitHandler] preloading $datasrc for $user\n" if $DEBUG;
-    adminsuidsetup($user);
-  }
-
-  close MAPSECRETS;
-
-  #lalala probably broken on freebsd
-  ($<, $>) = ($>, $<);
-  $< = 0;
-
-}
-
-1;
diff --git a/FS/FS/Msgcat.pm b/FS/FS/Msgcat.pm
deleted file mode 100644 (file)
index 625743d..0000000
+++ /dev/null
@@ -1,98 +0,0 @@
-package FS::Msgcat;
-
-use strict;
-use vars qw( @ISA @EXPORT_OK $conf $locale $debug );
-use Exporter;
-use FS::UID;
-#use FS::Record qw( qsearchs ); # wtf?  won't import...
-use FS::Record;
-use FS::Conf;
-use FS::msgcat;
-
-@ISA = qw(Exporter);
-@EXPORT_OK = qw( gettext geterror );
-
-$FS::UID::callback{'Msgcat'} = sub {
-  $conf = new FS::Conf;
-  $locale = $conf->config('locale') || 'en_US';
-  $debug = $conf->exists('show-msgcat-codes')
-};
-
-=head1 NAME
-
-FS::Msgcat - Message catalog functions
-
-=head1 SYNOPSIS
-
-  use FS::Msgcat qw(gettext geterror);
-
-  #simple interface for retreiving messages...
-  $message = gettext('msgcode');
-  #or errors (includes the error code)
-  $message = geterror('msgcode');
-
-=head1 DESCRIPTION
-
-FS::Msgcat provides functions to use the message catalog.  If you want to
-maintain the message catalog database, see L<FS::msgcat> instead.
-
-=head1 SUBROUTINES
-
-=over 4
-
-=item gettext MSGCODE
-
-Returns the full message for the supplied message code.
-
-=cut
-
-sub gettext {
-  $debug ? geterror(@_) : _gettext(@_);
-}
-
-sub _gettext {
-  my $msgcode = shift;
-  my $msgcat = FS::Record::qsearchs('msgcat', {
-    'msgcode' => $msgcode,
-    'locale' => $locale
-  } );
-  if ( $msgcat ) {
-    $msgcat->msg;
-  } else {
-    warn "WARNING: message for msgcode $msgcode in locale $locale not found";
-    $msgcode;
-  }
-
-}
-
-=item geterror MSGCODE
-
-Returns the full message for the supplied message code, including the message
-code.
-
-=cut
-
-sub geterror {
-  my $msgcode = shift;
-  my $msg = _gettext($msgcode);
-  if ( $msg eq $msgcode ) {
-    "Error code $msgcode (message for locale $locale not found)";
-  } else {
-    "$msg (error code $msgcode)";
-  }
-}
-
-=back
-
-=head1 BUGS
-
-i18n/l10n, eek
-
-=head1 SEE ALSO
-
-L<FS::msgcat>, L<FS::Record>, schema.html from the base documentation.
-
-=cut
-
-1;
-
diff --git a/FS/FS/Record.pm b/FS/FS/Record.pm
deleted file mode 100644 (file)
index e6126a1..0000000
+++ /dev/null
@@ -1,1258 +0,0 @@
-package FS::Record;
-
-use strict;
-use vars qw( $dbdef_file $dbdef $setup_hack $AUTOLOAD @ISA @EXPORT_OK $DEBUG
-             $me %dbdef_cache );
-use subs qw(reload_dbdef);
-use Exporter;
-use Carp qw(carp cluck croak confess);
-use File::CounterFile;
-use Locale::Country;
-use DBI qw(:sql_types);
-use DBIx::DBSchema 0.19;
-use FS::UID qw(dbh checkruid getotaker datasrc driver_name);
-use FS::SearchCache;
-use FS::Msgcat qw(gettext);
-
-@ISA = qw(Exporter);
-@EXPORT_OK = qw(dbh fields hfields qsearch qsearchs dbdef jsearch);
-
-$DEBUG = 0;
-$me = '[FS::Record]';
-
-#ask FS::UID to run this stuff for us later
-$FS::UID::callback{'FS::Record'} = sub { 
-  $File::CounterFile::DEFAULT_DIR = "/usr/local/etc/freeside/counters.". datasrc;
-  $dbdef_file = "/usr/local/etc/freeside/dbdef.". datasrc;
-  &reload_dbdef unless $setup_hack; #$setup_hack needed now?
-};
-
-=head1 NAME
-
-FS::Record - Database record objects
-
-=head1 SYNOPSIS
-
-    use FS::Record;
-    use FS::Record qw(dbh fields qsearch qsearchs dbdef);
-
-    $record = new FS::Record 'table', \%hash;
-    $record = new FS::Record 'table', { 'column' => 'value', ... };
-
-    $record  = qsearchs FS::Record 'table', \%hash;
-    $record  = qsearchs FS::Record 'table', { 'column' => 'value', ... };
-    @records = qsearch  FS::Record 'table', \%hash; 
-    @records = qsearch  FS::Record 'table', { 'column' => 'value', ... };
-
-    $table = $record->table;
-    $dbdef_table = $record->dbdef_table;
-
-    $value = $record->get('column');
-    $value = $record->getfield('column');
-    $value = $record->column;
-
-    $record->set( 'column' => 'value' );
-    $record->setfield( 'column' => 'value' );
-    $record->column('value');
-
-    %hash = $record->hash;
-
-    $hashref = $record->hashref;
-
-    $error = $record->insert;
-    #$error = $record->add; #deprecated
-
-    $error = $record->delete;
-    #$error = $record->del; #deprecated
-
-    $error = $new_record->replace($old_record);
-    #$error = $new_record->rep($old_record); #deprecated
-
-    $value = $record->unique('column');
-
-    $error = $record->ut_float('column');
-    $error = $record->ut_number('column');
-    $error = $record->ut_numbern('column');
-    $error = $record->ut_money('column');
-    $error = $record->ut_text('column');
-    $error = $record->ut_textn('column');
-    $error = $record->ut_alpha('column');
-    $error = $record->ut_alphan('column');
-    $error = $record->ut_phonen('column');
-    $error = $record->ut_anything('column');
-    $error = $record->ut_name('column');
-
-    $dbdef = reload_dbdef;
-    $dbdef = reload_dbdef "/non/standard/filename";
-    $dbdef = dbdef;
-
-    $quoted_value = _quote($value,'table','field');
-
-    #depriciated
-    $fields = hfields('table');
-    if ( $fields->{Field} ) { # etc.
-
-    @fields = fields 'table'; #as a subroutine
-    @fields = $record->fields; #as a method call
-
-
-=head1 DESCRIPTION
-
-(Mostly) object-oriented interface to database records.  Records are currently
-implemented on top of DBI.  FS::Record is intended as a base class for
-table-specific classes to inherit from, i.e. FS::cust_main.
-
-=head1 CONSTRUCTORS
-
-=over 4
-
-=item new [ TABLE, ] HASHREF
-
-Creates a new record.  It doesn't store it in the database, though.  See
-L<"insert"> for that.
-
-Note that the object stores this hash reference, not a distinct copy of the
-hash it points to.  You can ask the object for a copy with the I<hash> 
-method.
-
-TABLE can only be omitted when a dervived class overrides the table method.
-
-=cut
-
-sub new { 
-  my $proto = shift;
-  my $class = ref($proto) || $proto;
-  my $self = {};
-  bless ($self, $class);
-
-  unless ( defined ( $self->table ) ) {
-    $self->{'Table'} = shift;
-    carp "warning: FS::Record::new called with table name ". $self->{'Table'};
-  }
-
-  my $hashref = $self->{'Hash'} = shift;
-
-  foreach my $field ( grep !defined($hashref->{$_}), $self->fields ) { 
-    $hashref->{$field}='';
-  }
-
-  $self->_cache($hashref, shift) if $self->can('_cache') && @_;
-
-  $self;
-}
-
-sub new_or_cached {
-  my $proto = shift;
-  my $class = ref($proto) || $proto;
-  my $self = {};
-  bless ($self, $class);
-
-  $self->{'Table'} = shift unless defined ( $self->table );
-
-  my $hashref = $self->{'Hash'} = shift;
-  my $cache = shift;
-  if ( defined( $cache->cache->{$hashref->{$cache->key}} ) ) {
-    my $obj = $cache->cache->{$hashref->{$cache->key}};
-    $obj->_cache($hashref, $cache) if $obj->can('_cache');
-    $obj;
-  } else {
-    $cache->cache->{$hashref->{$cache->key}} = $self->new($hashref, $cache);
-  }
-
-}
-
-sub create {
-  my $proto = shift;
-  my $class = ref($proto) || $proto;
-  my $self = {};
-  bless ($self, $class);
-  if ( defined $self->table ) {
-    cluck "create constructor is depriciated, use new!";
-    $self->new(@_);
-  } else {
-    croak "FS::Record::create called (not from a subclass)!";
-  }
-}
-
-=item qsearch TABLE, HASHREF, SELECT, EXTRA_SQL, CACHE_OBJ
-
-Searches the database for all records matching (at least) the key/value pairs
-in HASHREF.  Returns all the records found as `FS::TABLE' objects if that
-module is loaded (i.e. via `use FS::cust_main;'), otherwise returns FS::Record
-objects.
-
-###oops, argh, FS::Record::new only lets us create database fields.
-#Normal behaviour if SELECT is not specified is `*', as in
-#C<SELECT * FROM table WHERE ...>.  However, there is an experimental new
-#feature where you can specify SELECT - remember, the objects returned,
-#although blessed into the appropriate `FS::TABLE' package, will only have the
-#fields you specify.  This might have unwanted results if you then go calling
-#regular FS::TABLE methods
-#on it.
-
-=cut
-
-sub qsearch {
-  my($stable, $record, $select, $extra_sql, $cache ) = @_;
-  #$stable =~ /^([\w\_]+)$/ or die "Illegal table: $table";
-  #for jsearch
-  $stable =~ /^([\w\s\(\)\.\,\=]+)$/ or die "Illegal table: $stable";
-  $stable = $1;
-  $select ||= '*';
-  my $dbh = dbh;
-
-  my $table = $cache ? $cache->table : $stable;
-
-  my @fields = grep exists($record->{$_}), fields($table);
-
-  my $statement = "SELECT $select FROM $stable";
-  if ( @fields ) {
-    $statement .= ' WHERE '. join(' AND ', map {
-
-      my $op = '=';
-      if ( ref($record->{$_}) ) {
-        $op = $record->{$_}{'op'} if $record->{$_}{'op'};
-        $op = 'LIKE' if $op =~ /^ILIKE$/i && driver_name !~ /^Pg$/i;
-        $record->{$_} = $record->{$_}{'value'}
-      }
-
-      if ( ! defined( $record->{$_} ) || $record->{$_} eq '' ) {
-        if ( $op eq '=' ) {
-          if ( driver_name =~ /^Pg$/i ) {
-            qq-( $_ IS NULL OR $_ = '' )-;
-          } else {
-            qq-( $_ IS NULL OR $_ = "" )-;
-          }
-        } elsif ( $op eq '!=' ) {
-          if ( driver_name =~ /^Pg$/i ) {
-            qq-( $_ IS NOT NULL AND $_ != '' )-;
-          } else {
-            qq-( $_ IS NOT NULL AND $_ != "" )-;
-          }
-        } else {
-          if ( driver_name =~ /^Pg$/i ) {
-            qq-( $_ $op '' )-;
-          } else {
-            qq-( $_ $op "" )-;
-          }
-        }
-      } else {
-        "$_ $op ?";
-      }
-    } @fields );
-  }
-  $statement .= " $extra_sql" if defined($extra_sql);
-
-  warn "[debug]$me $statement\n" if $DEBUG > 1;
-  my $sth = $dbh->prepare($statement)
-    or croak "$dbh->errstr doing $statement";
-
-  my $bind = 1;
-
-  foreach my $field (
-    grep defined( $record->{$_} ) && $record->{$_} ne '', @fields
-  ) {
-    if ( $record->{$field} =~ /^\d+(\.\d+)?$/
-         && $dbdef->table($table)->column($field)->type =~ /(int)/i
-    ) {
-      $sth->bind_param($bind++, $record->{$field}, { TYPE => SQL_INTEGER } );
-    } else {
-      $sth->bind_param($bind++, $record->{$field}, { TYPE => SQL_VARCHAR } );
-    }
-  }
-
-#  $sth->execute( map $record->{$_},
-#    grep defined( $record->{$_} ) && $record->{$_} ne '', @fields
-#  ) or croak "Error executing \"$statement\": ". $sth->errstr;
-
-  $sth->execute or croak "Error executing \"$statement\": ". $sth->errstr;
-
-  $dbh->commit or croak $dbh->errstr if $FS::UID::AutoCommit;
-
-  if ( eval 'scalar(@FS::'. $table. '::ISA);' ) {
-    if ( eval 'FS::'. $table. '->can(\'new\')' eq \&new ) {
-      #derivied class didn't override new method, so this optimization is safe
-      if ( $cache ) {
-        map {
-          new_or_cached( "FS::$table", { %{$_} }, $cache )
-        } @{$sth->fetchall_arrayref( {} )};
-      } else {
-        map {
-          new( "FS::$table", { %{$_} } )
-        } @{$sth->fetchall_arrayref( {} )};
-      }
-    } else {
-      warn "untested code (class FS::$table uses custom new method)";
-      map {
-        eval 'FS::'. $table. '->new( { %{$_} } )';
-      } @{$sth->fetchall_arrayref( {} )};
-    }
-  } else {
-    cluck "warning: FS::$table not loaded; returning FS::Record objects";
-    map {
-      FS::Record->new( $table, { %{$_} } );
-    } @{$sth->fetchall_arrayref( {} )};
-  }
-
-}
-
-=item jsearch TABLE, HASHREF, SELECT, EXTRA_SQL, PRIMARY_TABLE, PRIMARY_KEY
-
-Experimental JOINed search method.  Using this method, you can execute a
-single SELECT spanning multiple tables, and cache the results for subsequent
-method calls.  Interface will almost definately change in an incompatible
-fashion.
-
-Arguments: 
-
-=cut
-
-sub jsearch {
-  my($table, $record, $select, $extra_sql, $ptable, $pkey ) = @_;
-  my $cache = FS::SearchCache->new( $ptable, $pkey );
-  my %saw;
-  ( $cache,
-    grep { !$saw{$_->getfield($pkey)}++ }
-      qsearch($table, $record, $select, $extra_sql, $cache )
-  );
-}
-
-=item qsearchs TABLE, HASHREF
-
-Same as qsearch, except that if more than one record matches, it B<carp>s but
-returns the first.  If this happens, you either made a logic error in asking
-for a single item, or your data is corrupted.
-
-=cut
-
-sub qsearchs { # $result_record = &FS::Record:qsearchs('table',\%hash);
-  my(@result) = qsearch(@_);
-  carp "warning: Multiple records in scalar search!" if scalar(@result) > 1;
-    #should warn more vehemently if the search was on a primary key?
-  scalar(@result) ? ($result[0]) : ();
-}
-
-=back
-
-=head1 METHODS
-
-=over 4
-
-=item table
-
-Returns the table name.
-
-=cut
-
-sub table {
-#  cluck "warning: FS::Record::table depriciated; supply one in subclass!";
-  my $self = shift;
-  $self -> {'Table'};
-}
-
-=item dbdef_table
-
-Returns the DBIx::DBSchema::Table object for the table.
-
-=cut
-
-sub dbdef_table {
-  my($self)=@_;
-  my($table)=$self->table;
-  $dbdef->table($table);
-}
-
-=item get, getfield COLUMN
-
-Returns the value of the column/field/key COLUMN.
-
-=cut
-
-sub get {
-  my($self,$field) = @_;
-  # to avoid "Use of unitialized value" errors
-  if ( defined ( $self->{Hash}->{$field} ) ) {
-    $self->{Hash}->{$field};
-  } else { 
-    '';
-  }
-}
-sub getfield {
-  my $self = shift;
-  $self->get(@_);
-}
-
-=item set, setfield COLUMN, VALUE
-
-Sets the value of the column/field/key COLUMN to VALUE.  Returns VALUE.
-
-=cut
-
-sub set { 
-  my($self,$field,$value) = @_;
-  $self->{'Hash'}->{$field} = $value;
-}
-sub setfield {
-  my $self = shift;
-  $self->set(@_);
-}
-
-=item AUTLOADED METHODS
-
-$record->column is a synonym for $record->get('column');
-
-$record->column('value') is a synonym for $record->set('column','value');
-
-=cut
-
-# readable/safe
-sub AUTOLOAD {
-  my($self,$value)=@_;
-  my($field)=$AUTOLOAD;
-  $field =~ s/.*://;
-  if ( defined($value) ) {
-    confess "errant AUTOLOAD $field for $self (arg $value)"
-      unless $self->can('setfield');
-    $self->setfield($field,$value);
-  } else {
-    confess "errant AUTOLOAD $field for $self (no args)"
-      unless $self->can('getfield');
-    $self->getfield($field);
-  }    
-}
-
-# efficient
-#sub AUTOLOAD {
-#  my $field = $AUTOLOAD;
-#  $field =~ s/.*://;
-#  if ( defined($_[1]) ) {
-#    $_[0]->setfield($field, $_[1]);
-#  } else {
-#    $_[0]->getfield($field);
-#  }    
-#}
-
-=item hash
-
-Returns a list of the column/value pairs, usually for assigning to a new hash.
-
-To make a distinct duplicate of an FS::Record object, you can do:
-
-    $new = new FS::Record ( $old->table, { $old->hash } );
-
-=cut
-
-sub hash {
-  my($self) = @_;
-  %{ $self->{'Hash'} }; 
-}
-
-=item hashref
-
-Returns a reference to the column/value hash.
-
-=cut
-
-sub hashref {
-  my($self) = @_;
-  $self->{'Hash'};
-}
-
-=item insert
-
-Inserts this record to the database.  If there is an error, returns the error,
-otherwise returns false.
-
-=cut
-
-sub insert {
-  my $self = shift;
-
-  my $error = $self->check;
-  return $error if $error;
-
-  #single-field unique keys are given a value if false
-  #(like MySQL's AUTO_INCREMENT)
-  foreach ( $self->dbdef_table->unique->singles ) {
-    $self->unique($_) unless $self->getfield($_);
-  }
-  #and also the primary key
-  my $primary_key = $self->dbdef_table->primary_key;
-  $self->unique($primary_key) 
-    if $primary_key && ! $self->getfield($primary_key);
-
-  #false laziness w/delete
-  my @fields =
-    grep defined($self->getfield($_)) && $self->getfield($_) ne "",
-    $self->fields
-  ;
-  my @values = map { _quote( $self->getfield($_), $self->table, $_) } @fields;
-  #eslaf
-
-  my $statement = "INSERT INTO ". $self->table. " ( ".
-      join( ', ', @fields ).
-    ") VALUES (".
-      join( ', ', @values ).
-    ")"
-  ;
-  warn "[debug]$me $statement\n" if $DEBUG > 1;
-  my $sth = dbh->prepare($statement) or return dbh->errstr;
-
-  my $h_sth;
-  if ( defined $dbdef->table('h_'. $self->table) ) {
-    my $h_statement = $self->_h_statement('insert');
-    warn "[debug]$me $h_statement\n" if $DEBUG > 2;
-    $h_sth = dbh->prepare($h_statement) or return dbh->errstr;
-  } else {
-    $h_sth = '';
-  }
-
-  local $SIG{HUP} = 'IGNORE';
-  local $SIG{INT} = 'IGNORE';
-  local $SIG{QUIT} = 'IGNORE'; 
-  local $SIG{TERM} = 'IGNORE';
-  local $SIG{TSTP} = 'IGNORE';
-  local $SIG{PIPE} = 'IGNORE';
-
-  $sth->execute or return $sth->errstr;
-  $h_sth->execute or return $h_sth->errstr if $h_sth;
-  dbh->commit or croak dbh->errstr if $FS::UID::AutoCommit;
-
-  '';
-}
-
-=item add
-
-Depriciated (use insert instead).
-
-=cut
-
-sub add {
-  cluck "warning: FS::Record::add depriciated!";
-  insert @_; #call method in this scope
-}
-
-=item delete
-
-Delete this record from the database.  If there is an error, returns the error,
-otherwise returns false.
-
-=cut
-
-sub delete {
-  my $self = shift;
-
-  my $statement = "DELETE FROM ". $self->table. " WHERE ". join(' AND ',
-    map {
-      $self->getfield($_) eq ''
-        #? "( $_ IS NULL OR $_ = \"\" )"
-        ? ( driver_name =~ /^Pg$/i
-              ? "$_ IS NULL"
-              : "( $_ IS NULL OR $_ = \"\" )"
-          )
-        : "$_ = ". _quote($self->getfield($_),$self->table,$_)
-    } ( $self->dbdef_table->primary_key )
-          ? ( $self->dbdef_table->primary_key)
-          : $self->fields
-  );
-  warn "[debug]$me $statement\n" if $DEBUG > 1;
-  my $sth = dbh->prepare($statement) or return dbh->errstr;
-
-  my $h_sth;
-  if ( defined $dbdef->table('h_'. $self->table) ) {
-    my $h_statement = $self->_h_statement('delete');
-    warn "[debug]$me $h_statement\n" if $DEBUG > 2;
-    $h_sth = dbh->prepare($h_statement) or return dbh->errstr;
-  } else {
-    $h_sth = '';
-  }
-
-  local $SIG{HUP} = 'IGNORE';
-  local $SIG{INT} = 'IGNORE';
-  local $SIG{QUIT} = 'IGNORE'; 
-  local $SIG{TERM} = 'IGNORE';
-  local $SIG{TSTP} = 'IGNORE';
-  local $SIG{PIPE} = 'IGNORE';
-
-  my $rc = $sth->execute or return $sth->errstr;
-  #not portable #return "Record not found, statement:\n$statement" if $rc eq "0E0";
-  $h_sth->execute or return $h_sth->errstr if $h_sth;
-  dbh->commit or croak dbh->errstr if $FS::UID::AutoCommit;
-
-  #no need to needlessly destoy the data either (causes problems actually)
-  #undef $self; #no need to keep object!
-
-  '';
-}
-
-=item del
-
-Depriciated (use delete instead).
-
-=cut
-
-sub del {
-  cluck "warning: FS::Record::del depriciated!";
-  &delete(@_); #call method in this scope
-}
-
-=item replace OLD_RECORD
-
-Replace the OLD_RECORD with this one in the database.  If there is an error,
-returns the error, otherwise returns false.
-
-=cut
-
-sub replace {
-  my ( $new, $old ) = ( shift, shift );
-  warn "[debug]$me $new ->replace $old\n" if $DEBUG;
-
-  return "Records not in same table!" unless $new->table eq $old->table;
-
-  my $primary_key = $old->dbdef_table->primary_key;
-  return "Can't change $primary_key"
-    if $primary_key
-       && ( $old->getfield($primary_key) ne $new->getfield($primary_key) );
-
-  my $error = $new->check;
-  return $error if $error;
-
-  my @diff = grep $new->getfield($_) ne $old->getfield($_), $old->fields;
-  unless ( @diff ) {
-    carp "[warning]$me $new -> replace $old: records identical";
-    return '';
-  }
-
-  my $statement = "UPDATE ". $old->table. " SET ". join(', ',
-    map {
-      "$_ = ". _quote($new->getfield($_),$old->table,$_) 
-    } @diff
-  ). ' WHERE '.
-    join(' AND ',
-      map {
-        $old->getfield($_) eq ''
-          #? "( $_ IS NULL OR $_ = \"\" )"
-          ? ( driver_name =~ /^Pg$/i
-                ? "$_ IS NULL"
-                : "( $_ IS NULL OR $_ = \"\" )"
-            )
-          : "$_ = ". _quote($old->getfield($_),$old->table,$_)
-      } ( $primary_key ? ( $primary_key ) : $old->fields )
-    )
-  ;
-  warn "[debug]$me $statement\n" if $DEBUG > 1;
-  my $sth = dbh->prepare($statement) or return dbh->errstr;
-
-  my $h_old_sth;
-  if ( defined $dbdef->table('h_'. $old->table) ) {
-    my $h_old_statement = $old->_h_statement('replace_old');
-    warn "[debug]$me $h_old_statement\n" if $DEBUG > 2;
-    $h_old_sth = dbh->prepare($h_old_statement) or return dbh->errstr;
-  } else {
-    $h_old_sth = '';
-  }
-
-  my $h_new_sth;
-  if ( defined $dbdef->table('h_'. $new->table) ) {
-    my $h_new_statement = $new->_h_statement('replace_new');
-    warn "[debug]$me $h_new_statement\n" if $DEBUG > 2;
-    $h_new_sth = dbh->prepare($h_new_statement) or return dbh->errstr;
-  } else {
-    $h_new_sth = '';
-  }
-
-  local $SIG{HUP} = 'IGNORE';
-  local $SIG{INT} = 'IGNORE';
-  local $SIG{QUIT} = 'IGNORE'; 
-  local $SIG{TERM} = 'IGNORE';
-  local $SIG{TSTP} = 'IGNORE';
-  local $SIG{PIPE} = 'IGNORE';
-
-  my $rc = $sth->execute or return $sth->errstr;
-  #not portable #return "Record not found (or records identical)." if $rc eq "0E0";
-  $h_old_sth->execute or return $h_old_sth->errstr if $h_old_sth;
-  $h_new_sth->execute or return $h_new_sth->errstr if $h_new_sth;
-  dbh->commit or croak dbh->errstr if $FS::UID::AutoCommit;
-
-  '';
-
-}
-
-=item rep
-
-Depriciated (use replace instead).
-
-=cut
-
-sub rep {
-  cluck "warning: FS::Record::rep depriciated!";
-  replace @_; #call method in this scope
-}
-
-=item check
-
-Not yet implemented, croaks.  Derived classes should provide a check method.
-
-=cut
-
-sub check {
-  confess "FS::Record::check not implemented; supply one in subclass!";
-}
-
-sub _h_statement {
-  my( $self, $action ) = @_;
-
-  my @fields =
-    grep defined($self->getfield($_)) && $self->getfield($_) ne "",
-    $self->fields
-  ;
-  my @values = map { _quote( $self->getfield($_), $self->table, $_) } @fields;
-
-  "INSERT INTO h_". $self->table. " ( ".
-      join(', ', qw(history_date history_user history_action), @fields ).
-    ") VALUES (".
-      join(', ', time, dbh->quote(getotaker()), dbh->quote($action), @values).
-    ")"
-  ;
-}
-
-=item unique COLUMN
-
-Replaces COLUMN in record with a unique number.  Called by the B<add> method
-on primary keys and single-field unique columns (see L<DBIx::DBSchema::Table>).
-Returns the new value.
-
-=cut
-
-sub unique {
-  my($self,$field) = @_;
-  my($table)=$self->table;
-
-  #croak("&FS::UID::checkruid failed") unless &checkruid;
-
-  croak "Unique called on field $field, but it is ",
-        $self->getfield($field),
-        ", not null!"
-    if $self->getfield($field);
-
-  #warn "table $table is tainted" if is_tainted($table);
-  #warn "field $field is tainted" if is_tainted($field);
-
-  my($counter) = new File::CounterFile "$table.$field",0;
-# hack for web demo
-#  getotaker() =~ /^([\w\-]{1,16})$/ or die "Illegal CGI REMOTE_USER!";
-#  my($user)=$1;
-#  my($counter) = new File::CounterFile "$user/$table.$field",0;
-# endhack
-
-  my($index)=$counter->inc;
-  $index=$counter->inc
-    while qsearchs($table,{$field=>$index}); #just in case
-
-  $index =~ /^(\d*)$/;
-  $index=$1;
-
-  $self->setfield($field,$index);
-
-}
-
-=item ut_float COLUMN
-
-Check/untaint floating point numeric data: 1.1, 1, 1.1e10, 1e10.  May not be
-null.  If there is an error, returns the error, otherwise returns false.
-
-=cut
-
-sub ut_float {
-  my($self,$field)=@_ ;
-  ($self->getfield($field) =~ /^(\d+\.\d+)$/ ||
-   $self->getfield($field) =~ /^(\d+)$/ ||
-   $self->getfield($field) =~ /^(\d+\.\d+e\d+)$/ ||
-   $self->getfield($field) =~ /^(\d+e\d+)$/)
-    or return "Illegal or empty (float) $field: ". $self->getfield($field);
-  $self->setfield($field,$1);
-  '';
-}
-
-=item ut_number COLUMN
-
-Check/untaint simple numeric data (whole numbers).  May not be null.  If there
-is an error, returns the error, otherwise returns false.
-
-=cut
-
-sub ut_number {
-  my($self,$field)=@_;
-  $self->getfield($field) =~ /^(\d+)$/
-    or return "Illegal or empty (numeric) $field: ". $self->getfield($field);
-  $self->setfield($field,$1);
-  '';
-}
-
-=item ut_numbern COLUMN
-
-Check/untaint simple numeric data (whole numbers).  May be null.  If there is
-an error, returns the error, otherwise returns false.
-
-=cut
-
-sub ut_numbern {
-  my($self,$field)=@_;
-  $self->getfield($field) =~ /^(\d*)$/
-    or return "Illegal (numeric) $field: ". $self->getfield($field);
-  $self->setfield($field,$1);
-  '';
-}
-
-=item ut_money COLUMN
-
-Check/untaint monetary numbers.  May be negative.  Set to 0 if null.  If there
-is an error, returns the error, otherwise returns false.
-
-=cut
-
-sub ut_money {
-  my($self,$field)=@_;
-  $self->setfield($field, 0) if $self->getfield($field) eq '';
-  $self->getfield($field) =~ /^(\-)? ?(\d*)(\.\d{2})?$/
-    or return "Illegal (money) $field: ". $self->getfield($field);
-  #$self->setfield($field, "$1$2$3" || 0);
-  $self->setfield($field, ( ($1||''). ($2||''). ($3||'') ) || 0);
-  '';
-}
-
-=item ut_text COLUMN
-
-Check/untaint text.  Alphanumerics, spaces, and the following punctuation
-symbols are currently permitted: ! @ # $ % & ( ) - + ; : ' " , . ? / =
-May not be null.  If there is an error, returns the error, otherwise returns
-false.
-
-=cut
-
-sub ut_text {
-  my($self,$field)=@_;
-  #warn "msgcat ". \&msgcat. "\n";
-  #warn "notexist ". \&notexist. "\n";
-  #warn "AUTOLOAD ". \&AUTOLOAD. "\n";
-  $self->getfield($field) =~ /^([\w \!\@\#\$\%\&\(\)\-\+\;\:\'\"\,\.\?\/\=]+)$/
-    or return gettext('illegal_or_empty_text'). " $field: ".
-               $self->getfield($field);
-  $self->setfield($field,$1);
-  '';
-}
-
-=item ut_textn COLUMN
-
-Check/untaint text.  Alphanumerics, spaces, and the following punctuation
-symbols are currently permitted: ! @ # $ % & ( ) - + ; : ' " , . ? /
-May be null.  If there is an error, returns the error, otherwise returns false.
-
-=cut
-
-sub ut_textn {
-  my($self,$field)=@_;
-  $self->getfield($field) =~ /^([\w \!\@\#\$\%\&\(\)\-\+\;\:\'\"\,\.\?\/\=]*)$/
-    or return gettext('illegal_text'). " $field: ". $self->getfield($field);
-  $self->setfield($field,$1);
-  '';
-}
-
-=item ut_alpha COLUMN
-
-Check/untaint alphanumeric strings (no spaces).  May not be null.  If there is
-an error, returns the error, otherwise returns false.
-
-=cut
-
-sub ut_alpha {
-  my($self,$field)=@_;
-  $self->getfield($field) =~ /^(\w+)$/
-    or return "Illegal or empty (alphanumeric) $field: ".
-              $self->getfield($field);
-  $self->setfield($field,$1);
-  '';
-}
-
-=item ut_alpha COLUMN
-
-Check/untaint alphanumeric strings (no spaces).  May be null.  If there is an
-error, returns the error, otherwise returns false.
-
-=cut
-
-sub ut_alphan {
-  my($self,$field)=@_;
-  $self->getfield($field) =~ /^(\w*)$/ 
-    or return "Illegal (alphanumeric) $field: ". $self->getfield($field);
-  $self->setfield($field,$1);
-  '';
-}
-
-=item ut_phonen COLUMN [ COUNTRY ]
-
-Check/untaint phone numbers.  May be null.  If there is an error, returns
-the error, otherwise returns false.
-
-Takes an optional two-letter ISO country code; without it or with unsupported
-countries, ut_phonen simply calls ut_alphan.
-
-=cut
-
-sub ut_phonen {
-  my( $self, $field, $country ) = @_;
-  return $self->ut_alphan($field) unless defined $country;
-  my $phonen = $self->getfield($field);
-  if ( $phonen eq '' ) {
-    $self->setfield($field,'');
-  } elsif ( $country eq 'US' || $country eq 'CA' ) {
-    $phonen =~ s/\D//g;
-    $phonen =~ /^(\d{3})(\d{3})(\d{4})(\d*)$/
-      or return gettext('illegal_phone'). " $field: ". $self->getfield($field);
-    $phonen = "$1-$2-$3";
-    $phonen .= " x$4" if $4;
-    $self->setfield($field,$phonen);
-  } else {
-    warn "warning: don't know how to check phone numbers for country $country";
-    return $self->ut_textn($field);
-  }
-  '';
-}
-
-=item ut_ip COLUMN
-
-Check/untaint ip addresses.  IPv4 only for now.
-
-=cut
-
-sub ut_ip {
-  my( $self, $field ) = @_;
-  $self->getfield($field) =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/
-    or return "Illegal (IP address) $field: ". $self->getfield($field);
-  for ( $1, $2, $3, $4 ) { return "Illegal (IP address) $field" if $_ > 255; }
-  $self->setfield($field, "$1.$2.$3.$3");
-  '';
-}
-
-=item ut_ipn COLUMN
-
-Check/untaint ip addresses.  IPv4 only for now.  May be null.
-
-=cut
-
-sub ut_ipn {
-  my( $self, $field ) = @_;
-  if ( $self->getfield($field) =~ /^()$/ ) {
-    $self->setfield($field,'');
-    '';
-  } else {
-    $self->ut_ip($field);
-  }
-}
-
-=item ut_domain COLUMN
-
-Check/untaint host and domain names.
-
-=cut
-
-sub ut_domain {
-  my( $self, $field ) = @_;
-  #$self->getfield($field) =~/^(\w+\.)*\w+$/
-  $self->getfield($field) =~/^(([\w\-]+\.)*\w+)$/
-    or return "Illegal (domain) $field: ". $self->getfield($field);
-  $self->setfield($field,$1);
-  '';
-}
-
-=item ut_name COLUMN
-
-Check/untaint proper names; allows alphanumerics, spaces and the following
-punctuation: , . - '
-
-May not be null.
-
-=cut
-
-sub ut_name {
-  my( $self, $field ) = @_;
-  $self->getfield($field) =~ /^([\w \,\.\-\']+)$/
-    or return gettext('illegal_name'). " $field: ". $self->getfield($field);
-  $self->setfield($field,$1);
-  '';
-}
-
-=item ut_zip COLUMN
-
-Check/untaint zip codes.
-
-=cut
-
-sub ut_zip {
-  my( $self, $field, $country ) = @_;
-  if ( $country eq 'US' ) {
-    $self->getfield($field) =~ /\s*(\d{5}(\-\d{4})?)\s*$/
-      or return gettext('illegal_zip'). " $field for country $country: ".
-                $self->getfield($field);
-    $self->setfield($field,$1);
-  } else {
-    $self->getfield($field) =~ /^\s*(\w[\w\-\s]{2,8}\w)\s*$/
-      or return gettext('illegal_zip'). " $field: ". $self->getfield($field);
-    $self->setfield($field,$1);
-  }
-  '';
-}
-
-=item ut_country COLUMN
-
-Check/untaint country codes.  Country names are changed to codes, if possible -
-see L<Locale::Country>.
-
-=cut
-
-sub ut_country {
-  my( $self, $field ) = @_;
-  unless ( $self->getfield($field) =~ /^(\w\w)$/ ) {
-    if ( $self->getfield($field) =~ /^([\w \,\.\(\)\']+)$/ 
-         && country2code($1) ) {
-      $self->setfield($field,uc(country2code($1)));
-    }
-  }
-  $self->getfield($field) =~ /^(\w\w)$/
-    or return "Illegal (country) $field: ". $self->getfield($field);
-  $self->setfield($field,uc($1));
-  '';
-}
-
-=item ut_anything COLUMN
-
-Untaints arbitrary data.  Be careful.
-
-=cut
-
-sub ut_anything {
-  my( $self, $field ) = @_;
-  $self->getfield($field) =~ /^(.*)$/s
-    or return "Illegal $field: ". $self->getfield($field);
-  $self->setfield($field,$1);
-  '';
-}
-
-=item ut_enum COLUMN CHOICES_ARRAYREF
-
-Check/untaint a column, supplying all possible choices, like the "enum" type.
-
-=cut
-
-sub ut_enum {
-  my( $self, $field, $choices ) = @_;
-  foreach my $choice ( @$choices ) {
-    if ( $self->getfield($field) eq $choice ) {
-      $self->setfield($choice);
-      return '';
-    }
-  }
-  return "Illegal (enum) field $field: ". $self->getfield($field);
-}
-
-=item ut_foreign_key COLUMN FOREIGN_TABLE FOREIGN_COLUMN
-
-Check/untaint a foreign column key.  Call a regular ut_ method (like ut_number)
-on the column first.
-
-=cut
-
-sub ut_foreign_key {
-  my( $self, $field, $table, $foreign ) = @_;
-  qsearchs($table, { $foreign => $self->getfield($field) })
-    or return "Can't find $field ". $self->getfield($field).
-              " in $table.$foreign";
-  '';
-}
-
-=item ut_foreign_keyn COLUMN FOREIGN_TABLE FOREIGN_COLUMN
-
-Like ut_foreign_key, except the null value is also allowed.
-
-=cut
-
-sub ut_foreign_keyn {
-  my( $self, $field, $table, $foreign ) = @_;
-  $self->getfield($field)
-    ? $self->ut_foreign_key($field, $table, $foreign)
-    : '';
-}
-
-=item fields [ TABLE ]
-
-This can be used as both a subroutine and a method call.  It returns a list
-of the columns in this record's table, or an explicitly specified table.
-(See L<DBIx::DBSchema::Table>).
-
-=cut
-
-# Usage: @fields = fields($table);
-#        @fields = $record->fields;
-sub fields {
-  my $something = shift;
-  my $table;
-  if ( ref($something) ) {
-    $table = $something->table;
-  } else {
-    $table = $something;
-  }
-  #croak "Usage: \@fields = fields(\$table)\n   or: \@fields = \$record->fields" unless $table;
-  my($table_obj) = $dbdef->table($table);
-  confess "Unknown table $table" unless $table_obj;
-  $table_obj->columns;
-}
-
-=back
-
-=head1 SUBROUTINES
-
-=over 4
-
-=item reload_dbdef([FILENAME])
-
-Load a database definition (see L<DBIx::DBSchema>), optionally from a
-non-default filename.  This command is executed at startup unless
-I<$FS::Record::setup_hack> is true.  Returns a DBIx::DBSchema object.
-
-=cut
-
-sub reload_dbdef {
-  my $file = shift || $dbdef_file;
-
-  unless ( exists $dbdef_cache{$file} ) {
-    warn "[debug]$me loading dbdef for $file\n" if $DEBUG;
-    $dbdef_cache{$file} = DBIx::DBSchema->load( $file )
-                            or die "can't load database schema from $file";
-  } else {
-    warn "[debug]$me re-using cached dbdef for $file\n" if $DEBUG;
-  }
-  $dbdef = $dbdef_cache{$file};
-}
-
-=item dbdef
-
-Returns the current database definition.  See L<DBIx::DBSchema>.
-
-=cut
-
-sub dbdef { $dbdef; }
-
-=item _quote VALUE, TABLE, COLUMN
-
-This is an internal function used to construct SQL statements.  It returns
-VALUE DBI-quoted (see L<DBI/"quote">) unless VALUE is a number and the column
-type (see L<DBIx::DBSchema::Column>) does not end in `char' or `binary'.
-
-=cut
-
-sub _quote {
-  my($value,$table,$field)=@_;
-  my($dbh)=dbh;
-  if ( $value =~ /^\d+(\.\d+)?$/ && 
-#       ! ( datatype($table,$field) =~ /^char/ ) 
-       ! $dbdef->table($table)->column($field)->type =~ /(char|binary|text)$/i 
-  ) {
-    $value;
-  } else {
-    $dbh->quote($value);
-  }
-}
-
-=item hfields TABLE
-
-This is depriciated.  Don't use it.
-
-It returns a hash-type list with the fields of this record's table set true.
-
-=cut
-
-sub hfields {
-  carp "warning: hfields is depriciated";
-  my($table)=@_;
-  my(%hash);
-  foreach (fields($table)) {
-    $hash{$_}=1;
-  }
-  \%hash;
-}
-
-sub _dump {
-  my($self)=@_;
-  join("\n", map {
-    "$_: ". $self->getfield($_). "|"
-  } (fields($self->table)) );
-}
-
-sub DESTROY { return; }
-
-#sub DESTROY {
-#  my $self = shift;
-#  #use Carp qw(cluck);
-#  #cluck "DESTROYING $self";
-#  warn "DESTROYING $self";
-#}
-
-#sub is_tainted {
-#             return ! eval { join('',@_), kill 0; 1; };
-#         }
-
-=back
-
-=head1 BUGS
-
-This module should probably be renamed, since much of the functionality is
-of general use.  It is not completely unlike Adapter::DBI (see below).
-
-Exported qsearch and qsearchs should be depriciated in favor of method calls
-(against an FS::Record object like the old search and searchs that qsearch
-and qsearchs were on top of.)
-
-The whole fields / hfields mess should be removed.
-
-The various WHERE clauses should be subroutined.
-
-table string should be depriciated in favor of DBIx::DBSchema::Table.
-
-No doubt we could benefit from a Tied hash.  Documenting how exists / defined
-true maps to the database (and WHERE clauses) would also help.
-
-The ut_ methods should ask the dbdef for a default length.
-
-ut_sqltype (like ut_varchar) should all be defined
-
-A fallback check method should be provided which uses the dbdef.
-
-The ut_money method assumes money has two decimal digits.
-
-The Pg money kludge in the new method only strips `$'.
-
-The ut_phonen method only checks US-style phone numbers.
-
-The _quote function should probably use ut_float instead of a regex.
-
-All the subroutines probably should be methods, here or elsewhere.
-
-Probably should borrow/use some dbdef methods where appropriate (like sub
-fields)
-
-As of 1.14, DBI fetchall_hashref( {} ) doesn't set fetchrow_hashref NAME_lc,
-or allow it to be set.  Working around it is ugly any way around - DBI should
-be fixed.  (only affects RDBMS which return uppercase column names)
-
-ut_zip should take an optional country like ut_phone.
-
-=head1 SEE ALSO
-
-L<DBIx::DBSchema>, L<FS::UID>, L<DBI>
-
-Adapter::DBI from Ch. 11 of Advanced Perl Programming by Sriram Srinivasan.
-
-=cut
-
-1;
-
diff --git a/FS/FS/SearchCache.pm b/FS/FS/SearchCache.pm
deleted file mode 100644 (file)
index 4218acf..0000000
+++ /dev/null
@@ -1,96 +0,0 @@
-package FS::SearchCache;
-
-use strict;
-use vars qw($DEBUG);
-#use Carp qw(carp cluck croak confess);
-
-$DEBUG = 0;
-
-=head1 NAME
-
-FS::SearchCache - cache
-
-=head1 SYNOPSIS
-
-=head1 DESCRIPTION
-
-=head1 METHODS
-
-=over 4
-
-=item new
-
-=cut
-
-sub new { 
-  my $proto = shift;
-  my $class = ref($proto) || $proto;
-  my( $table, $key ) = @_;
-  warn "table $table\n" if $DEBUG > 1;
-  warn "key $key\n" if $DEBUG > 1;
-  my $self = { 'table' => $table,
-               'key'   => $key,
-               'cache' => {},
-               'subcache' => {},
-             };
-  bless ($self, $class);
-
-  $self;
-}
-
-=item table
-
-=cut
-
-sub table { my $self = shift; $self->{table}; }
-
-=item key
-
-=cut
-
-sub key { my $self = shift; $self->{key}; }
-
-=item cache
-
-=cut
-
-sub cache { my $self = shift; $self->{cache}; }
-
-=item subcache
-
-=cut
-
-sub subcache {
-  my $self = shift;
-  my $col = shift;
-  my $table = shift;
-  my $keyval = shift;
-  if ( exists $self->{subcache}->{$col}->{$keyval} ) {
-    warn "returning existing subcache for $keyval ($col)".
-         "$self->{subcache}->{$col}->{$keyval}\n" if $DEBUG;
-    return $self->{subcache}->{$col}->{$keyval};
-  } else {
-    #my $tablekey = @_ ? shift : $col;
-    my $tablekey = $col;
-    my $subcache = ref($self)->new( $table, $tablekey );
-    $self->{subcache}->{$col}->{$keyval} = $subcache;
-    warn "creating new subcache $table $tablekey: $subcache\n" if $DEBUG;
-    $subcache;
-  }
-}
-
-=back
-
-=head1 BUGS
-
-Dismal documentation.
-
-=head1 SEE ALSO
-
-L<FS::Record>, L<FS::cust_main>
-
-=cut
-
-1;
-
-
diff --git a/FS/FS/UI/Base.pm b/FS/FS/UI/Base.pm
deleted file mode 100644 (file)
index bbeb9e1..0000000
+++ /dev/null
@@ -1,194 +0,0 @@
-package FS::UI::Base;
-
-use strict;
-use vars qw ( @ISA );
-use FS::Record qw( fields qsearch );
-
-@ISA = ( $FS::UI::Base::_lock );
-
-=head1 NAME
-
-FS::UI::Base - Base class for all user-interface objects
-
-=head1 SYNOPSIS
-
-  use FS::UI::SomeInterface;
-  use FS::UI::some_table;
-
-  $interface = new FS::UI::some_table;
-
-  $error = $interface->browse;
-  $error = $interface->search;
-  $error = $interface->view;
-  $error = $interface->edit;
-  $error = $interface->process;
-
-=head1 DESCRIPTION
-
-An FS::UI::Base object represents a user interface object.  FS::UI::Base
-is intended as a base class for table-specfic classes to inherit from, i.e.
-FS::UI::cust_main.  The simplest case, which will provide a default UI for your
-new table, is as follows:
-
-  package FS::UI::table_name;
-  use vars qw ( @ISA );
-  use FS::UI::Base;
-  @ISA = qw( FS::UI::Base );
-  sub db_table { 'table_name'; }
-
-Currently available interfaces are:
-  FS::UI::Gtk, an X-Windows UI implemented using the Gtk+ toolkit
-  FS::UI::CGI, a web interface implemented using CGI.pm, etc.
-
-=head1 METHODS
-
-=over 4
-
-=item new
-
-=cut
-
-=item browse
-
-=cut
-
-sub browse {
-  my $self = shift;
-
-  my @fields = $self->list_fields;
-
-  #begin browse-specific stuff
-
-  $self->title( "Browse ". $self->db_names ) unless $self->title;
-  my @records = qsearch ( $self->db_table, {} );
-
-  #end browse-specific stuff
-
-  $self->addwidget ( new FS::UI::_Text ( $self->db_description ) );
-
-  my @header = $self->list_header;
-  my @headerspan = $self->list_headerspan;
-  my %callback = $self->db_callback;
-
-  my $columns;
-
-  my $table = new FS::UI::_Tableborder (
-    'rows' => 1 + scalar(@records),
-    'columns' => $columns || scalar(@fields),
-  );
-
-  my $c = 0;
-  foreach my $header ( @header ) {
-    my $headerspan = shift(@headerspan) || 1;
-    $table->attach(
-      0, $c, new FS::UI::_Text ( $header ), 1, $headerspan
-    );
-    $c += $headerspan;
-  }
-
-  my $r = 1;
-  
-  foreach my $record ( @records ) {
-    $c = 0;
-    foreach my $field ( @fields ) {
-      my $value = $record->getfield($field);
-      my $widget;
-      if ( $callback{$field} ) {
-        $widget = &{ $callback{$field} }( $value, $record );
-      } else {
-        $widget = new FS::UI::_Text ( $value );
-      }
-      $table->attach( $r, $c++, $widget, 1, 1 );
-    }
-    $r++;
-  }
-
-  $self->addwidget( $table );
-
-  $self->activate;
-
-}
-
-=item title
-
-=cut
-
-sub title {
-  my $self = shift;
-  my $value = shift;
-  if ( defined($value) ) {
-    $self->{'title'} = $value;
-  } else {
-    $self->{'title'};
-  }
-}
-
-=item addwidget
-
-=cut
-
-sub addwidget {
-  my $self = shift;
-  my $widget = shift;
-  push @{ $self->{'Widgets'} }, $widget;
-}
-
-#fallback methods
-
-sub db_description {}
-
-sub db_name {}
-
-sub db_names {
-  my $self = shift;
-  $self->db_name. 's';
-}
-
-sub list_fields {
-  my $self = shift;
-  fields( $self->db_table );
-}
-
-sub list_header {
-  my $self = shift;
-  $self->list_fields
-}
-
-sub list_headerspan {
-  my $self = shift;
-  map 1, $self->list_header;
-}
-
-sub db_callback {}
-
-=back
-
-=head1 VERSION
-
-$Id: Base.pm,v 1.1 1999-08-04 09:03:53 ivan Exp $
-
-=head1 BUGS
-
-This documentation is incomplete.
-
-There should be some sort of per-(freeside)-user preferences and the ability
-for specific FS::UI:: modules to put their own values there as well.
-
-=head1 SEE ALSO
-
-L<FS::UI::Gtk>, L<FS::UI::CGI>
-
-=head1 HISTORY
-
-$Log: Base.pm,v $
-Revision 1.1  1999-08-04 09:03:53  ivan
-initial checkin of module files for proper perl installation
-
-Revision 1.1  1999/01/20 09:30:36  ivan
-skeletal cross-UI UI code.
-
-
-=cut
-
-1;
-
diff --git a/FS/FS/UI/CGI.pm b/FS/FS/UI/CGI.pm
deleted file mode 100644 (file)
index ae87d13..0000000
+++ /dev/null
@@ -1,239 +0,0 @@
-package FS::UI::CGI;
-
-use strict;
-use CGI;
-#use CGI::Switch;  #when FS::UID user and preference callback stuff is fixed
-use CGI::Carp qw(fatalsToBrowser);
-use HTML::Table;
-use FS::UID qw(adminsuidsetup);
-#use FS::Record qw( qsearch fields );
-
-die "Can't initialize CGI interface; $FS::UI::Base::_lock used"
-  if $FS::UI::Base::_lock;
-$FS::UI::Base::_lock = "FS::UI::CGI";
-
-=head1 NAME
-
-FS::UI::CGI - Base class for CGI user-interface objects
-
-=head1 SYNOPSIS
-
-  use FS::UI::CGI;
-  use FS::UI::some_table;
-
-  $interface = new FS::UI::some_table;
-
-  $error = $interface->browse;
-  $error = $interface->search;
-  $error = $interface->view;
-  $error = $interface->edit;
-  $error = $interface->process;
-
-=head1 DESCRIPTION
-
-An FS::UI::CGI object represents a CGI interface object.
-
-=head1 METHODS
-
-=over 4
-
-=item new
-
-=cut
-
-sub new {
-  my $proto = shift;
-  my $class = ref($proto) || $proto;
-  my $self = { @_ };
-
-  $self->{'_cgi'} = new CGI;
-  $self->{'_user'} = $self->{'_cgi'}->remote_user;
-  $self->{'_dbh'} = FS::UID::adminsuidsetup $self->{'_user'};
-
-  bless ( $self, $class);
-}
-
-sub activate {
-  my $self = shift;
-  print $self->_header,
-        join ( "<BR>", map $_->sprint, @{ $self->{'Widgets'} } ),
-        $self->_footer,
-  ;
-}
-
-=item _header
-
-=cut
-
-sub _header {
-  my $self = shift;
-  my $cgi = $self->{'_cgi'};
-
-  $cgi->header( '-expires' => 'now' ), '<HTML>', 
-    '<HEAD><TITLE>', $self->title, '</TITLE></HEAD>',
-    '<BODY BGCOLOR="#ffffff">',
-    '<FONT COLOR="#ff0000" SIZE=7>', $self->title, '</FONT><BR><BR>',
-  ;
-}
-
-=item _footer
-
-=cut
-
-sub _footer {
-  "</BODY></HTML>";
-}
-
-=item interface
-
-Returns the string `CGI'.  Useful for the author of a table-specific UI class
-to conditionally specify certain behaviour.
-
-=cut
-
-sub interface { 'CGI'; }
-
-=back
-
-=cut
-
-package FS::UI::_Widget;
-
-use vars qw( $AUTOLOAD );
-
-sub new {
-  my $proto = shift;
-  my $class = ref($proto) || $proto;
-  my $self = { @_ };
-  bless ( $self, $class );
-}
-
-sub AUTOLOAD {
-  my $self = shift;
-  my $value = shift;
-  my($field)=$AUTOLOAD;
-  $field =~ s/.*://;
-  if ( defined($value) ) {
-    $self->{$field} = $value;
-  } else {
-    $self->{$field};
-  }    
-}
-
-package FS::UI::_Text;
-
-use vars qw ( @ISA );
-
-@ISA = qw ( FS::UI::_Widget);
-
-sub new {
-  my $proto = shift;
-  my $class = ref($proto) || $proto;
-  my $self = {};
-  $self->{'_text'} = shift;
-  bless ( $self, $class );
-}
-
-sub sprint {
-  my $self = shift;
-  $self->{'_text'};
-}
-
-package FS::UI::_Link;
-
-use vars qw ( @ISA $BASE_URL );
-
-@ISA = qw ( FS::UI::_Widget);
-$BASE_URL = "http://rootwood.sisd.com/freeside";
-
-sub sprint {
-  my $self = shift;
-  my $table = $self->{'table'};
-  my $method = $self->{'method'};
-
-  # i will be cleaned up when we're done moving from the old webinterface!
-  my @arg = @{$self->{'arg'}};
-  my $yuck = join( "&", @arg);
-  qq(<A HREF="$BASE_URL/$method/$table.cgi?$yuck">). $self->{'text'}. "<\A>";
-}
-
-package FS::UI::_Table;
-
-use vars qw ( @ISA );
-
-@ISA = qw ( FS::UI::_Widget);
-
-sub new {
-  my $proto = shift;
-  my $class = ref($proto) || $proto;
-  my $self = $class eq $proto ? { @_ } : $proto;
-  bless ( $self, $class );
-  $self->{'_table'} = new HTML::Table ( $self->rows, $self->columns );
-  $self;
-}
-
-sub attach {
-  my $self = shift;
-  my ( $row, $column, $widget, $rowspan, $colspan ) = @_;
-  $self->{"_table"}->setCell( $row+1, $column+1, $widget->sprint );
-  $self->{"_table"}->setCellRowSpan( $row+1, $column+1, $rowspan ) if $rowspan;
-  $self->{"_table"}->setCellColSpan( $row+1, $column+1, $colspan ) if $colspan;
-}
-
-sub sprint {
-  my $self = shift;
-  $self->{'_table'}->getTable;
-}
-
-package FS::UI::_Tableborder;
-
-use vars qw ( @ISA );
-
-@ISA = qw ( FS::UI::_Table );
-
-sub new {
-  my $proto = shift;
-  my $class = ref($proto) || $proto;
-  my $self = $class eq $proto ? { @_ } : $proto;
-  bless ( $self, $class );
-  $self->SUPER::new(@_);
-  $self->{'_table'}->setBorder;
-  $self;
-}
-
-=head1 VERSION
-
-$Id: CGI.pm,v 1.1 1999-08-04 09:03:53 ivan Exp $
-
-=head1 BUGS
-
-This documentation is incomplete.
-
-In _Tableborder, headers should be links that sort on their fields.
-
-_Link uses a constant $BASE_URL
-
-_Link passes the arguments as a manually-constructed GET string instead
-of POSTing, for compatability while the web interface is upgraded.  Once
-this is done it should pass arguements properly (i.e. as a POST, 8-bit clean)
-
-Still some small bits of widget code same as FS::UI::Gtk.
-
-=head1 SEE ALSO
-
-L<FS::UI::Base>
-
-=head1 HISTORY
-
-$Log: CGI.pm,v $
-Revision 1.1  1999-08-04 09:03:53  ivan
-initial checkin of module files for proper perl installation
-
-Revision 1.1  1999/01/20 09:30:36  ivan
-skeletal cross-UI UI code.
-
-
-=cut
-
-1;
-
diff --git a/FS/FS/UI/Gtk.pm b/FS/FS/UI/Gtk.pm
deleted file mode 100644 (file)
index 507a293..0000000
+++ /dev/null
@@ -1,224 +0,0 @@
-package FS::UI::Gtk;
-
-use strict;
-use Gtk;
-use FS::UID qw(adminsuidsetup);
-
-die "Can't initialize Gtk interface; $FS::UI::Base::_lock used"
-  if $FS::UI::Base::_lock;
-$FS::UI::Base::_lock = "FS::UI::Gtk";
-
-=head1 NAME
-
-FS::UI::Gtk - Base class for Gtk user-interface objects
-
-=head1 SYNOPSIS
-
-  use FS::UI::Gtk;
-  use FS::UI::some_table;
-
-  $interface = new FS::UI::some_table;
-
-  $error = $interface->browse;
-  $error = $interface->search;
-  $error = $interface->view;
-  $error = $interface->edit;
-  $error = $interface->process;
-
-=head1 DESCRIPTION
-
-An FS::UI::Gtk object represents a Gtk user interface object.
-
-=head1 METHODS
-
-=over 4
-
-=item new
-
-=cut
-
-sub new {
-  my $proto = shift;
-  my $class = ref($proto) || $proto;
-  my $self = { @_ };
-
-  bless ( $self, $class );
-
-  $self->{'_user'} = 'ivan'; #Pop up login window?
-  $self->{'_dbh'} = FS::UID::adminsuidsetup $self->{'_user'};
-
-
-
-  $self;
-}
-
-sub activate {
-  my $self = shift;
-
-  my $vbox = new Gtk::VBox ( 0, 4 );
-
-  foreach my $widget ( @{ $self->{'Widgets'} } ) {
-    $widget->_gtk->show;
-    $vbox->pack_start ( $widget->_gtk, 1, 1, 4 );
-  }
-  $vbox->show;
-
-  my $window = new Gtk::Window "toplevel";
-  $self->{'_gtk'} = $window;
-  $window->set_title( $self->title );
-  $window->add ( $vbox );
-  $window->show;
-  main Gtk;
-}
-
-=item interface
-
-Returns the string `Gtk'.  Useful for the author of a table-specific UI class
-to conditionally specify certain behaviour.
-
-=cut 
-
-sub interface { 'Gtk'; }
-
-=back
-
-=cut
-
-package FS::UI::_Widget;
-
-use vars qw( $AUTOLOAD );
-
-sub new {
-  my $proto = shift;
-  my $class = ref($proto) || $proto;
-  my $self = { @_ };
-  bless ( $self, $class );
-}
-
-sub _gtk {
-  my $self = shift;
-  $self->{'_gtk'};
-}
-
-sub AUTOLOAD {
-  my $self = shift;
-  my $value = shift;
-  my($field)=$AUTOLOAD;
-  $field =~ s/.*://;
-  if ( defined($value) ) {
-    $self->{$field} = $value;
-  } else {
-    $self->{$field};
-  }    
-}
-
-package FS::UI::_Text;
-
-use vars qw ( @ISA );
-
-@ISA = qw ( FS::UI::_Widget );
-
-sub new {
-  my $proto = shift;
-  my $class = ref($proto) || $proto;
-  my $self = {};
-  $self->{'_gtk'} = new Gtk::Label ( shift );
-  bless ( $self, $class );
-}
-
-package FS::UI::_Link;
-
-use vars qw ( @ISA );
-
-@ISA = qw ( FS::UI::_Widget );
-
-sub new {
-  my $proto = shift;
-  my $class = ref($proto) || $proto;
-  my $self = { @_ };
-  $self->{'_gtk'} = new_with_label Gtk::Button ( $self->{'text'} );
-  $self->{'_gtk'}->signal_connect( 'clicked', sub {
-      print "STUB: (Gtk) FS::UI::_Link";
-    }, "hi", "there" );
-  bless ( $self, $class );
-}
-
-
-package FS::UI::_Table;
-
-use vars qw ( @ISA );
-
-@ISA = qw ( FS::UI::_Widget );
-
-sub new {
-  my $proto = shift;
-  my $class = ref($proto) || $proto;
-  my $self = { @_ };
-  bless ( $self, $class );
-
-  $self->{'_gtk'} = new Gtk::Table (
-    $self->rows,
-    $self->columns,
-    0, #homogeneous
-  );
-
-  $self;
-}
-
-sub attach {
-  my $self = shift;
-  my ( $row, $column, $widget, $rowspan, $colspan ) = @_;
-  $rowspan ||= 1;
-  $colspan ||= 1;
-  $self->_gtk->attach_defaults(
-    $widget->_gtk,
-    $column,
-    $column + $colspan,
-    $row,
-    $row + $rowspan,
-  );
-  $widget->_gtk->show;
-}
-
-package FS::UI::_Tableborder;
-
-use vars qw ( @ISA );
-
-@ISA = qw ( FS::UI::_Table );
-
-=head1 VERSION
-
-$Id: Gtk.pm,v 1.1 1999-08-04 09:03:53 ivan Exp $
-
-=head1 BUGS
-
-This documentation is incomplete.
-
-_Tableborder is just a _Table now.  _Tableborders should scroll (but not the
-headers) and need and need more decoration. (data in white section ala gtksql
-and sliding field widths) headers should be buttons that callback to sort on
-their fields.
-
-There should be a persistant, per-(freeside)-user store for window positions
-and sizes and sort fields etc (see L<FS::UI::CGI/BUGS>.
-
-Still some small bits of widget code same as FS::UI::CGI.
-
-=head1 SEE ALSO
-
-L<FS::UI::Base>
-
-=head1 HISTORY
-
-$Log: Gtk.pm,v $
-Revision 1.1  1999-08-04 09:03:53  ivan
-initial checkin of module files for proper perl installation
-
-Revision 1.1  1999/01/20 09:30:36  ivan
-skeletal cross-UI UI code.
-
-
-=cut
-
-1;
-
diff --git a/FS/FS/UI/agent.pm b/FS/FS/UI/agent.pm
deleted file mode 100644 (file)
index ce9744a..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-package FS::UI::agent;
-
-use strict;
-use vars qw ( @ISA );
-use FS::UI::Base;
-use FS::Record qw( qsearchs );
-use FS::agent;
-use FS::agent_type;
-
-@ISA = qw ( FS::UI::Base );
-
-sub db_table { 'agent' };
-
-sub db_name { 'Agent' };
-
-sub db_description { <<END;
-Agents are resellers of your service. Agents may be limited to a subset of your
-full offerings (via their type).
-END
-}
-
-sub list_fields {
-  'agentnum',
-  'typenum',
-#  'freq',
-#  'prog',
-; }
-
-sub list_header {
-  'Agent',
-  'Type',
-#  'Freq (n/a)',
-#  'Prog (n/a)',
-; }
-
-sub db_callback { 
-  'agentnum' =>
-    sub {
-      my ( $agentnum, $record ) = @_;
-      my $agent = $record->agent;
-      new FS::UI::_Link (
-        'table'  => 'agent',
-        'method' => 'edit',
-        'arg'    => [ $agentnum ],
-        'text'   => "$agentnum: $agent",
-      );
-    },
-  'typenum' =>
-    sub {
-      my $typenum = shift;
-      my $agent_type = qsearchs( 'agent_type', { 'typenum' => $typenum } );
-      my $atype = $agent_type->atype;
-      new FS::UI::_Link (
-        'table'  => 'agent_type',
-        'method' => 'edit',
-        'arg'    => [ $typenum ],
-        'text'   => "$typenum: $atype"
-      );
-    },
-}
-
-1;
diff --git a/FS/FS/UID.pm b/FS/FS/UID.pm
deleted file mode 100644 (file)
index 0b10612..0000000
+++ /dev/null
@@ -1,281 +0,0 @@
-package FS::UID;
-
-use strict;
-use vars qw(
-  @ISA @EXPORT_OK $cgi $dbh $freeside_uid $user 
-  $conf_dir $secrets $datasrc $db_user $db_pass %callback $driver_name
-  $AutoCommit
-);
-use subs qw(
-  getsecrets cgisetotaker
-);
-use Exporter;
-use Carp qw(carp croak cluck);
-use DBI;
-use FS::Conf;
-
-@ISA = qw(Exporter);
-@EXPORT_OK = qw(checkeuid checkruid cgisuidsetup adminsuidsetup forksuidsetup
-                getotaker dbh datasrc getsecrets driver_name );
-
-$freeside_uid = scalar(getpwnam('freeside'));
-
-$conf_dir = "/usr/local/etc/freeside/";
-
-$AutoCommit = 1; #ours, not DBI
-
-=head1 NAME
-
-FS::UID - Subroutines for database login and assorted other stuff
-
-=head1 SYNOPSIS
-
-  use FS::UID qw(adminsuidsetup cgisuidsetup dbh datasrc getotaker
-  checkeuid checkruid);
-
-  adminsuidsetup $user;
-
-  $cgi = new CGI;
-  $dbh = cgisuidsetup($cgi);
-
-  $dbh = dbh;
-
-  $datasrc = datasrc;
-
-  $driver_name = driver_name;
-
-=head1 DESCRIPTION
-
-Provides a hodgepodge of subroutines. 
-
-=head1 SUBROUTINES
-
-=over 4
-
-=item adminsuidsetup USER
-
-Sets the user to USER (see config.html from the base documentation).
-Cleans the environment.
-Make sure the script is running as freeside, or setuid freeside.
-Opens a connection to the database.
-Swaps real and effective UIDs.
-Runs any defined callbacks (see below).
-Returns the DBI database handle (usually you don't need this).
-
-=cut
-
-sub adminsuidsetup {
-  $dbh->disconnect if $dbh;
-  &forksuidsetup(@_);
-}
-
-sub forksuidsetup {
-  $user = shift;
-  croak "fatal: adminsuidsetup called without arguements" unless $user;
-
-  $user =~ /^([\w\-\.]+)$/ or croak "fatal: illegal user $user";
-  $user = $1;
-
-  $ENV{'PATH'} ='/usr/local/bin:/usr/bin:/usr/ucb:/bin';
-  $ENV{'SHELL'} = '/bin/sh';
-  $ENV{'IFS'} = " \t\n";
-  $ENV{'CDPATH'} = '';
-  $ENV{'ENV'} = '';
-  $ENV{'BASH_ENV'} = '';
-
-  croak "Not running uid freeside!" unless checkeuid();
-  getsecrets;
-  $dbh = DBI->connect($datasrc,$db_user,$db_pass, {
-                          'AutoCommit' => 0,
-                          'ChopBlanks' => 1,
-  } ) or die "DBI->connect error: $DBI::errstr\n";
-
-  foreach ( keys %callback ) {
-    &{$callback{$_}};
-    # breaks multi-database installs # delete $callback{$_}; #run once
-  }
-
-  $dbh;
-}
-
-=item cgisuidsetup CGI_object
-
-Takes a single argument, which is a CGI (see L<CGI>) or Apache (see L<Apache>)
-object (CGI::Base is depriciated).  Runs cgisetotaker and then adminsuidsetup.
-
-=cut
-
-sub cgisuidsetup {
-  $cgi=shift;
-  if ( $cgi->isa('CGI::Base') ) {
-    carp "Use of CGI::Base is depriciated";
-  } elsif ( $cgi->isa('Apache') ) {
-
-  } elsif ( ! $cgi->isa('CGI') ) {
-    croak "fatal: unrecognized object $cgi";
-  }
-  cgisetotaker; 
-  adminsuidsetup($user);
-}
-
-=item cgi
-
-Returns the CGI (see L<CGI>) object.
-
-=cut
-
-sub cgi {
-  carp "warning: \$FS::UID::cgi isa Apache" if $cgi->isa('Apache');
-  $cgi;
-}
-
-=item dbh
-
-Returns the DBI database handle.
-
-=cut
-
-sub dbh {
-  $dbh;
-}
-
-=item datasrc
-
-Returns the DBI data source.
-
-=cut
-
-sub datasrc {
-  $datasrc;
-}
-
-=item driver_name
-
-Returns just the driver name portion of the DBI data source.
-
-=cut
-
-sub driver_name {
-  return $driver_name if defined $driver_name;
-  $driver_name = ( split(':', $datasrc) )[1];
-}
-
-sub suidsetup {
-  croak "suidsetup depriciated";
-}
-
-=item getotaker
-
-Returns the current Freeside user.
-
-=cut
-
-sub getotaker {
-  $user;
-}
-
-=item cgisetotaker
-
-Sets and returns the CGI REMOTE_USER.  $cgi should be defined as a CGI.pm
-object (see L<CGI>) or an Apache object (see L<Apache>).  Support for CGI::Base
-and derived classes is depriciated.
-
-=cut
-
-sub cgisetotaker {
-  if ( $cgi && $cgi->isa('CGI::Base') && defined $cgi->var('REMOTE_USER')) {
-    carp "Use of CGI::Base is depriciated";
-    $user = lc ( $cgi->var('REMOTE_USER') );
-  } elsif ( $cgi && $cgi->isa('CGI') && defined $cgi->remote_user ) {
-    $user = lc ( $cgi->remote_user );
-  } elsif ( $cgi && $cgi->isa('Apache') ) {
-    $user = lc ( $cgi->connection->user );
-  } else {
-    die "fatal: Can't get REMOTE_USER! for cgi $cgi - you need to setup ".
-        "Apache user authentication as documented in httemplate/docs/install.html";
-  }
-  $user;
-}
-
-=item checkeuid
-
-Returns true if effective UID is that of the freeside user.
-
-=cut
-
-sub checkeuid {
-  ( $> == $freeside_uid );
-}
-
-=item checkruid
-
-Returns true if the real UID is that of the freeside user.
-
-=cut
-
-sub checkruid {
-  ( $< == $freeside_uid );
-}
-
-=item getsecrets [ USER ]
-
-Sets the user to USER, if supplied.
-Sets and returns the DBI datasource, username and password for this user from
-the `/usr/local/etc/freeside/mapsecrets' file.
-
-=cut
-
-sub getsecrets {
-  my($setuser) = shift;
-  $user = $setuser if $setuser;
-  die "No user!" unless $user;
-  my($conf) = new FS::Conf $conf_dir;
-  my($line) = grep /^\s*$user\s/, $conf->config('mapsecrets');
-  die "User $user not found in mapsecrets!" unless $line;
-  $line =~ /^\s*$user\s+(.*)$/;
-  $secrets = $1;
-  die "Illegal mapsecrets line for user?!" unless $secrets;
-  ($datasrc, $db_user, $db_pass) = $conf->config($secrets)
-    or die "Can't get secrets: $!";
-  $FS::Conf::default_dir = $conf_dir. "/conf.$datasrc";
-  undef $driver_name;
-  ($datasrc, $db_user, $db_pass);
-}
-
-=back
-
-=head1 CALLBACKS
-
-Warning: this interface is likely to change in future releases.
-
-A package can install a callback to be run in adminsuidsetup by putting a
-coderef into the hash %FS::UID::callback :
-
-    $coderef = sub { warn "Hi, I'm returning your call!" };
-    $FS::UID::callback{'Package::Name'};
-
-=head1 VERSION
-
-$Id: UID.pm,v 1.18 2002-07-03 11:23:25 ivan Exp $
-
-=head1 BUGS
-
-Too many package-global variables.
-
-Not OO.
-
-No capabilities yet.  When mod_perl and Authen::DBI are implemented, 
-cgisuidsetup will go away as well.
-
-Goes through contortions to support non-OO syntax with multiple datasrc's.
-
-Callbacks are inelegant.
-
-=head1 SEE ALSO
-
-L<FS::Record>, L<CGI>, L<DBI>, config.html from the base documentation.
-
-=cut
-
-1;
-
diff --git a/FS/FS/agent.pm b/FS/FS/agent.pm
deleted file mode 100644 (file)
index f11a28d..0000000
+++ /dev/null
@@ -1,160 +0,0 @@
-package FS::agent;
-
-use strict;
-use vars qw( @ISA );
-use FS::Record qw( qsearch qsearchs );
-use FS::cust_main;
-use FS::agent_type;
-
-@ISA = qw( FS::Record );
-
-=head1 NAME
-
-FS::agent - Object methods for agent records
-
-=head1 SYNOPSIS
-
-  use FS::agent;
-
-  $record = new FS::agent \%hash;
-  $record = new FS::agent { 'column' => 'value' };
-
-  $error = $record->insert;
-
-  $error = $new_record->replace($old_record);
-
-  $error = $record->delete;
-
-  $error = $record->check;
-
-  $agent_type = $record->agent_type;
-
-  $hashref = $record->pkgpart_hashref;
-  #may purchase $pkgpart if $hashref->{$pkgpart};
-
-=head1 DESCRIPTION
-
-An FS::agent object represents an agent.  Every customer has an agent.  Agents
-can be used to track things like resellers or salespeople.  FS::agent inherits
-from FS::Record.  The following fields are currently supported:
-
-=over 4
-
-=item agentnum - primary key (assigned automatically for new agents)
-
-=item agent - Text name of this agent
-
-=item typenum - Agent type.  See L<FS::agent_type>
-
-=item prog - For future use.
-
-=item freq - For future use.
-
-=back
-
-=head1 METHODS
-
-=over 4
-
-=item new HASHREF
-
-Creates a new agent.  To add the agent to the database, see L<"insert">.
-
-=cut
-
-sub table { 'agent'; }
-
-=item insert
-
-Adds this agent to the database.  If there is an error, returns the error,
-otherwise returns false.
-
-=item delete
-
-Deletes this agent from the database.  Only agents with no customers can be
-deleted.  If there is an error, returns the error, otherwise returns false.
-
-=cut
-
-sub delete {
-  my $self = shift;
-
-  return "Can't delete an agent with customers!"
-    if qsearch( 'cust_main', { 'agentnum' => $self->agentnum } );
-
-  $self->SUPER::delete;
-}
-
-=item replace OLD_RECORD
-
-Replaces OLD_RECORD with this one in the database.  If there is an error,
-returns the error, otherwise returns false.
-
-=item check
-
-Checks all fields to make sure this is a valid agent.  If there is an error,
-returns the error, otherwise returns false.  Called by the insert and replace
-methods.
-
-=cut
-
-sub check {
-  my $self = shift;
-
-  my $error =
-    $self->ut_numbern('agentnum')
-      || $self->ut_text('agent')
-      || $self->ut_number('typenum')
-      || $self->ut_numbern('freq')
-      || $self->ut_textn('prog')
-  ;
-  return $error if $error;
-
-  return "Unknown typenum!"
-    unless $self->agent_type;
-
-  '';
-
-}
-
-=item agent_type
-
-Returns the FS::agent_type object (see L<FS::agent_type>) for this agent.
-
-=cut
-
-sub agent_type {
-  my $self = shift;
-  qsearchs( 'agent_type', { 'typenum' => $self->typenum } );
-}
-
-=item pkgpart_hashref
-
-Returns a hash reference.  The keys of the hash are pkgparts.  The value is
-true if this agent may purchase the specified package definition.  See
-L<FS::part_pkg>.
-
-=cut
-
-sub pkgpart_hashref {
-  my $self = shift;
-  $self->agent_type->pkgpart_hashref;
-}
-
-=back
-
-=head1 VERSION
-
-$Id: agent.pm,v 1.3 2002-03-24 18:23:47 ivan Exp $
-
-=head1 BUGS
-
-=head1 SEE ALSO
-
-L<FS::Record>, L<FS::agent_type>, L<FS::cust_main>, L<FS::part_pkg>, 
-schema.html from the base documentation.
-
-=cut
-
-1;
-
diff --git a/FS/FS/agent_type.pm b/FS/FS/agent_type.pm
deleted file mode 100644 (file)
index 988533a..0000000
+++ /dev/null
@@ -1,165 +0,0 @@
-package FS::agent_type;
-
-use strict;
-use vars qw( @ISA );
-use FS::Record qw( qsearch );
-use FS::agent;
-use FS::type_pkgs;
-
-@ISA = qw( FS::Record );
-
-=head1 NAME
-
-FS::agent_type - Object methods for agent_type records
-
-=head1 SYNOPSIS
-
-  use FS::agent_type;
-
-  $record = new FS::agent_type \%hash;
-  $record = new FS::agent_type { 'column' => 'value' };
-
-  $error = $record->insert;
-
-  $error = $new_record->replace($old_record);
-
-  $error = $record->delete;
-
-  $error = $record->check;
-
-  $hashref = $record->pkgpart_hashref;
-  #may purchase $pkgpart if $hashref->{$pkgpart};
-
-  @type_pkgs = $record->type_pkgs;
-
-  @pkgparts = $record->pkgpart;
-
-=head1 DESCRIPTION
-
-An FS::agent_type object represents an agent type.  Every agent (see
-L<FS::agent>) has an agent type.  Agent types define which packages (see
-L<FS::part_pkg>) may be purchased by customers (see L<FS::cust_main>), via 
-FS::type_pkgs records (see L<FS::type_pkgs>).  FS::agent_type inherits from
-FS::Record.  The following fields are currently supported:
-
-=over 4
-
-=item typenum - primary key (assigned automatically for new agent types)
-
-=item atype - Text name of this agent type
-
-=back
-
-=head1 METHODS
-
-=over 4
-
-=item new HASHREF
-
-Creates a new agent type.  To add the agent type to the database, see
-L<"insert">.
-
-=cut
-
-sub table { 'agent_type'; }
-
-=item insert
-
-Adds this agent type to the database.  If there is an error, returns the error,
-otherwise returns false.
-
-=item delete
-
-Deletes this agent type from the database.  Only agent types with no agents
-can be deleted.  If there is an error, returns the error, otherwise returns
-false.
-
-=cut
-
-sub delete {
-  my $self = shift;
-
-  return "Can't delete an agent_type with agents!"
-    if qsearch( 'agent', { 'typenum' => $self->typenum } );
-
-  $self->SUPER::delete;
-}
-
-=item replace OLD_RECORD
-
-Replaces OLD_RECORD with this one in the database.  If there is an error,
-returns the error, otherwise returns false.
-
-=item check
-
-Checks all fields to make sure this is a valid agent type.  If there is an
-error, returns the error, otherwise returns false.  Called by the insert and
-replace methods.
-
-=cut
-
-sub check {
-  my $self = shift;
-
-  $self->ut_numbern('typenum')
-  or $self->ut_text('atype');
-
-}
-
-=item pkgpart_hashref
-
-Returns a hash reference.  The keys of the hash are pkgparts.  The value is
-true iff this agent may purchase the specified package definition.  See
-L<FS::part_pkg>.
-
-=cut
-
-sub pkgpart_hashref {
-  my $self = shift;
-  my %pkgpart;
-  #$pkgpart{$_}++ foreach $self->pkgpart;
-  # not compatible w/5.004_04 (fixed in 5.004_05)
-  foreach ( $self->pkgpart ) { $pkgpart{$_}++; }
-  \%pkgpart;
-}
-
-=item type_pkgs
-
-Returns all FS::type_pkgs objects (see L<FS::type_pkgs>) for this agent type.
-
-=cut
-
-sub type_pkgs {
-  my $self = shift;
-  qsearch('type_pkgs', { 'typenum' => $self->typenum } );
-}
-
-=item pkgpart
-
-Returns the pkgpart of all package definitions (see L<FS::part_pkg>) for this
-agent type.
-
-=cut
-
-sub pkgpart {
-  my $self = shift;
-  map $_->pkgpart, $self->type_pkgs;
-}
-
-=back
-
-=head1 VERSION
-
-$Id: agent_type.pm,v 1.1 1999-08-04 09:03:53 ivan Exp $
-
-=head1 BUGS
-
-=head1 SEE ALSO
-
-L<FS::Record>, L<FS::agent>, L<FS::type_pkgs>, L<FS::cust_main>,
-L<FS::part_pkg>, schema.html from the base documentation.
-
-=cut
-
-1;
-
diff --git a/FS/FS/cust_bill.pm b/FS/FS/cust_bill.pm
deleted file mode 100644 (file)
index 5a9fdd0..0000000
+++ /dev/null
@@ -1,976 +0,0 @@
-package FS::cust_bill;
-
-use strict;
-use vars qw( @ISA $conf $money_char );
-use vars qw( $lpr $invoice_from $smtpmachine );
-use vars qw( $processor );
-use vars qw( $xaction $E_NoErr );
-use vars qw( $bop_processor $bop_login $bop_password $bop_action @bop_options );
-use vars qw( $invoice_lines @buf ); #yuck
-use Date::Format;
-use Mail::Internet 1.44;
-use Mail::Header;
-use Text::Template;
-use FS::Record qw( qsearch qsearchs );
-use FS::cust_main;
-use FS::cust_bill_pkg;
-use FS::cust_credit;
-use FS::cust_pay;
-use FS::cust_pkg;
-use FS::cust_credit_bill;
-use FS::cust_pay_batch;
-use FS::cust_bill_event;
-
-@ISA = qw( FS::Record );
-
-#ask FS::UID to run this stuff for us later
-$FS::UID::callback{'FS::cust_bill'} = sub { 
-
-  $conf = new FS::Conf;
-
-  $money_char = $conf->config('money_char') || '$';  
-
-  $lpr = $conf->config('lpr');
-  $invoice_from = $conf->config('invoice_from');
-  $smtpmachine = $conf->config('smtpmachine');
-
-  if ( $conf->exists('cybercash3.2') ) {
-    require CCMckLib3_2;
-      #qw($MCKversion %Config InitConfig CCError CCDebug CCDebug2);
-    require CCMckDirectLib3_2;
-      #qw(SendCC2_1Server);
-    require CCMckErrno3_2;
-      #qw(MCKGetErrorMessage $E_NoErr);
-    import CCMckErrno3_2 qw($E_NoErr);
-
-    my $merchant_conf;
-    ($merchant_conf,$xaction)= $conf->config('cybercash3.2');
-    my $status = &CCMckLib3_2::InitConfig($merchant_conf);
-    if ( $status != $E_NoErr ) {
-      warn "CCMckLib3_2::InitConfig error:\n";
-      foreach my $key (keys %CCMckLib3_2::Config) {
-        warn "  $key => $CCMckLib3_2::Config{$key}\n"
-      }
-      my($errmsg) = &CCMckErrno3_2::MCKGetErrorMessage($status);
-      die "CCMckLib3_2::InitConfig fatal error: $errmsg\n";
-    }
-    $processor='cybercash3.2';
-  } elsif ( $conf->exists('business-onlinepayment') ) {
-    ( $bop_processor,
-      $bop_login,
-      $bop_password,
-      $bop_action,
-      @bop_options
-    ) = $conf->config('business-onlinepayment');
-    $bop_action ||= 'normal authorization';
-    eval "use Business::OnlinePayment";  
-    $processor="Business::OnlinePayment::$bop_processor";
-  }
-
-};
-
-=head1 NAME
-
-FS::cust_bill - Object methods for cust_bill records
-
-=head1 SYNOPSIS
-
-  use FS::cust_bill;
-
-  $record = new FS::cust_bill \%hash;
-  $record = new FS::cust_bill { 'column' => 'value' };
-
-  $error = $record->insert;
-
-  $error = $new_record->replace($old_record);
-
-  $error = $record->delete;
-
-  $error = $record->check;
-
-  ( $total_previous_balance, @previous_cust_bill ) = $record->previous;
-
-  @cust_bill_pkg_objects = $cust_bill->cust_bill_pkg;
-
-  ( $total_previous_credits, @previous_cust_credit ) = $record->cust_credit;
-
-  @cust_pay_objects = $cust_bill->cust_pay;
-
-  $tax_amount = $record->tax;
-
-  @lines = $cust_bill->print_text;
-  @lines = $cust_bill->print_text $time;
-
-=head1 DESCRIPTION
-
-An FS::cust_bill object represents an invoice; a declaration that a customer
-owes you money.  The specific charges are itemized as B<cust_bill_pkg> records
-(see L<FS::cust_bill_pkg>).  FS::cust_bill inherits from FS::Record.  The
-following fields are currently supported:
-
-=over 4
-
-=item invnum - primary key (assigned automatically for new invoices)
-
-=item custnum - customer (see L<FS::cust_main>)
-
-=item _date - specified as a UNIX timestamp; see L<perlfunc/"time">.  Also see
-L<Time::Local> and L<Date::Parse> for conversion functions.
-
-=item charged - amount of this invoice
-
-=item printed - deprecated
-
-=item closed - books closed flag, empty or `Y'
-
-=back
-
-=head1 METHODS
-
-=over 4
-
-=item new HASHREF
-
-Creates a new invoice.  To add the invoice to the database, see L<"insert">.
-Invoices are normally created by calling the bill method of a customer object
-(see L<FS::cust_main>).
-
-=cut
-
-sub table { 'cust_bill'; }
-
-=item insert
-
-Adds this invoice to the database ("Posts" the invoice).  If there is an error,
-returns the error, otherwise returns false.
-
-=item delete
-
-Currently unimplemented.  I don't remove invoices because there would then be
-no record you ever posted this invoice (which is bad, no?)
-
-=cut
-
-sub delete {
-  my $self = shift;
-  return "Can't delete closed invoice" if $self->closed =~ /^Y/i;
-  $self->SUPER::delete(@_);
-}
-
-=item replace OLD_RECORD
-
-Replaces the OLD_RECORD with this one in the database.  If there is an error,
-returns the error, otherwise returns false.
-
-Only printed may be changed.  printed is normally updated by calling the
-collect method of a customer object (see L<FS::cust_main>).
-
-=cut
-
-sub replace {
-  my( $new, $old ) = ( shift, shift );
-  return "Can't change custnum!" unless $old->custnum == $new->custnum;
-  #return "Can't change _date!" unless $old->_date eq $new->_date;
-  return "Can't change _date!" unless $old->_date == $new->_date;
-  return "Can't change charged!" unless $old->charged == $new->charged;
-
-  $new->SUPER::replace($old);
-}
-
-=item check
-
-Checks all fields to make sure this is a valid invoice.  If there is an error,
-returns the error, otherwise returns false.  Called by the insert and replace
-methods.
-
-=cut
-
-sub check {
-  my $self = shift;
-
-  my $error =
-    $self->ut_numbern('invnum')
-    || $self->ut_number('custnum')
-    || $self->ut_numbern('_date')
-    || $self->ut_money('charged')
-    || $self->ut_numbern('printed')
-    || $self->ut_enum('closed', [ '', 'Y' ])
-  ;
-  return $error if $error;
-
-  return "Unknown customer"
-    unless qsearchs( 'cust_main', { 'custnum' => $self->custnum } );
-
-  $self->_date(time) unless $self->_date;
-
-  $self->printed(0) if $self->printed eq '';
-
-  ''; #no error
-}
-
-=item previous
-
-Returns a list consisting of the total previous balance for this customer, 
-followed by the previous outstanding invoices (as FS::cust_bill objects also).
-
-=cut
-
-sub previous {
-  my $self = shift;
-  my $total = 0;
-  my @cust_bill = sort { $a->_date <=> $b->_date }
-    grep { $_->owed != 0 && $_->_date < $self->_date }
-      qsearch( 'cust_bill', { 'custnum' => $self->custnum } ) 
-  ;
-  foreach ( @cust_bill ) { $total += $_->owed; }
-  $total, @cust_bill;
-}
-
-=item cust_bill_pkg
-
-Returns the line items (see L<FS::cust_bill_pkg>) for this invoice.
-
-=cut
-
-sub cust_bill_pkg {
-  my $self = shift;
-  qsearch( 'cust_bill_pkg', { 'invnum' => $self->invnum } );
-}
-
-=item cust_bill_event
-
-Returns the completed invoice events (see L<FS::cust_bill_event>) for this
-invoice.
-
-=cut
-
-sub cust_bill_event {
-  my $self = shift;
-  qsearch( 'cust_bill_event', { 'invnum' => $self->invnum } );
-}
-
-
-=item cust_main
-
-Returns the customer (see L<FS::cust_main>) for this invoice.
-
-=cut
-
-sub cust_main {
-  my $self = shift;
-  qsearchs( 'cust_main', { 'custnum' => $self->custnum } );
-}
-
-=item cust_credit
-
-Depreciated.  See the cust_credited method.
-
- #Returns a list consisting of the total previous credited (see
- #L<FS::cust_credit>) and unapplied for this customer, followed by the previous
- #outstanding credits (FS::cust_credit objects).
-
-=cut
-
-sub cust_credit {
-  use Carp;
-  croak "FS::cust_bill->cust_credit depreciated; see ".
-        "FS::cust_bill->cust_credit_bill";
-  #my $self = shift;
-  #my $total = 0;
-  #my @cust_credit = sort { $a->_date <=> $b->_date }
-  #  grep { $_->credited != 0 && $_->_date < $self->_date }
-  #    qsearch('cust_credit', { 'custnum' => $self->custnum } )
-  #;
-  #foreach (@cust_credit) { $total += $_->credited; }
-  #$total, @cust_credit;
-}
-
-=item cust_pay
-
-Depreciated.  See the cust_bill_pay method.
-
-#Returns all payments (see L<FS::cust_pay>) for this invoice.
-
-=cut
-
-sub cust_pay {
-  use Carp;
-  croak "FS::cust_bill->cust_pay depreciated; see FS::cust_bill->cust_bill_pay";
-  #my $self = shift;
-  #sort { $a->_date <=> $b->_date }
-  #  qsearch( 'cust_pay', { 'invnum' => $self->invnum } )
-  #;
-}
-
-=item cust_bill_pay
-
-Returns all payment applications (see L<FS::cust_bill_pay>) for this invoice.
-
-=cut
-
-sub cust_bill_pay {
-  my $self = shift;
-  sort { $a->_date <=> $b->_date }
-    qsearch( 'cust_bill_pay', { 'invnum' => $self->invnum } );
-}
-
-=item cust_credited
-
-Returns all applied credits (see L<FS::cust_credit_bill>) for this invoice.
-
-=cut
-
-sub cust_credited {
-  my $self = shift;
-  sort { $a->_date <=> $b->_date }
-    qsearch( 'cust_credit_bill', { 'invnum' => $self->invnum } )
-  ;
-}
-
-=item tax
-
-Returns the tax amount (see L<FS::cust_bill_pkg>) for this invoice.
-
-=cut
-
-sub tax {
-  my $self = shift;
-  my $total = 0;
-  my @taxlines = qsearch( 'cust_bill_pkg', { 'invnum' => $self->invnum ,
-                                             'pkgnum' => 0 } );
-  foreach (@taxlines) { $total += $_->setup; }
-  $total;
-}
-
-=item owed
-
-Returns the amount owed (still outstanding) on this invoice, which is charged
-minus all payment applications (see L<FS::cust_bill_pay>) and credit
-applications (see L<FS::cust_credit_bill>).
-
-=cut
-
-sub owed {
-  my $self = shift;
-  my $balance = $self->charged;
-  $balance -= $_->amount foreach ( $self->cust_bill_pay );
-  $balance -= $_->amount foreach ( $self->cust_credited );
-  $balance = sprintf( "%.2f", $balance);
-  $balance =~ s/^\-0\.00$/0.00/; #yay ieee fp
-  $balance;
-}
-
-=item send
-
-Sends this invoice to the destinations configured for this customer: send
-emails or print.  See L<FS::cust_main_invoice>.
-
-=cut
-
-sub send {
-  my($self,$template) = @_;
-  my @print_text = $self->print_text('', $template);
-  my @invoicing_list = $self->cust_main->invoicing_list;
-
-  if ( grep { $_ ne 'POST' } @invoicing_list ) { #email invoice
-    #false laziness w/FS::cust_pay::delete & fs_signup_server && ::realtime_card
-    #$ENV{SMTPHOSTS} = $smtpmachine;
-    $ENV{MAILADDRESS} = $invoice_from;
-    my $header = new Mail::Header ( [
-      "From: $invoice_from",
-      "To: ". join(', ', grep { $_ ne 'POST' } @invoicing_list ),
-      "Sender: $invoice_from",
-      "Reply-To: $invoice_from",
-      "Date: ". time2str("%a, %d %b %Y %X %z", time),
-      "Subject: Invoice",
-    ] );
-    my $message = new Mail::Internet (
-      'Header' => $header,
-      'Body' => [ @print_text ], #( date)
-    );
-    $!=0;
-    $message->smtpsend( Host => $smtpmachine )
-      or $message->smtpsend( Host => $smtpmachine, Debug => 1 )
-        or return "(customer # ". $self->custnum. ") can't send invoice email".
-                  " to ". join(', ', grep { $_ ne 'POST' } @invoicing_list ).
-                  " via server $smtpmachine with SMTP: $!";
-
-  }
-
-  if ( ! @invoicing_list || grep { $_ eq 'POST' } @invoicing_list ) { #postal
-    open(LPR, "|$lpr")
-      or return "Can't open pipe to $lpr: $!";
-    print LPR @print_text;
-    close LPR
-      or return $! ? "Error closing $lpr: $!"
-                   : "Exit status $? from $lpr";
-  }
-
-  '';
-
-}
-
-=item comp
-
-Pays this invoice with a compliemntary payment.  If there is an error,
-returns the error, otherwise returns false.
-
-=cut
-
-sub comp {
-  my $self = shift;
-  my $cust_pay = new FS::cust_pay ( {
-    'invnum'   => $self->invnum,
-    'paid'     => $self->owed,
-    '_date'    => '',
-    'payby'    => 'COMP',
-    'payinfo'  => $self->cust_main->payinfo,
-    'paybatch' => '',
-  } );
-  $cust_pay->insert;
-}
-
-=item realtime_card
-
-Attempts to pay this invoice with a Business::OnlinePayment realtime gateway.
-See http://search.cpan.org/search?mode=module&query=Business%3A%3AOnlinePayment
-for supproted processors.
-
-=cut
-
-sub realtime_card {
-  my $self = shift;
-  my $cust_main = $self->cust_main;
-  my $amount = $self->owed;
-
-  unless ( $processor =~ /^Business::OnlinePayment::(.*)$/ ) {
-    return "Real-time card processing not enabled (processor $processor)";
-  }
-  my $bop_processor = $1; #hmm?
-
-  my $address = $cust_main->address1;
-  $address .= ", ". $cust_main->address2 if $cust_main->address2;
-
-  #fix exp. date
-  #$cust_main->paydate =~ /^(\d+)\/\d*(\d{2})$/;
-  $cust_main->paydate =~ /^\d{2}(\d{2})[\/\-](\d+)[\/\-]\d+$/;
-  my $exp = "$2/$1";
-
-  my($payname, $payfirst, $paylast);
-  if ( $cust_main->payname ) {
-    $payname = $cust_main->payname;
-    $payname =~ /^\s*([\w \,\.\-\']*)?\s+([\w\,\.\-\']+)\s*$/
-      or do {
-              #$dbh->rollback if $oldAutoCommit;
-              return "Illegal payname $payname";
-            };
-    ($payfirst, $paylast) = ($1, $2);
-  } else {
-    $payfirst = $cust_main->getfield('first');
-    $paylast = $cust_main->getfield('last');
-    $payname =  "$payfirst $paylast";
-  }
-
-  my @invoicing_list = grep { $_ ne 'POST' } $cust_main->invoicing_list;
-  if ( $conf->exists('emailinvoiceauto')
-       || ( $conf->exists('emailinvoiceonly') && ! @invoicing_list ) ) {
-    push @invoicing_list, $cust_main->default_invoicing_list;
-  }
-  my $email = $invoicing_list[0];
-
-  my( $action1, $action2 ) = split(/\s*\,\s*/, $bop_action );
-
-  my $description = 'Internet Services';
-  if ( $conf->exists('business-onlinepayment-description') ) {
-    my $dtempl = $conf->config('business-onlinepayment-description');
-
-    my $agent_obj = $cust_main->agent
-      or die "can't retreive agent for $cust_main (agentnum ".
-             $cust_main->agentnum. ")";
-    my $agent = $agent_obj->agent;
-    my $pkgs = join(', ',
-      map { $_->cust_pkg->part_pkg->pkg }
-        grep { $_->pkgnum } $self->cust_bill_pkg
-    );
-    $description = eval qq("$dtempl");
-
-  }
-  
-  my $transaction =
-    new Business::OnlinePayment( $bop_processor, @bop_options );
-  $transaction->content(
-    'type'           => 'CC',
-    'login'          => $bop_login,
-    'password'       => $bop_password,
-    'action'         => $action1,
-    'description'    => $description,
-    'amount'         => $amount,
-    'invoice_number' => $self->invnum,
-    'customer_id'    => $self->custnum,
-    'last_name'      => $paylast,
-    'first_name'     => $payfirst,
-    'name'           => $payname,
-    'address'        => $address,
-    'city'           => $cust_main->city,
-    'state'          => $cust_main->state,
-    'zip'            => $cust_main->zip,
-    'country'        => $cust_main->country,
-    'card_number'    => $cust_main->payinfo,
-    'expiration'     => $exp,
-    'referer'        => 'http://cleanwhisker.420.am/',
-    'email'          => $email,
-    'phone'          => $cust_main->daytime || $cust_main->night,
-  );
-  $transaction->submit();
-
-  if ( $transaction->is_success() && $action2 ) {
-    my $auth = $transaction->authorization;
-    my $ordernum = $transaction->order_number;
-
-    #warn "********* $auth ***********\n";
-    #warn "********* $ordernum ***********\n";
-    my $capture =
-      new Business::OnlinePayment( $bop_processor, @bop_options );
-
-    $capture->content(
-      action         => $action2,
-      login          => $bop_login,
-      password       => $bop_password,
-      order_number   => $ordernum,
-      amount         => $amount,
-      authorization  => $auth,
-      description    => $description,
-    );
-
-    $capture->submit();
-
-    unless ( $capture->is_success ) {
-      my $e = "Authorization sucessful but capture failed, invnum #".
-              $self->invnum. ': '.  $capture->result_code.
-              ": ". $capture->error_message;
-      warn $e;
-      return $e;
-    }
-
-  }
-
-  if ( $transaction->is_success() ) {
-
-    my $cust_pay = new FS::cust_pay ( {
-       'invnum'   => $self->invnum,
-       'paid'     => $amount,
-       '_date'     => '',
-       'payby'    => 'CARD',
-       'payinfo'  => $cust_main->payinfo,
-       'paybatch' => "$processor:". $transaction->authorization,
-    } );
-    my $error = $cust_pay->insert;
-    if ( $error ) {
-      # gah, even with transactions.
-      my $e = 'WARNING: Card debited but database not updated - '.
-              'error applying payment, invnum #' . $self->invnum.
-              " ($processor): $error";
-      warn $e;
-      return $e;
-    } else {
-      return '';
-    }
-  #} elsif ( $options{'report_badcard'} ) {
-  } else {
-
-    my $perror = "$processor error, invnum #". $self->invnum. ': '.
-                 $transaction->result_code. ": ". $transaction->error_message;
-
-    if ( $conf->exists('emaildecline')
-         && grep { $_ ne 'POST' } $cust_main->invoicing_list
-    ) {
-      my @templ = $conf->config('declinetemplate');
-      my $template = new Text::Template (
-        TYPE   => 'ARRAY',
-        SOURCE => [ map "$_\n", @templ ],
-      ) or return "($perror) can't create template: $Text::Template::ERROR";
-      $template->compile()
-        or return "($perror) can't compile template: $Text::Template::ERROR";
-
-      my $templ_hash = { error => $transaction->error_message };
-
-      #false laziness w/FS::cust_pay::delete & fs_signup_server && ::send
-      $ENV{MAILADDRESS} = $invoice_from;
-      my $header = new Mail::Header ( [
-        "From: $invoice_from",
-        "To: ". join(', ', grep { $_ ne 'POST' } $cust_main->invoicing_list ),
-        "Sender: $invoice_from",
-        "Reply-To: $invoice_from",
-        "Date: ". time2str("%a, %d %b %Y %X %z", time),
-        "Subject: Your credit card could not be processed",
-      ] );
-      my $message = new Mail::Internet (
-        'Header' => $header,
-        'Body' => [ $template->fill_in(HASH => $templ_hash) ],
-      );
-      $!=0;
-      $message->smtpsend( Host => $smtpmachine )
-        or $message->smtpsend( Host => $smtpmachine, Debug => 1 )
-          or return "($perror) (customer # ". $self->custnum.
-            ") can't send card decline email to ".
-            join(', ', grep { $_ ne 'POST' } $cust_main->invoicing_list ).
-            " via server $smtpmachine with SMTP: $!";
-    }
-  
-    return $perror;
-  }
-
-}
-
-=item realtime_card_cybercash
-
-Attempts to pay this invoice with the CyberCash CashRegister realtime gateway.
-
-=cut
-
-sub realtime_card_cybercash {
-  my $self = shift;
-  my $cust_main = $self->cust_main;
-  my $amount = $self->owed;
-
-  return "CyberCash CashRegister real-time card processing not enabled!"
-    unless $processor eq 'cybercash3.2';
-
-  my $address = $cust_main->address1;
-  $address .= ", ". $cust_main->address2 if $cust_main->address2;
-
-  #fix exp. date
-  #$cust_main->paydate =~ /^(\d+)\/\d*(\d{2})$/;
-  $cust_main->paydate =~ /^\d{2}(\d{2})[\/\-](\d+)[\/\-]\d+$/;
-  my $exp = "$2/$1";
-
-  #
-
-  my $paybatch = $self->invnum. 
-                  '-' . time2str("%y%m%d%H%M%S", time);
-
-  my $payname = $cust_main->payname ||
-                $cust_main->getfield('first').' '.$cust_main->getfield('last');
-
-  my $country = $cust_main->country eq 'US' ? 'USA' : $cust_main->country;
-
-  my @full_xaction = ( $xaction,
-    'Order-ID'     => $paybatch,
-    'Amount'       => "usd $amount",
-    'Card-Number'  => $cust_main->getfield('payinfo'),
-    'Card-Name'    => $payname,
-    'Card-Address' => $address,
-    'Card-City'    => $cust_main->getfield('city'),
-    'Card-State'   => $cust_main->getfield('state'),
-    'Card-Zip'     => $cust_main->getfield('zip'),
-    'Card-Country' => $country,
-    'Card-Exp'     => $exp,
-  );
-
-  my %result;
-  %result = &CCMckDirectLib3_2::SendCC2_1Server(@full_xaction);
-  
-  if ( $result{'MStatus'} eq 'success' ) { #cybercash smps v.2 or 3
-    my $cust_pay = new FS::cust_pay ( {
-       'invnum'   => $self->invnum,
-       'paid'     => $amount,
-       '_date'     => '',
-       'payby'    => 'CARD',
-       'payinfo'  => $cust_main->payinfo,
-       'paybatch' => "$processor:$paybatch",
-    } );
-    my $error = $cust_pay->insert;
-    if ( $error ) {
-      # gah, even with transactions.
-      my $e = 'WARNING: Card debited but database not updated - '.
-              'error applying payment, invnum #' . $self->invnum.
-              " (CyberCash Order-ID $paybatch): $error";
-      warn $e;
-      return $e;
-    } else {
-      return '';
-    }
-#  } elsif ( $result{'Mstatus'} ne 'failure-bad-money'
-#            || $options{'report_badcard'}
-#          ) {
-  } else {
-     return 'Cybercash error, invnum #' . 
-       $self->invnum. ':'. $result{'MErrMsg'};
-  }
-
-}
-
-=item batch_card
-
-Adds a payment for this invoice to the pending credit card batch (see
-L<FS::cust_pay_batch>).
-
-=cut
-
-sub batch_card {
-  my $self = shift;
-  my $cust_main = $self->cust_main;
-
-  my $cust_pay_batch = new FS::cust_pay_batch ( {
-    'invnum'   => $self->getfield('invnum'),
-    'custnum'  => $cust_main->getfield('custnum'),
-    'last'     => $cust_main->getfield('last'),
-    'first'    => $cust_main->getfield('first'),
-    'address1' => $cust_main->getfield('address1'),
-    'address2' => $cust_main->getfield('address2'),
-    'city'     => $cust_main->getfield('city'),
-    'state'    => $cust_main->getfield('state'),
-    'zip'      => $cust_main->getfield('zip'),
-    'country'  => $cust_main->getfield('country'),
-    'trancode' => 77,
-    'cardnum'  => $cust_main->getfield('payinfo'),
-    'exp'      => $cust_main->getfield('paydate'),
-    'payname'  => $cust_main->getfield('payname'),
-    'amount'   => $self->owed,
-  } );
-  my $error = $cust_pay_batch->insert;
-  die $error if $error;
-
-  '';
-}
-
-=item print_text [TIME];
-
-Returns an text invoice, as a list of lines.
-
-TIME an optional value used to control the printing of overdue messages.  The
-default is now.  It isn't the date of the invoice; that's the `_date' field.
-It is specified as a UNIX timestamp; see L<perlfunc/"time">.  Also see
-L<Time::Local> and L<Date::Parse> for conversion functions.
-
-=cut
-
-sub print_text {
-
-  my( $self, $today, $template ) = @_;
-  $today ||= time;
-#  my $invnum = $self->invnum;
-  my $cust_main = qsearchs('cust_main', { 'custnum', $self->custnum } );
-  $cust_main->payname( $cust_main->first. ' '. $cust_main->getfield('last') )
-    unless $cust_main->payname;
-
-  my( $pr_total, @pr_cust_bill ) = $self->previous; #previous balance
-#  my( $cr_total, @cr_cust_credit ) = $self->cust_credit; #credits
-  #my $balance_due = $self->owed + $pr_total - $cr_total;
-  my $balance_due = $self->owed + $pr_total;
-
-  #my @collect = ();
-  #my($description,$amount);
-  @buf = ();
-
-  #previous balance
-  foreach ( @pr_cust_bill ) {
-    push @buf, [
-      "Previous Balance, Invoice #". $_->invnum. 
-                 " (". time2str("%x",$_->_date). ")",
-      $money_char. sprintf("%10.2f",$_->owed)
-    ];
-  }
-  if (@pr_cust_bill) {
-    push @buf,['','-----------'];
-    push @buf,[ 'Total Previous Balance',
-                $money_char. sprintf("%10.2f",$pr_total ) ];
-    push @buf,['',''];
-  }
-
-  #new charges
-  foreach ( $self->cust_bill_pkg ) {
-
-    if ( $_->pkgnum ) {
-
-      my($cust_pkg)=qsearchs('cust_pkg', { 'pkgnum', $_->pkgnum } );
-      my($part_pkg)=qsearchs('part_pkg',{'pkgpart'=>$cust_pkg->pkgpart});
-      my($pkg)=$part_pkg->pkg;
-
-      if ( $_->setup != 0 ) {
-        push @buf, [ "$pkg Setup", $money_char. sprintf("%10.2f",$_->setup) ];
-        push @buf,
-          map { [ "  ". $_->[0]. ": ". $_->[1], '' ] } $cust_pkg->labels;
-      }
-
-      if ( $_->recur != 0 ) {
-        push @buf, [
-          "$pkg (" . time2str("%x",$_->sdate) . " - " .
-                                time2str("%x",$_->edate) . ")",
-          $money_char. sprintf("%10.2f",$_->recur)
-        ];
-        push @buf,
-          map { [ "  ". $_->[0]. ": ". $_->[1], '' ] } $cust_pkg->labels;
-      }
-
-    } else { #pkgnum Tax
-      push @buf,["Tax", $money_char. sprintf("%10.2f",$_->setup) ] 
-        if $_->setup != 0;
-    }
-  }
-
-  push @buf,['','-----------'];
-  push @buf,['Total New Charges',
-             $money_char. sprintf("%10.2f",$self->charged) ];
-  push @buf,['',''];
-
-  push @buf,['','-----------'];
-  push @buf,['Total Charges',
-             $money_char. sprintf("%10.2f",$self->charged + $pr_total) ];
-  push @buf,['',''];
-
-  #credits
-  foreach ( $self->cust_credited ) {
-
-    #something more elaborate if $_->amount ne $_->cust_credit->credited ?
-
-    my $reason = substr($_->cust_credit->reason,0,32);
-    $reason .= '...' if length($reason) < length($_->cust_credit->reason);
-    $reason = " ($reason) " if $reason;
-    push @buf,[
-      "Credit #". $_->crednum. " (". time2str("%x",$_->cust_credit->_date) .")".
-        $reason,
-      $money_char. sprintf("%10.2f",$_->amount)
-    ];
-  }
-  #foreach ( @cr_cust_credit ) {
-  #  push @buf,[
-  #    "Credit #". $_->crednum. " (" . time2str("%x",$_->_date) .")",
-  #    $money_char. sprintf("%10.2f",$_->credited)
-  #  ];
-  #}
-
-  #get & print payments
-  foreach ( $self->cust_bill_pay ) {
-
-    #something more elaborate if $_->amount ne ->cust_pay->paid ?
-
-    push @buf,[
-      "Payment received ". time2str("%x",$_->cust_pay->_date ),
-      $money_char. sprintf("%10.2f",$_->amount )
-    ];
-  }
-
-  #balance due
-  push @buf,['','-----------'];
-  push @buf,['Balance Due', $money_char. 
-    sprintf("%10.2f", $balance_due ) ];
-
-  #create the template
-  my $templatefile = 'invoice_template';
-  $templatefile .= "_$template" if $template;
-  my @invoice_template = $conf->config($templatefile)
-  or die "cannot load config file $templatefile";
-  $invoice_lines = 0;
-  my $wasfunc = 0;
-  foreach ( grep /invoice_lines\(\d+\)/, @invoice_template ) { #kludgy
-    /invoice_lines\((\d+)\)/;
-    $invoice_lines += $1;
-    $wasfunc=1;
-  }
-  die "no invoice_lines() functions in template?" unless $wasfunc;
-  my $invoice_template = new Text::Template (
-    TYPE   => 'ARRAY',
-    SOURCE => [ map "$_\n", @invoice_template ],
-  ) or die "can't create new Text::Template object: $Text::Template::ERROR";
-  $invoice_template->compile()
-    or die "can't compile template: $Text::Template::ERROR";
-
-  #setup template variables
-  package FS::cust_bill::_template; #!
-  use vars qw( $invnum $date $page $total_pages @address $overdue @buf );
-
-  $invnum = $self->invnum;
-  $date = $self->_date;
-  $page = 1;
-
-  if ( $FS::cust_bill::invoice_lines ) {
-    $total_pages =
-      int( scalar(@FS::cust_bill::buf) / $FS::cust_bill::invoice_lines );
-    $total_pages++
-      if scalar(@FS::cust_bill::buf) % $FS::cust_bill::invoice_lines;
-  } else {
-    $total_pages = 1;
-  }
-
-  #format address (variable for the template)
-  my $l = 0;
-  @address = ( '', '', '', '', '', '' );
-  package FS::cust_bill; #!
-  $FS::cust_bill::_template::address[$l++] =
-    $cust_main->payname.
-      ( ( $cust_main->payby eq 'BILL' ) && $cust_main->payinfo
-        ? " (P.O. #". $cust_main->payinfo. ")"
-        : ''
-      )
-  ;
-  $FS::cust_bill::_template::address[$l++] = $cust_main->company
-    if $cust_main->company;
-  $FS::cust_bill::_template::address[$l++] = $cust_main->address1;
-  $FS::cust_bill::_template::address[$l++] = $cust_main->address2
-    if $cust_main->address2;
-  $FS::cust_bill::_template::address[$l++] =
-    $cust_main->city. ", ". $cust_main->state. "  ".  $cust_main->zip;
-  $FS::cust_bill::_template::address[$l++] = $cust_main->country
-    unless $cust_main->country eq 'US';
-
-       #  #overdue? (variable for the template)
-       #  $FS::cust_bill::_template::overdue = ( 
-       #    $balance_due > 0
-       #    && $today > $self->_date 
-       ##    && $self->printed > 1
-       #    && $self->printed > 0
-       #  );
-
-  #and subroutine for the template
-
-  sub FS::cust_bill::_template::invoice_lines {
-    my $lines = shift or return @buf;
-    map { 
-      scalar(@buf) ? shift @buf : [ '', '' ];
-    }
-    ( 1 .. $lines );
-  }
-
-
-  #and fill it in
-  $FS::cust_bill::_template::page = 1;
-  my $lines;
-  my @collect;
-  while (@buf) {
-    push @collect, split("\n",
-      $invoice_template->fill_in( PACKAGE => 'FS::cust_bill::_template' )
-    );
-    $FS::cust_bill::_template::page++;
-  }
-
-  map "$_\n", @collect;
-
-}
-
-=back
-
-=head1 VERSION
-
-$Id: cust_bill.pm,v 1.38 2002-06-26 02:37:48 ivan Exp $
-
-=head1 BUGS
-
-The delete method.
-
-print_text formatting (and some logic :/) is in source, but needs to be
-slurped in from a file.  Also number of lines ($=).
-
-missing print_ps for a nice postscript copy (maybe HylaFAX-cover-page-style
-or something similar so the look can be completely customized?)
-
-=head1 SEE ALSO
-
-L<FS::Record>, L<FS::cust_main>, L<FS::cust_bill_pay>, L<FS::cust_pay>,
-L<FS::cust_bill_pkg>, L<FS::cust_bill_credit>, schema.html from the base
-documentation.
-
-=cut
-
-1;
-
diff --git a/FS/FS/cust_bill_event.pm b/FS/FS/cust_bill_event.pm
deleted file mode 100644 (file)
index f631987..0000000
+++ /dev/null
@@ -1,175 +0,0 @@
-package FS::cust_bill_event;
-
-use strict;
-use vars qw( @ISA );
-use FS::Record qw( qsearch qsearchs );
-use FS::part_bill_event;
-
-@ISA = qw(FS::Record);
-
-=head1 NAME
-
-FS::cust_bill_event - Object methods for cust_bill_event records
-
-=head1 SYNOPSIS
-
-  use FS::cust_bill_event;
-
-  $record = new FS::cust_bill_event \%hash;
-  $record = new FS::cust_bill_event { 'column' => 'value' };
-
-  $error = $record->insert;
-
-  $error = $new_record->replace($old_record);
-
-  $error = $record->delete;
-
-  $error = $record->check;
-
-=head1 DESCRIPTION
-
-An FS::cust_bill_event object represents an complete invoice event.
-FS::cust_bill_event inherits from FS::Record.  The following fields are
-currently supported:
-
-=over 4
-
-=item eventnum - primary key
-
-=item invnum - invoice (see L<FS::cust_bill>)
-
-=item eventpart - event definition (see L<FS::part_bill_event>)
-
-=item _date - specified as a UNIX timestamp; see L<perlfunc/"time">.  Also see
-L<Time::Local> and L<Date::Parse> for conversion functions.
-
-=back
-
-=head1 METHODS
-
-=over 4
-
-=item new HASHREF
-
-Creates a new completed invoice event.  To add the compelted invoice event to
-the database, see L<"insert">.
-
-Note that this stores the hash reference, not a distinct copy of the hash it
-points to.  You can ask the object for a copy with the I<hash> method.
-
-=cut
-
-# the new method can be inherited from FS::Record, if a table method is defined
-
-sub table { 'cust_bill_event'; }
-
-=item insert
-
-Adds this record to the database.  If there is an error, returns the error,
-otherwise returns false.
-
-=cut
-
-# the insert method can be inherited from FS::Record
-
-=item delete
-
-Delete this record from the database.
-
-=cut
-
-# the delete method can be inherited from FS::Record
-
-=item replace OLD_RECORD
-
-Replaces the OLD_RECORD with this one in the database.  If there is an error,
-returns the error, otherwise returns false.
-
-=cut
-
-# the replace method can be inherited from FS::Record
-
-=item check
-
-Checks all fields to make sure this is a valid completed invoice event.  If
-there is an error, returns the error, otherwise returns false.  Called by the
-insert and replace methods.
-
-=cut
-
-# the check method should currently be supplied - FS::Record contains some
-# data checking routines
-
-sub check {
-  my $self = shift;
-
-  my $error = $self->ut_numbern('eventnum')
-    || $self->ut_number('invnum')
-    || $self->ut_number('eventpart')
-    || $self->ut_number('_date')
-    || $self->ut_enum('status', [qw( done failed )])
-    || $self->ut_textn('statustext')
-  ;
-
-  return "Unknown invnum"
-    unless qsearchs( 'cust_bill' ,{ 'invnum' => $self->invnum } );
-
-  return "Unknown eventpart"
-    unless qsearchs( 'part_bill_event' ,{ 'eventpart' => $self->eventpart } );
-
-  ''; #no error
-}
-
-=item part_bill_event
-
-Returns the invoice event definition (see L<FS::part_bill_event>) for this
-completed invoice event.
-
-=cut
-
-sub part_bill_event {
-  my $self = shift;
-  qsearchs( 'part_bill_event', { 'eventpart' => $self->eventpart } );
-}
-
-=item cust_bill
-
-Returns the invoice (see L<FS::cust_bill>) for this completed invoice event.
-
-=cut
-
-sub cust_bill {
-  my $self = shift;
-  qsearchs( 'cust_bill', { 'invnum' => $self->invnum } );
-}
-
-=item retry
-
-Changes the status of this event from B<done> to B<failed>, allowing it to be
-retried.
-
-=cut
-
-sub retry {
-  my $self = shift;
-  return '' unless $self->status eq 'done';
-  my $old = ref($self)->new( { $self->hash } );
-  $self->status('failed');
-  $self->replace($old);
-}
-
-=back
-
-=head1 BUGS
-
-Far too early in the morning.
-
-=head1 SEE ALSO
-
-L<FS::part_bill_event>, L<FS::cust_bill>, L<FS::Record>, schema.html from the
-base documentation.
-
-=cut
-
-1;
-
diff --git a/FS/FS/cust_bill_pay.pm b/FS/FS/cust_bill_pay.pm
deleted file mode 100644 (file)
index 913704b..0000000
+++ /dev/null
@@ -1,219 +0,0 @@
-package FS::cust_bill_pay;
-
-use strict;
-use vars qw( @ISA );
-use FS::Record qw( qsearch qsearchs dbh );
-use FS::cust_bill;
-use FS::cust_pay;
-
-@ISA = qw( FS::Record );
-
-=head1 NAME
-
-FS::cust_bill_pay - Object methods for cust_bill_pay records
-
-=head1 SYNOPSIS 
-
-  use FS::cust_bill_pay;
-
-  $record = new FS::cust_bill_pay \%hash;
-  $record = new FS::cust_bill_pay { 'column' => 'value' };
-
-  $error = $record->insert;
-
-  $error = $new_record->replace($old_record);
-
-  $error = $record->delete;
-
-  $error = $record->check;
-
-=head1 DESCRIPTION
-
-An FS::cust_bill_pay object represents the application of a payment to a
-specific invoice.  FS::cust_bill_pay inherits from FS::Record.  The following
-fields are currently supported:
-
-=over 4
-
-=item billpaynum - primary key (assigned automatically)
-
-=item invnum - Invoice (see L<FS::cust_bill>)
-
-=item paynum - Payment (see L<FS::cust_pay>)
-
-=item amount - Amount of the payment to apply to the specific invoice.
-
-=item _date - specified as a UNIX timestamp; see L<perlfunc/"time">.  Also see
-L<Time::Local> and L<Date::Parse> for conversion functions.
-
-=back
-
-=head1 METHODS
-
-=over 4 
-
-=item new HASHREF
-
-Creates a new record.  To add the record to the database, see L<"insert">.
-
-=cut
-
-sub table { 'cust_bill_pay'; }
-
-=item insert
-
-Adds this record to the database.  If there is an error, returns the error,
-otherwise returns false.
-
-=cut
-
-sub insert {
-  my $self = shift;
-
-  local $SIG{HUP} = 'IGNORE';
-  local $SIG{INT} = 'IGNORE';
-  local $SIG{QUIT} = 'IGNORE';
-  local $SIG{TERM} = 'IGNORE';
-  local $SIG{TSTP} = 'IGNORE';
-  local $SIG{PIPE} = 'IGNORE';
-
-  my $oldAutoCommit = $FS::UID::AutoCommit;
-  local $FS::UID::AutoCommit = 0;
-  my $dbh = dbh;
-
-  my $error = $self->check;
-  return $error if $error;
-
-  $error = $self->SUPER::insert;
-
-  my $cust_pay = qsearchs('cust_pay', { 'paynum' => $self->paynum } ) or do {
-    $dbh->rollback if $oldAutoCommit;
-    return "unknown cust_pay.paynum: ". $self->paynum;
-  };
-
-  my $pay_total = 0;
-  $pay_total += $_ foreach map { $_->amount }
-    qsearch('cust_bill_pay', { 'paynum' => $self->paynum } );
-
-  if ( sprintf("%.2f", $pay_total) > sprintf("%.2f", $cust_pay->paid) ) {
-    $dbh->rollback if $oldAutoCommit;
-    return "total cust_bill_pay.amount $pay_total for paynum ". $self->paynum.
-           " greater than cust_pay.paid ". $cust_pay->paid;
-  }
-
-  my $cust_bill = qsearchs('cust_bill', { 'invnum' => $self->invnum } ) or do {
-    $dbh->rollback if $oldAutoCommit;
-    return "unknown cust_bill.invnum: ". $self->invnum;
-  };
-
-  my $bill_total = 0;
-  $bill_total += $_ foreach map { $_->amount }
-    qsearch('cust_bill_pay', { 'invnum' => $self->invnum } );
-  $bill_total += $_ foreach map { $_->amount } 
-    qsearch('cust_credit_bill', { 'invnum' => $self->invnum } );
-  if ( sprintf("%.2f", $bill_total) > sprintf("%.2f", $cust_bill->charged) ) {
-    $dbh->rollback if $oldAutoCommit;
-    return "total cust_bill_pay.amount and cust_credit_bill.amount $bill_total".
-           " for invnum ". $self->invnum.
-           " greater than cust_bill.charged ". $cust_bill->charged;
-  }
-
-  $dbh->commit or die $dbh->errstr if $oldAutoCommit;
-
-  '';
-}
-
-=item delete
-
-Deletes this payment application, unless the closed flag for the parent payment
-(see L<FS::cust_pay>) is set.
-
-=cut
-
-sub delete {
-  my $self = shift;
-  return "Can't delete application for closed payment"
-    if $self->cust_pay->closed =~ /^Y/i;
-  $self->SUPER::delete(@_);
-}
-
-=item replace OLD_RECORD
-
-Currently unimplemented (accounting reasons).
-
-=cut
-
-sub replace {
-   return "Can't (yet?) modify cust_bill_pay records!";
-}
-
-=item check
-
-Checks all fields to make sure this is a valid payment.  If there is an error,
-returns the error, otherwise returns false.  Called by the insert method.
-
-=cut
-
-sub check {
-  my $self = shift;
-
-  my $error = 
-    $self->ut_numbern('billpaynum')
-    || $self->ut_number('invnum')
-    || $self->ut_number('paynum')
-    || $self->ut_money('amount')
-    || $self->ut_numbern('_date')
-  ;
-  return $error if $error;
-
-  return "amount must be > 0" if $self->amount <= 0;
-
-  $self->_date(time) unless $self->_date;
-
-  ''; #no error
-}
-
-=item cust_pay 
-
-Returns the payment (see L<FS::cust_pay>)
-
-=cut
-
-sub cust_pay {
-  my $self = shift;
-  qsearchs( 'cust_pay', { 'paynum' => $self->paynum } );
-}
-
-=item cust_bill 
-
-Returns the invoice (see L<FS::cust_bill>)
-
-=cut
-
-sub cust_bill {
-  my $self = shift;
-  qsearchs( 'cust_bill', { 'invnum' => $self->invnum } );
-}
-
-=back
-
-=head1 VERSION
-
-$Id: cust_bill_pay.pm,v 1.12 2002-02-07 22:29:34 ivan Exp $
-
-=head1 BUGS
-
-Delete and replace methods.
-
-the checks for over-applied payments could be better done like the ones in
-cust_bill_credit
-
-=head1 SEE ALSO
-
-L<FS::cust_pay>, L<FS::cust_bill>, L<FS::Record>, schema.html from the
-base documentation.
-
-=cut
-
-1;
-
diff --git a/FS/FS/cust_bill_pkg.pm b/FS/FS/cust_bill_pkg.pm
deleted file mode 100644 (file)
index 72f9ce4..0000000
+++ /dev/null
@@ -1,155 +0,0 @@
-package FS::cust_bill_pkg;
-
-use strict;
-use vars qw( @ISA );
-use FS::Record qw( qsearchs );
-use FS::cust_pkg;
-use FS::cust_bill;
-
-@ISA = qw(FS::Record );
-
-=head1 NAME
-
-FS::cust_bill_pkg - Object methods for cust_bill_pkg records
-
-=head1 SYNOPSIS
-
-  use FS::cust_bill_pkg;
-
-  $record = new FS::cust_bill_pkg \%hash;
-  $record = new FS::cust_bill_pkg { 'column' => 'value' };
-
-  $error = $record->insert;
-
-  $error = $new_record->replace($old_record);
-
-  $error = $record->delete;
-
-  $error = $record->check;
-
-=head1 DESCRIPTION
-
-An FS::cust_bill_pkg object represents an invoice line item.
-FS::cust_bill_pkg inherits from FS::Record.  The following fields are currently
-supported:
-
-=over 4
-
-=item invnum - invoice (see L<FS::cust_bill>)
-
-=item pkgnum - package (see L<FS::cust_pkg>) or 0 for the special virtual sales tax package
-
-=item setup - setup fee
-
-=item recur - recurring fee
-
-=item sdate - starting date of recurring fee
-
-=item edate - ending date of recurring fee
-
-=back
-
-sdate and edate are specified as UNIX timestamps; see L<perlfunc/"time">.  Also
-see L<Time::Local> and L<Date::Parse> for conversion functions.
-
-=head1 METHODS
-
-=over 4
-
-=item new HASHREF
-
-Creates a new line item.  To add the line item to the database, see
-L<"insert">.  Line items are normally created by calling the bill method of a
-customer object (see L<FS::cust_main>).
-
-=cut
-
-sub table { 'cust_bill_pkg'; }
-
-=item insert
-
-Adds this line item to the database.  If there is an error, returns the error,
-otherwise returns false.
-
-=item delete
-
-Currently unimplemented.  I don't remove line items because there would then be
-no record the items ever existed (which is bad, no?)
-
-=cut
-
-sub delete {
-  return "Can't delete cust_bill_pkg records!";
-}
-
-=item replace OLD_RECORD
-
-Currently unimplemented.  This would be even more of an accounting nightmare
-than deleteing the items.  Just don't do it.
-
-=cut
-
-sub replace {
-  return "Can't modify cust_bill_pkg records!";
-}
-
-=item check
-
-Checks all fields to make sure this is a valid line item.  If there is an
-error, returns the error, otherwise returns false.  Called by the insert
-method.
-
-=cut
-
-sub check {
-  my $self = shift;
-
-  my $error =
-    $self->ut_number('pkgnum')
-      || $self->ut_number('invnum')
-      || $self->ut_money('setup')
-      || $self->ut_money('recur')
-      || $self->ut_numbern('sdate')
-      || $self->ut_numbern('edate')
-  ;
-  return $error if $error;
-
-  if ( $self->pkgnum != 0 ) { #allow unchecked pkgnum 0 for tax! (add to part_pkg?)
-    return "Unknown pkgnum ". $self->pkgnum
-      unless qsearchs( 'cust_pkg', { 'pkgnum' => $self->pkgnum } );
-  }
-
-  return "Unknown invnum"
-    unless qsearchs( 'cust_bill' ,{ 'invnum' => $self->invnum } );
-
-  ''; #no error
-}
-
-=item cust_pkg
-
-Returns the package (see L<FS::cust_pkg>) for this invoice line item.
-
-=cut
-
-sub cust_pkg {
-  my $self = shift;
-  qsearchs( 'cust_pkg', { 'pkgnum' => $self->pkgnum } );
-}
-
-=back
-
-=head1 VERSION
-
-$Id: cust_bill_pkg.pm,v 1.3 2002-04-06 22:32:43 ivan Exp $
-
-=head1 BUGS
-
-=head1 SEE ALSO
-
-L<FS::Record>, L<FS::cust_bill>, L<FS::cust_pkg>, L<FS::cust_main>, schema.html
-from the base documentation.
-
-=cut
-
-1;
-
diff --git a/FS/FS/cust_credit.pm b/FS/FS/cust_credit.pm
deleted file mode 100644 (file)
index 284d59d..0000000
+++ /dev/null
@@ -1,260 +0,0 @@
-package FS::cust_credit;
-
-use strict;
-use vars qw( @ISA $conf $unsuspendauto );
-use FS::UID qw( dbh getotaker );
-use FS::Record qw( qsearch qsearchs );
-use FS::cust_main;
-use FS::cust_refund;
-use FS::cust_credit_bill;
-
-@ISA = qw( FS::Record );
-
-#ask FS::UID to run this stuff for us later
-$FS::UID::callback{'FS::cust_credit'} = sub { 
-
-  $conf = new FS::Conf;
-  $unsuspendauto = $conf->exists('unsuspendauto');
-
-};
-
-=head1 NAME
-
-FS::cust_credit - Object methods for cust_credit records
-
-=head1 SYNOPSIS
-
-  use FS::cust_credit;
-
-  $record = new FS::cust_credit \%hash;
-  $record = new FS::cust_credit { 'column' => 'value' };
-
-  $error = $record->insert;
-
-  $error = $new_record->replace($old_record);
-
-  $error = $record->delete;
-
-  $error = $record->check;
-
-=head1 DESCRIPTION
-