Index for ledgersmb

Dumped on 2011-10-01

# Index of database - ledgersmb

- public

- ac_tax_form
- acc_trans
- account
- account_checkpoint
- account_heading
- account_link
- account_link_description
- ap
- ar
- assembly
- asset_class
- asset_dep_method
- asset_disposal_method
- asset_item
- asset_note
- asset_report
- asset_report_class
- asset_report_line
- asset_rl_to_disposal_method
- asset_unit_class
- audittrail
- batch
- batch_class
- business
- chart
- company
- company_to_contact
- company_to_entity
- company_to_location
- contact_class
- country
- country_tax_form
- cr_coa_to_account
- cr_report
- cr_report_line
- custom_field_catalog
- custom_table_catalog
- customertax
- defaults
- department
- dpt_trans
- eca_note
- eca_to_contact
- eca_to_location
- employee_search
- employees
- entity
- entity_bank_account
- entity_class
- entity_class_to_entity
- entity_credit_account
- entity_employee
- entity_note
- entity_other_name
- exchangerate
- file_base
- file_class
- file_links
- file_order
- file_order_links
- file_order_to_order
- file_order_to_tx
- file_part
- file_secondary_attachment
- file_transaction
- file_tx_links
- file_tx_to_order
- file_view_catalog
- gifi
- gl
- inventory
- invoice
- invoice_note
- invoice_tax_form
- jcitems
- language
- location
- location_class
- lsmb_roles
- makemodel
- menu_acl
- menu_attribute
- menu_friendly
- menu_node
- mime_type
- new_shipto
- note
- note_class
- oe
- oe_class
- open_forms
- orderitems
- overpayments
- parts
- parts_translation
- partscustomer
- partsgroup
- partsgroup_translation
- partstax
- partsvendor
- payment
- payment_links
- payment_type
- payments_queue
- pending_job
- periods
- person
- person_to_company
- person_to_contact
- person_to_entity
- person_to_location
- pricegroup
- project
- project_translation
- recon_payee
- recurring
- recurringemail
- recurringprint
- role_view
- salutation
- session
- sic
- status
- tax
- tax_extended
- taxcategory
- taxmodule
- transactions
- translation
- user_listable
- user_preference
- users
- vendortax
- voucher
- warehouse
- yearend
- _entity_location_save(in_country_code integer, in_mail_code integer, in_state integer, in_city text, in_line_three text, in_line_two text, in_line_one text, in_location_class text, in_location_id text, in_entity_id integer)
- _get_parser_from_curcfg()
- account__get_by_link_desc(in_description text)
- account__get_from_accno(in_accno text)
- account__get_taxes()
- account__obtain_balance(in_account_id date, in_transdate integer)
- account__save_tax(in_old_validto integer, in_taxmodule_id date, in_pass numeric, in_taxnumber text, in_rate integer, in_validto integer, in_chart_id date)
- account_get(in_id integer)
- account_has_transactions(in_id integer)
- account_heading__list()
- account_heading_get(in_id integer)
- account_heading_list()
- account_heading_save(in_parent integer, in_description text, in_accno text, in_id integer)
- account_save(in_link integer, in_tax text, in_contra text, in_heading bpchar, in_gifi_accno text, in_category integer, in_description boolean, in_accno boolean, in_id text[])
- add_custom_field(character varying, character varying, character varying)
- admin__add_function_to_group(in_role text, in_func text)
- admin__add_user_to_role(in_role text, in_username text)
- admin__create_group(in_group_name text)
- admin__delete_group(in_group_name text)
- admin__delete_user(in_drop_role text, in_username boolean)
- admin__drop_session(in_session_id integer)
- admin__get_roles()
- admin__get_roles_for_user(in_user_id integer)
- admin__get_user(in_user_id integer)
- admin__is_group(in_group_name text)
- admin__is_user(in_user text)
- admin__list_roles(in_username text)
- admin__list_sessions()
- admin__remove_function_from_group(in_role text, in_func text)
- admin__remove_user_from_role(in_role text, in_username text)
- admin__save_user(in_import integer, in_password integer, in_username text, in_entity_id text, in_id boolean)
- admin__search_users(in_dob text, in_ssn text, in_last_name text, in_first_name text, in_username date)
- as_array(anyelement)
- asset__get(in_tag integer, in_id text)
- asset__import_from_disposal(in_id integer)
- asset__save(in_exp_account_id integer, in_dep_account_id integer, in_asset_account_id text, in_invoice_id text, in_department_id date, in_warehouse_id numeric, in_start_depreciation numeric, in_salvage_value numeric, in_usable_life date, in_purchase_value integer, in_purchase_date integer, in_tag integer, in_description integer, in_asset_class integer, in_id integer)
- asset__search(in_salvage_value integer, in_usable_life text, in_purchase_value text, in_purchase_date date, in_tag numeric, in_description numeric, in_asset_class numeric)
- asset_class__get(in_id integer)
- asset_class__get_asset_accounts()
- asset_class__get_dep_accounts()
- asset_class__get_dep_method(in_asset_class integer)
- asset_class__get_dep_methods()
- asset_class__list()
- asset_class__save(in_unit_label integer, in_label integer, in_method integer, in_dep_account_id integer, in_asset_account_id text, in_id text)
- asset_class__search(in_label integer, in_method integer, in_dep_account_id integer, in_asset_account_id text)
- asset_dep__straight_line_base(in_dep_to_date numeric, in_basis numeric, in_used numeric, in_life numeric, in_base_life numeric)
- asset_dep__used_months(in_usable_life date, in_dep_date date, in_last_dep numeric)
- asset_dep_get_usable_life_yr(in_dep_date numeric, in_start_date date, in_usable_life date)
- asset_dep_straight_line_month(in_report_id integer[], in_report_date date, in_asset_ids integer)
- asset_dep_straight_line_yr_m(in_report_id integer[], in_report_date date, in_asset_ids integer)
- asset_depreciation__approve(in_expense_acct integer, in_report_id integer)
- asset_disposal__approve(in_asset_acct integer, in_loss_acct integer, in_gain_acct integer, in_id integer)
- asset_item__add_note(in_note integer, in_subject text, in_id text)
- asset_item__search(in_dep_account_id integer, in_asset_account_id integer, in_invoice_id text, in_department_id text, in_warehouse_id date, in_start_depreciation numeric, in_salvage_value numeric, in_usable_life numeric, in_purchase_value date, in_purchase_date integer, in_tag integer, in_description integer, in_asset_class integer, in_id integer)
- asset_nbv_report()
- asset_report__approve(in_loss_acct integer, in_gain_acct integer, in_expense_acct integer, in_id integer)
- asset_report__begin_disposal(in_report_class integer, in_report_date date, in_asset_class integer)
- asset_report__begin_import(in_report_date integer, in_asset_class date)
- asset_report__disposal_gl(in_loss_acct integer, in_gain_acct integer, in_id integer)
- asset_report__dispose(in_percent_disposed integer, in_dm integer, in_amount numeric, in_asset_id integer, in_id numeric)
- asset_report__generate(in_report_date boolean, in_asset_class integer, in_depreciation date)
- asset_report__generate_gl(in_accum_account_id integer, in_report_id integer)
- asset_report__get(in_id integer)
- asset_report__get_disposal(in_id integer)
- asset_report__get_disposal_methods()
- asset_report__get_expense_accts()
- asset_report__get_gain_accts()
- asset_report__get_lines(in_id integer)
- asset_report__get_loss_accts()
- asset_report__import(in_obsolete_other text, in_accum_dep text, in_dep_report_id numeric, in_invoice_id numeric, in_asset_class_id numeric, in_exp_account_id date, in_dep_account_id date, in_asset_account_id integer, in_department_id integer, in_location_id integer, in_start_depreciation integer, in_purchase_date integer, in_usable_life integer, in_salvage_value integer, in_purchase_value integer, in_tag numeric, in_description boolean)
- asset_report__record_approve(in_id integer)
- asset_report__save(in_submit integer, in_asset_class date, in_report_class integer, in_report_date integer, in_id boolean)
- asset_report__search(in_entered_by date, in_approved date, in_asset_class integer, in_end_date boolean, in_start_date integer)
- asset_report_partial_disposal_details(in_id integer)
- avgcost(integer)
- batch_create(in_batch_date text, in_batch_class text, in_description text, in_batch_number date)
- batch_delete(in_batch_id integer)
- batch_get_class_id(in_type text)
- batch_get_users()
- batch_list_classes()
- batch_post(in_batch_id integer)
- batch_search(in_approved integer, in_amount_lt text, in_amount_gt integer, in_date_to date, in_date_from date, in_created_by_eid numeric, in_description numeric, in_class_id boolean)
- batch_search_empty(in_approved integer, in_amount_lt text, in_amount_gt integer, in_created_by_eid numeric, in_description numeric, in_class_id boolean)
- batch_search_mini(in_approved integer, in_created_by_eid text, in_description integer, in_class_id boolean)
- business_type__list()
- chart_get_ar_ap(in_account_class integer)
- chart_list_all()
- chart_list_cash(in_account_class integer)
- chart_list_discount(in_account_class integer)
- chart_list_overpayment(in_account_class integer)
- chart_list_search(in_link_desc text, in_search text)
- check_department()
- check_expiration()
- company__delete_contact(in_contact integer, in_contact_class_id integer, in_company_id text)
- company__delete_location(in_location_class integer, in_location_id integer, in_company_id integer)
- company__get_all_accounts(in_entity_class integer, in_entity_id integer)
- company__list_bank_account(in_entity_id integer)
- company__list_contacts(in_entity_id integer)
- company__list_locations(in_entity_id integer)
- company__list_notes(in_entity_id integer)
- company__location_save(in_created integer, in_country_code integer, in_mail_code integer, in_state text, in_city text, in_line_two text, in_line_one text, in_location_class text, in_location_id integer, in_entity_id date)
- company__next_id()
- company__save_contact(in_contact integer, in_description integer, in_contact_class text, in_entity_id text)
- company__search(in_control_code integer, in_legal_name text, in_business_id text[], in_date_to text, in_date_from text, in_country text, in_mail_code text, in_state text, in_city text, in_address date, in_meta_number date, in_contact_info integer, in_contact text, in_account_class text)
- company_get_billing_info(in_id integer)
- company_retrieve(in_entity_id integer)
- company_save(in_country_id_t integer, in_sic_code text, in_entity_id integer, in_tax_id text, in_name text, in_entity_class integer, in_control_code text, in_id integer)
- compound_array(anyarray)
- concat(tsvector, tsvector)
- concat_colon(text)
- concat_colon(text, text)
- connectby(text, text, text, text, integer)
- connectby(text, text, text, text, integer, text)
- connectby(text, text, text, text, text, integer)
- connectby(text, text, text, text, text, integer, text)
- cr_coa_to_account_save(in_description text, in_accno text)
- cr_report_block_changing_approved()
- crosstab(text)
- crosstab(text, integer)
- crosstab(text, text)
- crosstab2(text)
- crosstab3(text)
- crosstab4(text)
- currency_get_exchangerate(in_account_class bpchar, in_date date, in_currency integer)
- customer_location_save(in_country_id integer, in_mail_code integer, in_state text, in_city text, in_line_three text, in_line_two text, in_line_one text, in_location_class text, in_entity_id integer)
- date_get_all_years()
- days_in_month(in_date date)
- defaults_get_defaultcurrency()
- del_department()
- del_exchangerate()
- del_recurring()
- del_yearend()
- department__list_all()
- department_list(in_role bpchar)
- dex_init(internal)
- dex_lexize(internal, internal, integer)
- draft__search(in_amount_ge text, in_amount_le text, in_to_date date, in_from_date date, in_with_accno numeric, in_type numeric)
- draft_approve(in_id integer)
- draft_delete(in_id integer)
- drop_custom_field(character varying, character varying)
- eca__delete_contact(in_contact integer, in_contact_class_id integer, in_credit_id text)
- eca__delete_location(in_location_class integer, in_location_id integer, in_credit_id integer)
- eca__get_entity(in_credit_id integer)
- eca__get_taxes(in_credit_id integer)
- eca__list_contacts(in_credit_id integer)
- eca__list_locations(in_credit_id integer)
- eca__list_notes(in_credit_id integer)
- eca__location_save(in_old_location_class integer, in_country_code integer, in_mail_code integer, in_state text, in_city text, in_line_three text, in_line_two text, in_line_one text, in_location_class text, in_location_id integer, in_credit_id integer)
- eca__save_bank_account(in_bank_account_id integer, in_iban integer, in_bic text, in_credit_id text, in_entity_id integer)
- eca__save_contact(in_old_contact_class integer, in_old_contact integer, in_contact text, in_description text, in_contact_class text, in_credit_id integer)
- eca__save_notes(in_subject integer, in_note text, in_credit_id text)
- eca__set_taxes(in_tax_ids integer, in_credit_id integer[])
- eca_history(in_inc_closed text, in_inc_open text, in_account_class text, in_start_to text, in_start_from text, in_type text, in_to_date text, in_from_date text, in_country_id text, in_notes integer, in_salesperson date, in_zip date, in_state bpchar, in_city date, in_address_line date, in_contact_info integer, in_meta_number boolean, in_name boolean)
- eca_history_summary(in_inc_closed text, in_inc_open text, in_account_class text, in_start_to text, in_start_from text, in_type text, in_to_date text, in_from_date text, in_country_id text, in_notes integer, in_salesperson date, in_zip date, in_state bpchar, in_city date, in_address_line date, in_contact_info integer, in_meta_number boolean, in_name boolean)
- employee__get(in_entity_id integer)
- employee__get_user(in_entity_id integer)
- employee__list_managers(in_id integer)
- employee__save(in_employee_number integer, in_manager_id date, in_sales date, in_ssn date, in_role text, in_dob text, in_end_date boolean, in_start_date integer, in_entity_id text)
- employee__search(in_notes text, in_last_name date, in_middle_name date, in_first_name text, in_startdate_to text, in_startdate_from text, in_employeenumber text)
- employee_search(in_sales date, in_enddatefrom date, in_enddateto character varying, in_notes text, in_name date, in_startdateto date, in_startdatefrom boolean)
- employee_set_location(in_location integer, in_employee integer)
- entity__delete_bank_account(in_id integer, in_entity_id integer)
- entity__get_by_cc(in_control_code text)
- entity__get_entity(in_entity_id integer)
- entity__list_classes()
- entity__list_credit(in_entity_class integer, in_entity_id integer)
- entity__save_bank_account(in_bank_account_id integer, in_iban text, in_bic text, in_entity_id integer)
- entity__save_notes(in_subject integer, in_note text, in_entity_id text)
- entity_credit__get(in_id integer)
- entity_credit_get_id(in_meta_number integer, in_entity_class integer, in_entity_id text)
- entity_credit_get_id_by_meta_number(in_account_class text, in_meta_number integer)
- entity_credit_save(in_taxform_id integer, in_pay_to_name integer, in_cash_account_id integer, in_ar_ap_account_id text, in_threshold numeric, in_enddate boolean, in_startdate numeric, in_curr integer, in_pricegroup_id integer, in_language character varying, in_business_id integer, in_meta_number character varying, in_terms integer, in_discount_terms bpchar, in_creditlimit date, in_taxincluded date, in_discount numeric, in_description integer, in_entity_id integer, in_entity_class text, in_credit_id integer)
- entity_list_contact_class()
- entity_save(in_entity_class integer, in_name text, in_entity_id integer)
- eoy_close_books(in_retention_acc_id date, in_description text, in_reference text, in_end_date integer)
- eoy_create_checkpoint(in_end_date date)
- eoy_earnings_accounts()
- eoy_reopen_books(in_end_date date)
- eoy_zero_accounts(in_retention_acc_id date, in_description text, in_reference text, in_end_date integer)
- file__attach_to_order(in_file_class bytea, in_ref_key integer, in_id text, in_description text, in_file_name integer, in_mime_type_id integer, in_content integer)
- file__attach_to_part(in_file_class bytea, in_ref_key integer, in_id text, in_description text, in_file_name integer, in_mime_type_id integer, in_content integer)
- file__attach_to_tx(in_file_class bytea, in_ref_key integer, in_id text, in_description text, in_file_name integer, in_mime_type_id integer, in_content integer)
- file__get(in_file_class integer, in_id integer)
- file__get_mime_type(in_mime_type_text integer, in_mime_type_id text)
- file__list_by(in_file_class integer, in_ref_key integer)
- file__list_links(in_file_class integer, in_ref_key integer)
- file_links_vrebuild()
- form_check(in_form_id integer, in_session_id integer)
- form_close(in_form_id integer, in_session_id integer)
- form_open(in_session_id integer)
- get_covers(tsvector, tsquery)
- get_fractional_month(in_date_second date, in_date_first date)
- get_fractional_year(in_date_to date, in_date_from date)
- get_link_descriptions()
- gin_extract_trgm(text, internal)
- gin_extract_trgm(text, internal, smallint, internal, internal)
- gin_trgm_consistent(internal, smallint, text, integer, internal, internal)
- gl_audit_trail_append()
- gtrgm_compress(internal)
- gtrgm_consistent(internal, text, integer, oid, internal)
- gtrgm_decompress(internal)
- gtrgm_in(cstring)
- gtrgm_out(public.gtrgm)
- gtrgm_penalty(internal, internal, internal)
- gtrgm_picksplit(internal, internal)
- gtrgm_same(public.gtrgm, public.gtrgm, internal)
- gtrgm_union(bytea, internal)
- headline(oid, text, tsquery)
- headline(oid, text, tsquery, text)
- headline(text, text, tsquery)
- headline(text, text, tsquery, text)
- headline(text, tsquery)
- headline(text, tsquery, text)
- invoice__get_by_vendor_number(in_invoice_number text, in_meta_nunber text)
- is_leapyear(in_date date)
- is_same_month(in_date2 date, in_date1 date)
- is_same_year(in_date2 date, in_date1 date)
- je_get_default_lines()
- je_set_default_lines(in_rowcount integer)
- lastcost(integer)
- leap_days(in_year_to integer, in_year_from integer)
- length(tsvector)
- lexize(oid, text)
- lexize(text)
- lexize(text, text)
- list_taxforms(in_entity_id integer)
- location__get(in_id integer)
- location_delete(in_id integer)
- location_list_all()
- location_list_class()
- location_list_country()
- location_save(in_country integer, in_zipcode text, in_state text, in_city text, in_address3 text, in_address2 text, in_address1 text, in_location_id integer)
- location_search(in_country character varying, in_zipcode character varying, in_state character varying, in_city character varying, in_address2 character varying, in_address1 character varying)
- lock_record(in_session_id integer, in_id integer)
- menu_children(in_parent_id integer)
- menu_generate()
- menu_insert(in_label integer, in_position integer, in_parent_id text)
- months_passed(in_end timestamp without time zone, in_start timestamp without time zone)
- next_leap_year_calc(is_end date, in_date boolean)
- normal_rand(integer, double precision, double precision)
- numnode(tsquery)
- parse(oid, text)
- parse(text)
- parse(text, text)
- payment__reverse(in_voucher_id text, in_batch_id date, in_account_class integer, in_date_reversed text, in_cash_accno date, in_credit_id integer, in_date_paid integer, in_source integer)
- payment__search(in_account_class text, in_cash_accno date, in_credit_id date, in_date_to integer, in_date_from text, in_source integer)
- payment_bulk_post(in_curr numeric[], in_exchangerate integer, in_account_class text, in_payment_date numeric, in_cash_accno text, in_ar_ap_accno text, in_total date, in_source integer, in_batch_id numeric, in_transactions text)
- payment_gather_header_info(in_payment_id integer, in_account_class integer)
- payment_gather_line_info(in_payment_id integer, in_account_class integer)
- payment_get_all_accounts(in_account_class integer)
- payment_get_all_contact_invoices(in_meta_number integer, in_ar_ap_accno integer, in_batch_id bpchar, in_date_to date, in_date_from date, in_currency integer, in_business_id text, in_account_class text)
- payment_get_available_overpayment_amount(in_entity_credit_id integer, in_account_class integer)
- payment_get_entity_accounts(in_vc_idn integer, in_vc_name text, in_account_class text)
- payment_get_open_accounts(in_account_class integer)
- payment_get_open_invoice(in_invnumber integer, in_department_id integer, in_amountto bpchar, in_amountfrom date, in_dateto date, in_datefrom numeric, in_curr numeric, in_entity_credit_id integer, in_account_class text)
- payment_get_open_invoices(in_department_id integer, in_amountto integer, in_amountfrom bpchar, in_dateto date, in_datefrom date, in_curr numeric, in_entity_credit_id numeric, in_account_class integer)
- payment_get_open_overpayment_entities(in_account_class integer)
- payment_get_unused_overpayment(in_chart_id integer, in_entity_credit_id integer, in_account_class integer)
- payment_get_vc_info(in_location_class_id integer, in_entity_credit_id integer)
- payment_post(in_approved date, in_ovp_payment_id integer, in_op_account_id integer, in_op_memo bpchar, in_op_source text, in_op_cash_account_id integer, in_op_amount text, in_transaction_id integer[], in_memo numeric[], in_source boolean[], in_cash_approved text[], in_amount text[], in_cash_account_id integer[], in_gl_description numeric[], in_department_id integer[], in_notes text[], in_curr text[], in_entity_credit_id integer[], in_account_class integer[], in_datepaid boolean)
- payment_type__get_label(in_payment_type_id integer)
- payment_type__list()
- payments_get_open_currencies(in_account_class integer)
- payments_set_exchangerate(in_datepaid integer, in_curr numeric, in_exchangerate bpchar, in_account_class date)
- periods_get()
- person__delete_contact(in_contact integer, in_contact_class_id integer, in_person_id text)
- person__delete_location(in_location_class integer, in_location_id integer, in_person_id integer)
- person__get_my_entity_id()
- person__list_bank_account(in_entity_id integer)
- person__list_contacts(in_entity_id integer)
- person__list_locations(in_entity_id integer)
- person__list_notes(in_entity_id integer)
- person__list_salutations()
- person__save(in_country_id integer, in_last_name integer, in_middle_name text, in_first_name text, in_salutation_id text, in_entity_id integer)
- person__save_contact(in_contact_new integer, in_contact_orig integer, in_contact_class text, in_entity_id text)
- person__save_location(in_old_location_class integer, in_country_code integer, in_mail_code integer, in_state text, in_city text, in_line_three text, in_line_two text, in_line_one text, in_location_class text, in_location_id integer, in_entity_id integer)
- plainto_tsquery(oid, text)
- plainto_tsquery(text)
- plainto_tsquery(text, text)
- project_list_open(in_date date)
- prsd_end(internal)
- prsd_getlexeme(internal, internal, internal)
- prsd_headline(internal, internal, internal)
- prsd_lextype(internal)
- prsd_start(internal, integer)
- querytree(tsquery)
- rank(real[], tsvector, tsquery)
- rank(real[], tsvector, tsquery, integer)
- rank(tsvector, tsquery)
- rank(tsvector, tsquery, integer)
- rank_cd(real[], tsvector, tsquery)
- rank_cd(real[], tsvector, tsquery, integer)
- rank_cd(tsvector, tsquery)
- rank_cd(tsvector, tsquery, integer)
- reconciliation__account_list()
- reconciliation__add_entry(in_amount integer, in_date text, in_type text, in_scn timestamp without time zone, in_report_id numeric)
- reconciliation__delete_my_report(in_report_id integer)
- reconciliation__delete_unapproved(in_report_id integer)
- reconciliation__get_cleared_balance(in_chart_id integer)
- reconciliation__get_current_balance(in_date integer, in_account_id date)
- reconciliation__get_total(in_report_id integer)
- reconciliation__new_report_id(in_end_date integer, in_total numeric, in_chart_id date)
- reconciliation__pending_transactions(in_their_total date, in_report_id integer, in_chart_id integer, in_end_date numeric)
- reconciliation__report_approve(in_report_id integer)
- reconciliation__report_details(in_report_id integer)
- reconciliation__report_details_payee(in_report_id integer)
- reconciliation__report_summary(in_report_id integer)
- reconciliation__save_set(in_line_ids integer, in_report_id integer[])
- reconciliation__search(in_approved date, in_submitted date, in_chart_id numeric, in_balance_to numeric, in_balance_from integer, in_date_to boolean, in_date_from boolean)
- reconciliation__submit_set(in_line_ids integer, in_report_id integer[])
- report_invoice_aging(in_entity_class integer, in_entity_id integer)
- report_trial_balance(in_gifi date, in_project_id date, in_department_id integer, in_dateto integer, in_datefrom boolean)
- reset_tsearch()
- rewrite(tsquery, text)
- rewrite(tsquery, tsquery, tsquery)
- rewrite(tsquery[])
- rewrite_accum(tsquery, tsquery[])
- rewrite_finish(tsquery)
- save_taxform(in_taxform_name integer, in_country_code text)
- session_check(in_token integer, in_session_id text)
- set_curcfg(integer)
- set_curcfg(text)
- set_curdict(integer)
- set_curdict(text)
- set_curprs(integer)
- set_curprs(text)
- set_limit(real)
- setting__get_currencies()
- setting_get(in_key character varying)
- setting_get_default_accounts()
- setting_increment(in_key character varying)
- setting_set(in_value character varying, in_key character varying)
- setweight(tsvector, "char")
- show_curcfg()
- show_limit()
- show_trgm(text)
- similarity(text, text)
- similarity_op(text, text)
- snb_en_init(internal)
- snb_lexize(internal, internal, integer)
- snb_ru_init(internal)
- snb_ru_init_koi8(internal)
- snb_ru_init_utf8(internal)
- spell_init(internal)
- spell_lexize(internal, internal, integer)
- stat(text)
- stat(text, text)
- strip(tsvector)
- syn_init(internal)
- syn_lexize(internal, internal, integer)
- tax_form__get(in_form_id integer)
- tax_form__list_all()
- tax_form__list_ext()
- tax_form__save(in_default_reportable integer, in_form_name integer, in_country_id text, in_id boolean)
- tax_form_details_report(in_meta_number integer, in_end date, in_begin date, in_tax_form_id text)
- tax_form_summary_report(in_end integer, in_begin date, in_tax_form_id date)
- thesaurus_init(internal)
- thesaurus_lexize(internal, internal, integer, internal)
- to_args(in_args text[], in_base text[])
- to_args(text[])
- to_tsquery(oid, text)
- to_tsquery(text)
- to_tsquery(text, text)
- to_tsvector(oid, text)
- to_tsvector(text)
- to_tsvector(text, text)
- token_type()
- token_type(integer)
- token_type(text)
- track_global_sequence()
- trigger_parts_short()
- trigger_pending_job()
- ts_debug(text)
- tsearch2()
- tsq_mcontained(tsquery, tsquery)
- tsq_mcontains(tsquery, tsquery)
- tsquery_and(tsquery, tsquery)
- tsquery_not(tsquery)
- tsquery_or(tsquery, tsquery)
- unlock(in_id integer)
- unlock_all()
- user__change_password(in_new_password text)
- user__check_my_expiration()
- user__expires_soon()
- user__get_all_users()
- user__get_preferences(in_user_id integer)
- user__save_preferences(in_printer text, in_stylesheet text, in_language text, in_numberformat text, in_dateformat text)
- voucher__delete(in_voucher_id integer)
- voucher_get_batch(in_batch_id integer)
- voucher_list(in_batch_id integer)
- warehouse__list_all()

## Table: ac_tax_form

Mapping acc_trans to country_tax_form for reporting purposes.

ac_tax_form Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
acc_trans.entry_id | entry_id | integer | PRIMARY KEY
| reportable | boolean |

Index - Schema public

## Table: acc_trans

This table stores line items for financial transactions. Please note that payments in 1.3 are not full-fledged transactions.

acc_trans Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
transactions.id | trans_id | integer | NOT NULL
account.id | chart_id | integer | NOT NULL
| amount | numeric |
| transdate | date | DEFAULT ('now'::text)::date
| source | text | Document Source identifier for individual line items, usually used for payments.
| cleared | boolean | DEFAULT false
| fx_transaction | boolean | DEFAULT false
| project_id | integer |
| memo | text |
| invoice_id | integer |
| approved | boolean | DEFAULT true
| cleared_on | date |
| reconciled_on | date |
voucher.id | voucher_id | integer |
| entry_id | serial | PRIMARY KEY

Tables referencing this one via Foreign Key Constraints:

- ac_tax_form

- cr_report_line

- payment_links

- tax_extended

acc_trans_chart_id_key chart_id acc_trans_source_key lower(source) acc_trans_trans_id_key trans_id acc_trans_transdate_key transdate acc_trans_voucher_id_idx voucher_id

Index - Schema public

## Table: account

This table stores the main account info.

account Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| id | serial | UNIQUE NOT NULL
| accno | text | PRIMARY KEY
| description | text |
| category | character(1) | NOT NULL
| gifi_accno | text |
account_heading.id | heading | integer | NOT NULL
| contra | boolean | NOT NULL DEFAULT false
| tax | boolean | NOT NULL DEFAULT false

Tables referencing this one via Foreign Key Constraints:

- acc_trans

- account_checkpoint

- account_link

- asset_class

- asset_item

- cr_coa_to_account

- cr_report

- customertax

- entity_credit_account

- partstax

- tax

- vendortax

Index - Schema public

## Table: account_checkpoint

This table holds account balances at various dates. Transactions MUST NOT be posted prior to the latest end_date in this table, and no unapproved transactions (vouchers or drafts) can remain in the closed period.

account_checkpoint Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| end_date | date | PRIMARY KEY
account.id | account_id | integer | PRIMARY KEY
| amount | numeric | NOT NULL
| id | serial | UNIQUE NOT NULL
| debits | numeric |
| credits | numeric |

Index - Schema public

## Table: account_heading

This table holds the account headings in the system. Each account must belong to a heading, and a heading can belong to another heading. In this way it is possible to nest accounts for reporting purposes.

account_heading Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| id | serial | UNIQUE NOT NULL
| accno | text | PRIMARY KEY
account_heading.id | parent_id | integer |
| description | text |

Tables referencing this one via Foreign Key Constraints:

- account

- account_heading

Index - Schema public

## Table: account_link

account_link Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
account.id | account_id | integer | PRIMARY KEY
account_link_description.description | description | text | PRIMARY KEY

Index - Schema public

## Table: account_link_description

This is a lookup table which provide basic information as to categories and dropdowns of accounts. In general summary accounts cannot belong to more than one category (an AR summary account cannot appear in other dropdowns for example). Custom fields are not overwritten when the account is edited from the front-end.

account_link_description Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| description | text | PRIMARY KEY
| summary | boolean | NOT NULL
| custom | boolean | NOT NULL

Tables referencing this one via Foreign Key Constraints:

- account_link

Index - Schema public

## Table: ap

Summary/header information for AP transactions and vendor invoices. Note that some constraints here are hard to enforce because we haven not gotten to rewriting the relevant code here.

ap Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
transactions.id | id | integer | PRIMARY KEY DEFAULT nextval('id'::regclass)
| invnumber | text | Text identifier for the invoice. Must be unique.
| transdate | date | DEFAULT ('now'::text)::date
entity.id | entity_id | integer |
| taxincluded | boolean | DEFAULT false
| amount | numeric | This stores the total amount (including taxes) for the transaction.
| netamount | numeric | Total amount excluding taxes for the transaction.
| paid | numeric |
| datepaid | date |
| duedate | date |
| invoice | boolean | DEFAULT false True if the transaction tracks goods/services purchase using the invoice table. False otherwise.
| ordnumber | text | Order Number
| curr | character(3) | 3 letters to identify the currency.
| notes | text | These notes are displayed on the invoice when printed or emailed
entity_employee.entity_id | person_id | integer | Person who created the transaction
| till | character varying(20) |
| quonumber | text | Quotation Number
| intnotes | text | These notes are not displayed when the invoice is printed or emailed and may be updated without reposting hte invocie.
| department_id | integer |
| shipvia | text |
| language_code | character varying(6) |
| ponumber | text | Purchase Order Number
| shippingpoint | text |
| on_hold | boolean | DEFAULT false
| approved | boolean | DEFAULT true Only show in financial reports if true.
| reverse | boolean | DEFAULT false If true numbers are displayed after multiplying by -1
| terms | smallint |
| description | text |
| force_closed | boolean | Not exposed to the UI, but can be set to prevent an invoice from showing up for payment or in outstanding reports.
entity_credit_account.id | entity_credit_account | integer | NOT NULL reference for the vendor account used.

ap Constraints
Name | Constraint
--- | ---
ap_check | CHECK ((((amount IS NULL) AND (curr IS NULL)) OR ((amount IS NOT NULL) AND (curr IS NOT NULL))))

Tables referencing this one via Foreign Key Constraints:

- asset_item

ap_approved_idx approved ap_curr_idz curr ap_id_key id ap_invnumber_key invnumber ap_ordnumber_key ordnumber ap_quonumber_key quonumber ap_transdate_key transdate

Index - Schema public

## Table: ar

Summary/header information for AR transactions and sales invoices. Note that some constraints here are hard to enforce because we haven not gotten to rewriting the relevant code here.

ar Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
transactions.id | id | integer | PRIMARY KEY DEFAULT nextval('id'::regclass)
| invnumber | text | UNIQUE Text identifier for the invoice. Must be unique.
| transdate | date | DEFAULT ('now'::text)::date
entity.id | entity_id | integer |
| taxincluded | boolean |
| amount | numeric | This stores the total amount (including taxes) for the transaction.
| netamount | numeric | Total amount excluding taxes for the transaction.
| paid | numeric |
| datepaid | date |
| duedate | date |
| invoice | boolean | DEFAULT false True if the transaction tracks goods/services purchase using the invoice table. False otherwise.
| shippingpoint | text |
| terms | smallint |
| notes | text | These notes are displayed on the invoice when printed or emailed
| curr | character(3) | 3 letters to identify the currency.
| ordnumber | text | Order Number
entity_employee.entity_id | person_id | integer | Person who created the transaction
| till | character varying(20) |
| quonumber | text | Quotation Number
| intnotes | text | These notes are not displayed when the invoice is printed or emailed and may be updated without reposting hte invocie.
| department_id | integer |
| shipvia | text |
| language_code | character varying(6) |
| ponumber | text | Purchase Order Number
| on_hold | boolean | DEFAULT false
| reverse | boolean | DEFAULT false If true numbers are displayed after multiplying by -1
| approved | boolean | DEFAULT true Only show in financial reports if true.
entity_credit_account.id | entity_credit_account | integer | NOT NULL reference for the customer account used.
| force_closed | boolean | Not exposed to the UI, but can be set to prevent an invoice from showing up for payment or in outstanding reports.
| description | text |

ar Constraints
Name | Constraint
--- | ---
ar_check | CHECK ((((amount IS NULL) AND (curr IS NULL)) OR ((amount IS NOT NULL) AND (curr IS NOT NULL))))

ar_approved_idx approved ar_curr_idz curr ar_id_key id ar_ordnumber_key ordnumber ar_quonumber_key quonumber ar_transdate_key transdate

Index - Schema public

## Table: assembly

Holds mapping for parts that are members of assemblies.

assembly Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
parts.id | id | integer | PRIMARY KEY This is the id of the assembly the part is being mapped to.
parts.id | parts_id | integer | PRIMARY KEY ID of part that is a member of the assembly.
| qty | numeric |
| bom | boolean |
| adj | boolean |

assembly_id_key id

Index - Schema public

## Table: asset_class

The account fields here set the defaults for the individual asset items. They are non-authoritative.

asset_class Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| id | serial | UNIQUE NOT NULL
| label | text | PRIMARY KEY
account.id | asset_account_id | integer |
account.id | dep_account_id | integer |
asset_dep_method.id | method | integer |

Tables referencing this one via Foreign Key Constraints:

- asset_item

- asset_report

Index - Schema public

## Table: asset_dep_method

Stores asset depreciation methods, and their relevant stored procedures. The fixed asset system is such depreciation methods can be plugged in via this table.

asset_dep_method Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| id | serial | UNIQUE NOT NULL
| method | text | PRIMARY KEY These are keyed to specific stored procedures. Currently only "straight_line" is supported
| sproc | text | UNIQUE NOT NULL The sproc mentioned here is a stored procedure which must have the following arguments: (in_asset_ids int[], in_report_date date, in_report_id int). Here in_asset_ids are the assets to be depreciated, in_report_date is the date of the report, and in_report_id is the id of the report. The sproc MUST insert the relevant lines into asset_report_line.
| unit_label | text | NOT NULL
| short_name | text | UNIQUE NOT NULL
asset_unit_class.id | unit_class | integer | NOT NULL

Tables referencing this one via Foreign Key Constraints:

- asset_class

Index - Schema public

## Table: asset_disposal_method

asset_disposal_method Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| label | text | PRIMARY KEY
| id | serial | UNIQUE NOT NULL
| multiple | integer |
| short_label | character(1) |

asset_disposal_method Constraints
Name | Constraint
--- | ---
asset_disposal_method_multiple_check | CHECK ((multiple = ANY (ARRAY[1, 0, (-1)])))

Tables referencing this one via Foreign Key Constraints:

- asset_rl_to_disposal_method

Index - Schema public

## Table: asset_item

Stores details of asset items. The account fields here are authoritative, while the ones in the asset_class table are defaults.

asset_item Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| id | serial | PRIMARY KEY
| description | text |
| tag | text | UNIQUE#1 NOT NULL This can be plugged into other routines to generate it automatically via ALTER TABLE .... SET DEFAULT.....
| purchase_value | numeric |
| salvage_value | numeric |
| usable_life | numeric |
| purchase_date | date | NOT NULL
| start_depreciation | date | NOT NULL
warehouse.id | location_id | integer |
department.id | department_id | integer |
ap.id | invoice_id | integer |
account.id | asset_account_id | integer |
account.id | dep_account_id | integer |
account.id | exp_account_id | integer |
asset_item.id | obsolete_by | integer | UNIQUE#1
asset_class.id | asset_class_id | integer |

Tables referencing this one via Foreign Key Constraints:

- asset_item

- asset_note

- asset_report_line

- asset_rl_to_disposal_method

Index - Schema public

## Table: asset_note

asset_note Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| id | integer | NOT NULL DEFAULT nextval('note_id_seq'::regclass)
| note_class | integer | NOT NULL DEFAULT 4
| note | text | NOT NULL
| vector | tsvector | NOT NULL DEFAULT ''::tsvector
| created | timestamp without time zone | NOT NULL DEFAULT now()
| created_by | text | DEFAULT "session_user"()
asset_item.id | ref_key | integer | NOT NULL
| subject | text |

Table asset_note Inherits note,

asset_note Constraints
Name | Constraint
--- | ---
asset_note_note_class_check | CHECK ((note_class = 4))

Index - Schema public

## Table: asset_report

Asset reports are discrete sets of depreciation or disposal transctions, and each one may be turned into no more than one GL transaction.

asset_report Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| id | serial | PRIMARY KEY
| report_date | date |
gl.id | gl_id | bigint | UNIQUE
asset_class.id | asset_class | bigint |
asset_report_class.id | report_class | integer |
entity.id | entered_by | bigint | NOT NULL DEFAULT person__get_my_entity_id()
entity.id | approved_by | bigint |
| entered_at | timestamp without time zone | DEFAULT now()
| approved_at | timestamp without time zone |
| depreciated_qty | numeric |
| dont_approve | boolean | DEFAULT false
| submitted | boolean | NOT NULL DEFAULT false

Tables referencing this one via Foreign Key Constraints:

- asset_report_line

- asset_rl_to_disposal_method

Index - Schema public

## Table: asset_report_class

By default only four types of asset reports are supported. In the future others may be added. Please correspond on the list before adding more types.

asset_report_class Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| id | integer | UNIQUE NOT NULL
| class | text | PRIMARY KEY

Tables referencing this one via Foreign Key Constraints:

- asset_report

Index - Schema public

## Table: asset_report_line

asset_report_line Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
asset_item.id | asset_id | bigint | PRIMARY KEY
asset_report.id | report_id | bigint | PRIMARY KEY
| amount | numeric |
department.id | department_id | integer | In case assets are moved between departments, we have to store this here.
warehouse.id | warehouse_id | integer |

Index - Schema public

## Table: asset_rl_to_disposal_method

Maps disposal method to line items in the asset disposal report.

asset_rl_to_disposal_method Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
asset_report.id | report_id | integer | PRIMARY KEY
asset_item.id | asset_id | integer | PRIMARY KEY
asset_disposal_method.id | disposal_method_id | integer | PRIMARY KEY
| percent_disposed | numeric |

Index - Schema public

## Table: asset_unit_class

asset_unit_class Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| id | integer | UNIQUE NOT NULL
| class | text | PRIMARY KEY

Tables referencing this one via Foreign Key Constraints:

- asset_dep_method

Index - Schema public

## Table: audittrail

This stores information on who entered or updated rows in the ar, ap, or gl tables.

audittrail Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| trans_id | integer |
| tablename | text |
| reference | text |
| formname | text |
| action | text |
| transdate | timestamp without time zone | DEFAULT now()
person.entity_id | person_id | integer | NOT NULL
| entry_id | bigserial | PRIMARY KEY

audittrail_trans_id_key trans_id

Index - Schema public

## Table: batch

Stores batch header info. Batches are groups of vouchers that are posted together.

batch Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| id | serial | PRIMARY KEY
batch_class.id | batch_class_id | integer | NOT NULL Note that this field is largely used for sorting the vouchers. A given batch is NOT restricted to this type.
| control_code | text | NOT NULL
| description | text |
| default_date | date | NOT NULL
| approved_on | date |
entity_employee.entity_id | approved_by | integer |
entity_employee.entity_id | created_by | integer |
session.session_id | locked_by | integer |
| created_on | date | DEFAULT now()

batch Constraints
Name | Constraint
--- | ---
batch_control_code_check | CHECK ((length(control_code) > 0))

Tables referencing this one via Foreign Key Constraints:

- pending_job

- voucher

Index - Schema public

## Table: batch_class

These values are hard-coded. Please coordinate before adding standard values.

batch_class Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| id | serial | UNIQUE NOT NULL
| class | character varying | PRIMARY KEY

Tables referencing this one via Foreign Key Constraints:

- batch

- pending_job

- voucher

Index - Schema public

## Table: business

Groups of Customers assigned joint discounts.

business Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| id | serial | PRIMARY KEY
| description | text |
| discount | numeric |

Index - Schema public

## View: chart

Compatibility chart for 1.2 and earlier.

chart Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| id | integer |
| accno | text |
| description | text |
| charttype | text |
| category | bpchar |
| link | text |
| account_heading | integer |
| gifi_accno | text |
| contra | boolean |
| tax | boolean |

    SELECT account_heading.id
    , account_heading.accno
    , account_heading.description
    ,'H' AS charttype
    , NULL::unknown AS category
    , NULL::unknown AS link
    , NULL::unknown AS account_heading
    , NULL::unknown AS gifi_accno
    , false AS contra
    , false AS tax
    FROM account_heading
    UNIONSELECT c.id
    , c.accno
    , c.description
    ,'A' AS charttype
    , c.category
    , concat_colon
    (l.description) AS link
    , c.heading AS account_heading
    , c.gifi_accno
    , c.contra
    , c.tax
    FROM (account c
    LEFT JOIN account_link l
        ON (
               (c.id = l.account_id)
         )
    )
    GROUP BY c.id
    , c.accno
    , c.description
    , c.category
    , c.heading
    , c.gifi_accno
    , c.contra
    , c.tax;

Index - Schema public

## Table: company

company Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| id | serial | UNIQUE NOT NULL
entity.id | entity_id | integer | PRIMARY KEY
| legal_name | text | PRIMARY KEY
| tax_id | text | In the US this would be a EIN.
| sic_code | character varying |
| created | date | NOT NULL DEFAULT ('now'::text)::date

company Constraints
Name | Constraint
--- | ---
company_legal_name_check | CHECK ((legal_name ~ '[[:alnum:]_]'::text))

Tables referencing this one via Foreign Key Constraints:

- company_to_contact

- company_to_entity

- company_to_location

- person_to_company

company_name_gist__idx legal_name gist_trgm_ops

Index - Schema public

## Table: company_to_contact

company_to_contact Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
company.id | company_id | integer | PRIMARY KEY
contact_class.id | contact_class_id | integer | PRIMARY KEY
| contact | text | PRIMARY KEY
| description | text |

company_to_contact Constraints
Name | Constraint
--- | ---
company_to_contact_contact_check | CHECK ((contact ~ '[[:alnum:]_]'::text))

Index - Schema public

## Table: company_to_entity

This provides a map so that entities can also be used like groups.

company_to_entity Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
company.id | company_id | integer | PRIMARY KEY
entity.id | entity_id | integer | PRIMARY KEY
| related_how | text |
| created | date | NOT NULL DEFAULT ('now'::text)::date

company_to_entity Constraints
Name | Constraint
--- | ---
company_to_entity_check | CHECK ((company_id <> entity_id))

Index - Schema public

## Table: company_to_location

This table is used for locations generic to companies. For contract-bound addresses, use eca_to_location instead

company_to_location Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
location.id | location_id | integer | PRIMARY KEY
location_class.id | location_class | integer | PRIMARY KEY
company.id | company_id | integer | PRIMARY KEY

Index - Schema public

## Table: contact_class

Stores type of contact information attached to companies and persons. Please coordinate with others before adding new types.

contact_class Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| id | serial | UNIQUE NOT NULL
| class | text | PRIMARY KEY

contact_class Constraints
Name | Constraint
--- | ---
contact_class_class_check | CHECK ((class ~ '[[:alnum:]_]'::text))

Tables referencing this one via Foreign Key Constraints:

- company_to_contact

- eca_to_contact

- person_to_contact

Index - Schema public

## Table: country

country Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| id | serial | PRIMARY KEY
| name | text | NOT NULL
| short_name | text | NOT NULL
| itu | text | The ITU Telecommunication Standardization Sector code for calling internationally. For example, the US is 1, Great Britain is 44

country Constraints
Name | Constraint
--- | ---
country_name_check | CHECK ((name ~ '[[:alnum:]_]'::text))
country_short_name_check | CHECK ((short_name ~ '[[:alnum:]_]'::text))

Tables referencing this one via Foreign Key Constraints:

- country_tax_form

- entity

- entity_class

- location

Index - Schema public

## Table: country_tax_form

This table was designed for holding information relating to reportable sales or purchases, such as IRS 1099 forms and international equivalents.

country_tax_form Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
country.id | country_id | integer | PRIMARY KEY
| form_name | text | PRIMARY KEY
| id | serial | UNIQUE NOT NULL
| default_reportable | boolean | NOT NULL DEFAULT false

Tables referencing this one via Foreign Key Constraints:

- entity_credit_account

Index - Schema public

## Table: cr_coa_to_account

Provides name mapping for the cash reconciliation screen.

cr_coa_to_account Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
account.id | chart_id | integer | NOT NULL
| account | text | NOT NULL

Index - Schema public

## Table: cr_report

This table holds header data for cash reports.

cr_report Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| id | bigserial | PRIMARY KEY
account.id | chart_id | integer | NOT NULL
| their_total | numeric | NOT NULL
| approved | boolean | NOT NULL DEFAULT false
| submitted | boolean | NOT NULL DEFAULT false
| end_date | date | NOT NULL DEFAULT now()
| updated | timestamp without time zone | NOT NULL DEFAULT now()
entity.id | entered_by | integer | NOT NULL DEFAULT person__get_my_entity_id()
| entered_username | text | NOT NULL DEFAULT "session_user"()
| deleted | boolean | NOT NULL DEFAULT false
entity.id | deleted_by | integer |
entity.id | approved_by | integer |
| approved_username | text |

cr_report Constraints
Name | Constraint
--- | ---
cr_report_check | CHECK (((deleted IS NOT TRUE) OR (approved IS NOT TRUE)))

Tables referencing this one via Foreign Key Constraints:

- cr_report_line

Index - Schema public

## Table: cr_report_line

This stores line item data on transaction lines and whether they are cleared.

cr_report_line Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| id | bigserial | PRIMARY KEY
cr_report.id | report_id | integer | NOT NULL
| scn | text | This is the check number. Maps to acc_trans.source
| their_balance | numeric |
| our_balance | numeric |
| errorcode | integer |
entity.id | user | integer | NOT NULL
| clear_time | date |
| insert_time | timestamp with time zone | NOT NULL DEFAULT now()
| trans_type | text |
| post_date | date |
acc_trans.entry_id | ledger_id | integer |
voucher.id | voucher_id | integer |
| overlook | boolean | NOT NULL DEFAULT false
| cleared | boolean | NOT NULL DEFAULT false

Index - Schema public

## Table: custom_field_catalog

Deprecated, use only with old code.

custom_field_catalog Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| field_id | serial | PRIMARY KEY
custom_table_catalog.table_id | table_id | integer |
| field_name | text |

Index - Schema public

## Table: custom_table_catalog

Deprecated, use only with old code.

custom_table_catalog Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| table_id | serial | PRIMARY KEY
| extends | text |
| table_name | text |

Tables referencing this one via Foreign Key Constraints:

- custom_field_catalog

Index - Schema public

## Table: customertax

Mapping customer to taxes.

customertax Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
entity_credit_account.id | customer_id | integer | PRIMARY KEY
account.id | chart_id | integer | PRIMARY KEY

customer_customer_id_key customer_id

Index - Schema public

## Table: defaults

This is a free-form table for managing application settings per company database. We use key-value modelling here because this most accurately maps the actual semantics of the data.

defaults Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| setting_key | text | PRIMARY KEY
| value | text |

Index - Schema public

## Table: department

department Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| id | serial | PRIMARY KEY
| description | text |
| role | character(1) | DEFAULT 'P'::bpchar P for Profit Center, C for Cost Center

Tables referencing this one via Foreign Key Constraints:

- asset_item

- asset_report_line

department_id_key id

Index - Schema public

## Table: dpt_trans

Department to Transaction Map

dpt_trans Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| trans_id | integer | PRIMARY KEY
| department_id | integer |

Index - Schema public

## Table: eca_note

Notes for entity_credit_account entries.

eca_note Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| id | integer | PRIMARY KEY DEFAULT nextval('note_id_seq'::regclass)
| note_class | integer | NOT NULL
| note | text | NOT NULL
| vector | tsvector | NOT NULL DEFAULT ''::tsvector
| created | timestamp without time zone | NOT NULL DEFAULT now()
| created_by | text | DEFAULT "session_user"()
entity_credit_account.id | ref_key | integer | NOT NULL references entity_credit_account.id
| subject | text |

Table eca_note Inherits note,

eca_note Constraints
Name | Constraint
--- | ---
eca_note_note_class_check | CHECK ((note_class = 3))

Index - Schema public

## Table: eca_to_contact

To keep track of the relationship between multiple contact methods and a single vendor or customer account. For generic contacts, use company_to_contact or person_to_contact instead.

eca_to_contact Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
entity_credit_account.id | credit_id | integer | PRIMARY KEY
contact_class.id | contact_class_id | integer | PRIMARY KEY
| contact | text | PRIMARY KEY
| description | text |

eca_to_contact Constraints
Name | Constraint
--- | ---
eca_to_contact_contact_check | CHECK ((contact ~ '[[:alnum:]_]'::text))

Index - Schema public

## Table: eca_to_location

This table is used for locations bound to contracts. For generic contact addresses, use company_to_location instead

eca_to_location Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
location.id | location_id | integer | PRIMARY KEY
location_class.id | location_class | integer | PRIMARY KEY
entity_credit_account.id | credit_id | integer | PRIMARY KEY

Index - Schema public

## View: employee_search

employee_search Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| entity_id | integer |
| startdate | date |
| enddate | date |
| role | character varying(20) |
| ssn | text |
| sales | boolean |
| manager_id | integer |
| employeenumber | character varying(32) |
| dob | date |
| manager | text |
| note | text |
| name | text |

    SELECT e.entity_id
    , e.startdate
    , e.enddate
    , e.role
    , e.ssn
    , e.sales
    , e.manager_id
    , e.employeenumber
    , e.dob
    , em.name AS manager
    , emn.note
    , en.name
    FROM (
         (
               (
                     (entity_employee e
                   LEFT JOIN entity en
                          ON (
                                 (e.entity_id = en.id)
                           )
                     )
             LEFT JOIN entity_employee m
                    ON (
                           (e.manager_id = m.entity_id)
                     )
               )
       LEFT JOIN entity em
              ON (
                     (em.id = m.entity_id)
               )
         )
    LEFT JOIN entity_note emn
        ON (
               (emn.ref_key = em.id)
         )
    );

Index - Schema public

## View: employees

employees Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| salutation | text |
| first_name | text |
| last_name | text |
| entity_id | integer |
| startdate | date |
| enddate | date |
| role | character varying(20) |
| ssn | text |
| sales | boolean |
| manager_id | integer |
| employeenumber | character varying(32) |
| dob | date |

    SELECT s.salutation
    , p.first_name
    , p.last_name
    , ee.entity_id
    , ee.startdate
    , ee.enddate
    , ee.role
    , ee.ssn
    , ee.sales
    , ee.manager_id
    , ee.employeenumber
    , ee.dob
    FROM (
         (person p
            JOIN entity_employee ee
           USING (entity_id)
         )
    LEFT JOIN salutation s
        ON (
               (p.salutation_id = s.id)
         )
    );

Index - Schema public

## Table: entity

The primary entity table to map to all contacts

entity Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| id | serial | UNIQUE NOT NULL
| name | text | This is the common name of an entity. If it was a person it may be Joshua Drake, a company Acme Corp. You may also choose to use a domain such as commandprompt.com
entity_class.id entity_class.id | entity_class | integer | PRIMARY KEY
| created | date | NOT NULL DEFAULT ('now'::text)::date
| control_code | text | UNIQUE PRIMARY KEY DEFAULT setting_increment('entity_control'::character varying)
country.id | country_id | integer | NOT NULL

entity Constraints
Name | Constraint
--- | ---
entity_name_check | CHECK ((name ~ '[[:alnum:]_]'::text))

Tables referencing this one via Foreign Key Constraints:

- ap

- ar

- asset_report

- company

- company_to_entity

- cr_report

- cr_report_line

- entity_bank_account

- entity_class_to_entity

- entity_credit_account

- entity_employee

- entity_note

- entity_other_name

- file_base

- file_secondary_attachment

- oe

- person

- person_to_entity

- transactions

- users

entity_name_gist_idx name gist_trgm_ops

Index - Schema public

## Table: entity_bank_account

This stores bank account information for both companies and persons.

entity_bank_account Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| id | serial | UNIQUE NOT NULL
entity.id | entity_id | integer | PRIMARY KEY
| bic | character varying | PRIMARY KEY Banking Institution Code, such as routing number of SWIFT code.
| iban | character varying | PRIMARY KEY International Bank Account Number. used to store the actual account number for the banking institution.

Tables referencing this one via Foreign Key Constraints:

- entity_credit_account

Index - Schema public

## Table: entity_class

Defines the class type such as vendor, customer, contact, employee

entity_class Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| id | serial | PRIMARY KEY The first 7 values are reserved and permanent. Individuals who create new classes, however, should coordinate with others for ranges to use.
| class | text | NOT NULL
country.id | country_id | integer |
| active | boolean | NOT NULL DEFAULT true

entity_class Constraints
Name | Constraint
--- | ---
entity_class_class_check | CHECK ((class ~ '[[:alnum:]_]'::text))

Tables referencing this one via Foreign Key Constraints:

- entity

- entity_class_to_entity

- entity_credit_account

entity_class_idx lower(class)

Index - Schema public

## Table: entity_class_to_entity

Relation builder for classes to entity

entity_class_to_entity Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
entity_class.id | entity_class_id | integer | PRIMARY KEY
entity.id | entity_id | integer | PRIMARY KEY

Index - Schema public

## Table: entity_credit_account

This table stores informmation relating to general relationships regarding moneys owed on invoice. Invoices, whether AR or AP, must be attached to a record in this table.

entity_credit_account Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| id | serial | UNIQUE NOT NULL
entity.id | entity_id | integer | PRIMARY KEY
entity_class.id | entity_class | integer | PRIMARY KEY
| pay_to_name | text |
| discount | numeric |
| description | text |
| discount_terms | integer |
account.id | discount_account_id | integer |
| taxincluded | boolean | DEFAULT false
| creditlimit | numeric |
| terms | smallint |
| meta_number | character varying(32) | PRIMARY KEY This stores the human readable control code for the customer/vendor record. This is typically called the customer/vendor "account" in the application.
| business_id | integer |
| language_code | character varying(6) |
pricegroup.id | pricegroup_id | integer |
| curr | character(3) |
| startdate | date | DEFAULT ('now'::text)::date
| enddate | date |
| threshold | numeric |
entity_employee.entity_id | employee_id | integer |
person.id | primary_contact | integer |
account.id | ar_ap_account_id | integer |
account.id | cash_account_id | integer |
entity_bank_account.id | bank_account | integer |
country_tax_form.id | taxform_id | integer |

entity_credit_account Constraints
Name | Constraint
--- | ---
entity_credit_account_check | CHECK (((ar_ap_account_id IS NOT NULL) OR (entity_id = 0)))
entity_credit_account_entity_class_check | CHECK ((entity_class = ANY (ARRAY[1, 2])))

Tables referencing this one via Foreign Key Constraints:

- ap

- ar

- customertax

- eca_note

- eca_to_contact

- eca_to_location

- oe

- partscustomer

- partsvendor

- payment

- project

- vendortax

Index - Schema public

## Table: entity_employee

This contains employee-specific extensions to person/entity.

entity_employee Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
entity.id | entity_id | integer | PRIMARY KEY
| startdate | date | NOT NULL DEFAULT ('now'::text)::date
| enddate | date |
| role | character varying(20) |
| ssn | text |
| sales | boolean | DEFAULT false
entity.id | manager_id | integer |
| employeenumber | character varying(32) |
| dob | date |

Tables referencing this one via Foreign Key Constraints:

- ap

- ar

- batch

- entity_credit_account

- inventory

Index - Schema public

## Table: entity_note

entity_note Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| id | integer | PRIMARY KEY DEFAULT nextval('note_id_seq'::regclass)
| note_class | integer | NOT NULL
| note | text | NOT NULL
| vector | tsvector | NOT NULL DEFAULT ''::tsvector
| created | timestamp without time zone | NOT NULL DEFAULT now()
| created_by | text | DEFAULT "session_user"()
entity.id | ref_key | integer | NOT NULL
| subject | text |
entity.id | entity_id | integer |

Table entity_note Inherits note,

entity_note Constraints
Name | Constraint
--- | ---
entity_note_note_class_check | CHECK ((note_class = 1))

entity_note_id_idx id entity_note_vectors_idx vector notes_idx note gist_trgm_ops

Index - Schema public

## Table: entity_other_name

Similar to company_other_name, a person may be jd, Joshua Drake, linuxpoet... all are the same person. Currently unused in the front-end but will likely be added in future versions.

entity_other_name Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
entity.id | entity_id | integer | PRIMARY KEY
| other_name | text | PRIMARY KEY

entity_other_name Constraints
Name | Constraint
--- | ---
entity_other_name_other_name_check | CHECK ((other_name ~ '[[:alnum:]_]'::text))

Index - Schema public

## Table: exchangerate

exchangerate Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| curr | character(3) | PRIMARY KEY
| transdate | date | PRIMARY KEY
| buy | numeric |
| sell | numeric |

exchangerate_ct_key curr, transdate

Index - Schema public

## Table: file_base

Abstract table, holds no records. Inheriting table store actual file attachment data. Can be queried however to retrieve lists of all files.

file_base Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| content | bytea | NOT NULL
mime_type.id | mime_type_id | integer | NOT NULL
| file_name | text | PRIMARY KEY
| description | text |
entity.id | uploaded_by | integer | NOT NULL
| uploaded_at | timestamp without time zone | NOT NULL DEFAULT now()
| id | serial | UNIQUE NOT NULL
| ref_key | integer | PRIMARY KEY This column inheriting tables is used to reference the database row for the attachment. Inheriting tables MUST set the foreign key here appropriately. This can also be used to create classifications of other documents, such as by source of automatic import (where the file is not yet attached) or even standard, long-lived documents.
file_class.id | file_class | integer | PRIMARY KEY

Index - Schema public

## Table: file_class

File classes are collections of files attached against rows in specific tables in the database. They can be used in the future to implement other form of file attachment.

file_class Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| id | serial | UNIQUE NOT NULL
| class | text | PRIMARY KEY

Tables referencing this one via Foreign Key Constraints:

- file_base

- file_secondary_attachment

- file_view_catalog

Index - Schema public

## View: file_links

file_links Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| file_id | integer |
| ref_key | integer |
| reference | text |
| type | text |
| dest_class | integer |
| source_class | integer |
| dest_ref | integer |

    SELECT file_tx_links.file_id
    , file_tx_links.ref_key
    , file_tx_links.reference
    , file_tx_links.type
    , file_tx_links.dest_class
    , file_tx_links.source_class
    , file_tx_links.dest_ref
    FROM file_tx_links
    UNIONSELECT file_order_links.file_id
    , file_order_links.ref_key
    , file_order_links.reference
    , file_order_links.oe_class AS type
    , file_order_links.dest_class
    , file_order_links.source_class
    , file_order_links.dest_ref
    FROM file_order_links;

Index - Schema public

## Table: file_order

File attachments primarily attached to orders and quotations.

file_order Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| content | bytea | NOT NULL
| mime_type_id | integer | NOT NULL
| file_name | text | PRIMARY KEY
| description | text |
| uploaded_by | integer | NOT NULL
| uploaded_at | timestamp without time zone | NOT NULL DEFAULT now()
| id | integer | UNIQUE NOT NULL DEFAULT nextval('file_base_id_seq'::regclass)
oe.id | ref_key | integer | PRIMARY KEY
| file_class | integer | PRIMARY KEY

Table file_order Inherits file_base,

file_order Constraints
Name | Constraint
--- | ---
file_order_file_class_check | CHECK ((file_class = 2))

Tables referencing this one via Foreign Key Constraints:

- file_order_to_order

- file_order_to_tx

Index - Schema public

## View: file_order_links

file_order_links Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| file_id | integer |
| ref_key | integer |
| reference | text |
| oe_class | text |
| dest_class | integer |
| source_class | integer |
| dest_ref | integer |

    SELECT sl.file_id
    , sl.ref_key
    , oe.ordnumber AS reference
    , oc.oe_class
    , sl.dest_class
    , sl.source_class
    , sl.ref_key AS dest_ref
    FROM (
         (file_secondary_attachment sl
            JOIN oe
              ON (
                     (sl.ref_key = oe.id)
               )
         )
      JOIN oe_class oc
        ON (
               (oe.oe_class_id = oc.id)
         )
    )
    WHERE (sl.source_class = 2);

Index - Schema public

## Table: file_order_to_order

Secondary links from one order to another, for example to support order consolidation.

file_order_to_order Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
file_order.id | file_id | integer | PRIMARY KEY
| source_class | integer | PRIMARY KEY
oe.id | ref_key | integer | PRIMARY KEY
| dest_class | integer | PRIMARY KEY
| attached_by | integer | NOT NULL
| attached_at | timestamp without time zone | NOT NULL DEFAULT now()

Table file_order_to_order Inherits file_secondary_attachment,

file_order_to_order Constraints
Name | Constraint
--- | ---
file_order_to_order_dest_class_check | CHECK ((dest_class = 2))
file_order_to_order_source_class_check | CHECK ((source_class = 2))

Index - Schema public

## Table: file_order_to_tx

Secondary links from orders to transactions, for example to track files when invoices are generated from orders.

file_order_to_tx Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
file_order.id | file_id | integer | PRIMARY KEY
| source_class | integer | PRIMARY KEY
transactions.id | ref_key | integer | PRIMARY KEY
| dest_class | integer | PRIMARY KEY
| attached_by | integer | NOT NULL
| attached_at | timestamp without time zone | NOT NULL DEFAULT now()

Table file_order_to_tx Inherits file_secondary_attachment,

file_order_to_tx Constraints
Name | Constraint
--- | ---
file_order_to_tx_dest_class_check | CHECK ((dest_class = 1))
file_order_to_tx_source_class_check | CHECK ((source_class = 2))

Index - Schema public

## Table: file_part

File attachments primarily attached to orders and quotations.

file_part Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| content | bytea | NOT NULL
| mime_type_id | integer | NOT NULL
| file_name | text | PRIMARY KEY
| description | text |
| uploaded_by | integer | NOT NULL
| uploaded_at | timestamp without time zone | NOT NULL DEFAULT now()
| id | integer | UNIQUE NOT NULL DEFAULT nextval('file_base_id_seq'::regclass)
parts.id | ref_key | integer | PRIMARY KEY
| file_class | integer | PRIMARY KEY

Table file_part Inherits file_base,

file_part Constraints
Name | Constraint
--- | ---
file_part_file_class_check | CHECK ((file_class = 3))

Index - Schema public

## Table: file_secondary_attachment

Another abstract table. This one will use rewrite rules to make inserts safe because of the difficulty in managing inserts otherwise. Inheriting tables provide secondary links between the file and other database objects. Due to the nature of database inheritance and unique constraints in PostgreSQL, this must be partitioned in a star format.

file_secondary_attachment Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| file_id | integer | PRIMARY KEY
file_class.id | source_class | integer | PRIMARY KEY
| ref_key | integer | PRIMARY KEY
file_class.id | dest_class | integer | PRIMARY KEY
entity.id | attached_by | integer | NOT NULL
| attached_at | timestamp without time zone | NOT NULL DEFAULT now()

Index - Schema public

## Table: file_transaction

File attachments primarily attached to AR/AP/GL.

file_transaction Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| content | bytea | NOT NULL
| mime_type_id | integer | NOT NULL
| file_name | text | PRIMARY KEY
| description | text |
| uploaded_by | integer | NOT NULL
| uploaded_at | timestamp without time zone | NOT NULL DEFAULT now()
| id | integer | UNIQUE NOT NULL DEFAULT nextval('file_base_id_seq'::regclass)
transactions.id | ref_key | integer | PRIMARY KEY
| file_class | integer | PRIMARY KEY

Table file_transaction Inherits file_base,

file_transaction Constraints
Name | Constraint
--- | ---
file_transaction_file_class_check | CHECK ((file_class = 1))

Tables referencing this one via Foreign Key Constraints:

- file_tx_to_order

Index - Schema public

## View: file_tx_links

file_tx_links Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| file_id | integer |
| ref_key | integer |
| reference | text |
| type | text |
| dest_class | integer |
| source_class | integer |
| dest_ref | integer |

    SELECT sl.file_id
    , sl.ref_key
    , gl.reference
    , gl.type
    , sl.dest_class
    , sl.source_class
    , sl.ref_key AS dest_ref
    FROM (file_secondary_attachment sl
      JOIN (
               (
                SELECT gl.id
                     , gl.reference
                     ,'gl' AS type
                  FROM gl
                 UNIONSELECT ar.id
                     , ar.invnumber
                     , CASE WHEN ar.invoice THEN 'is'::text ELSE 'ar'::text END AS type
                  FROM ar
               )
           UNIONSELECT ap.id
               , ap.invnumber
               , CASE WHEN ap.invoice THEN 'ir'::text ELSE 'ap'::text END AS type
            FROM ap
         ) gl
        ON (
               (
                     (sl.ref_key = gl.id)
                   AND (sl.source_class = 1)
               )
         )
    );

Index - Schema public

## Table: file_tx_to_order

Secondary links from transactions to orders.

file_tx_to_order Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
file_transaction.id | file_id | integer | PRIMARY KEY
| source_class | integer | PRIMARY KEY
oe.id | ref_key | integer | PRIMARY KEY
| dest_class | integer | PRIMARY KEY
| attached_by | integer | NOT NULL
| attached_at | timestamp without time zone | NOT NULL DEFAULT now()

Table file_tx_to_order Inherits file_secondary_attachment,

file_tx_to_order Constraints
Name | Constraint
--- | ---
file_tx_to_order_dest_class_check | CHECK ((dest_class = 2))
file_tx_to_order_source_class_check | CHECK ((source_class = 1))

Index - Schema public

## Table: file_view_catalog

file_view_catalog Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
file_class.id | file_class | integer | PRIMARY KEY
| view_name | text | UNIQUE NOT NULL

Index - Schema public

## Table: gifi

GIFI labels for accounts, used in Canada and some EU countries for tax reporting

gifi Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| accno | text | PRIMARY KEY
| description | text |

Index - Schema public

## Table: gl

This table holds summary information for entries in the general journal. Does not hold summary information in 1.3 for AR or AP entries.

gl Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
transactions.id | id | integer | PRIMARY KEY DEFAULT nextval('id'::regclass)
| reference | text |
| description | text |
| transdate | date | DEFAULT ('now'::text)::date
person.id | person_id | integer | the person_id of the employee who created the entry.
| notes | text |
| approved | boolean | DEFAULT true
| department_id | integer |

Tables referencing this one via Foreign Key Constraints:

- asset_report

- payment

- yearend

gl_approved_idx approved gl_description_key lower(description) gl_id_key id gl_reference_key reference gl_transdate_key transdate

Index - Schema public

## Table: inventory

This table contains inventory mappings to warehouses, not general inventory management data.

inventory Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
entity_employee.entity_id | entity_id | integer |
| warehouse_id | integer |
| parts_id | integer |
| trans_id | integer |
| orderitems_id | integer |
| qty | numeric |
| shippingdate | date |
| entry_id | serial | PRIMARY KEY

Index - Schema public

## Table: invoice

Line items of invoices with goods/services attached.

invoice Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| id | serial | PRIMARY KEY
transactions.id | trans_id | integer |
parts.id | parts_id | integer |
| description | text |
| qty | numeric | Positive is normal for sales invoices, negative for vendor invoices.
| allocated | integer | Number of allocated items, negative relative to qty. When qty + allocated = 0, then the item is fully used for purposes of COGS calculations.
| sellprice | numeric |
| precision | integer |
| fxsellprice | numeric |
| discount | numeric |
| assemblyitem | boolean | DEFAULT false
| unit | character varying(5) |
| project_id | integer |
| deliverydate | date |
| serialnumber | text |
| notes | text |

Tables referencing this one via Foreign Key Constraints:

- invoice_note

- invoice_tax_form

invoice_id_key id invoice_trans_id_key trans_id

Index - Schema public

## Table: invoice_note

invoice_note Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| id | integer | PRIMARY KEY DEFAULT nextval('note_id_seq'::regclass)
| note_class | integer | NOT NULL
| note | text | NOT NULL
| vector | tsvector | NOT NULL DEFAULT ''::tsvector
| created | timestamp without time zone | NOT NULL DEFAULT now()
| created_by | text | DEFAULT "session_user"()
invoice.id | ref_key | integer | NOT NULL
| subject | text |

Table invoice_note Inherits note,

invoice_note_id_idx id invoice_note_vectors_idx vector

Index - Schema public

## Table: invoice_tax_form

Maping invoice to country_tax_form.

invoice_tax_form Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
invoice.id | invoice_id | integer | PRIMARY KEY
| reportable | boolean |

Index - Schema public

## Table: jcitems

Time and materials cards. Materials cards not implemented.

jcitems Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| id | serial | PRIMARY KEY
| project_id | integer |
| parts_id | integer |
| description | text |
| qty | numeric |
| allocated | numeric |
| sellprice | numeric |
| fxsellprice | numeric |
| serialnumber | text |
| checkedin | timestamp with time zone |
| checkedout | timestamp with time zone |
person.id | person_id | integer | NOT NULL
| notes | text |

jcitems_id_key id

Index - Schema public

## Table: language

Languages for manual translations and so forth.

language Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| code | character varying(6) | PRIMARY KEY
| description | text |

Tables referencing this one via Foreign Key Constraints:

- user_preference

Index - Schema public

## Table: location

This table stores addresses, such as shipto and bill to addresses.

location Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| id | serial | PRIMARY KEY
| line_one | text | NOT NULL
| line_two | text |
| line_three | text |
| city | text | NOT NULL
| state | text |
country.id | country_id | integer | NOT NULL
| mail_code | text | NOT NULL
| created | date | NOT NULL DEFAULT now()
| inactive_date | timestamp without time zone |
| active | boolean | NOT NULL DEFAULT true

location Constraints
Name | Constraint
--- | ---
location_city_check | CHECK ((city ~ '[[:alnum:]_]'::text))
location_line_one_check | CHECK ((line_one ~ '[[:alnum:]_]'::text))
location_mail_code_check | CHECK ((mail_code ~ '[[:alnum:]_]'::text))
location_state_check | CHECK ((state ~ '[[:alnum:]_]'::text))

Tables referencing this one via Foreign Key Constraints:

- company_to_location

- eca_to_location

- new_shipto

- person_to_company

- person_to_location

location_address_one_gist__idx line_one gist_trgm_ops location_address_three_gist__idx line_three gist_trgm_ops location_address_two_gist__idx line_two gist_trgm_ops location_city_prov_gist_idx city gist_trgm_ops

Index - Schema public

## Table: location_class

Individuals seeking to add new location classes should coordinate with others.

location_class Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| id | serial | UNIQUE NOT NULL
| class | text | PRIMARY KEY
| authoritative | boolean | PRIMARY KEY

location_class Constraints
Name | Constraint
--- | ---
location_class_class_check | CHECK ((class ~ '[[:alnum:]_]'::text))

Tables referencing this one via Foreign Key Constraints:

- company_to_location

- eca_to_location

- person_to_location

Index - Schema public

## Table: lsmb_roles

Tracks role assignments in the front end. Not sure why we need this. Will rethink for 1.4.

lsmb_roles Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
users.id | user_id | integer | NOT NULL
| role | text | NOT NULL

Index - Schema public

## Table: makemodel

A single parts entry can have multiple make/model entries. These store manufacturer/model number info.

makemodel Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| parts_id | integer | PRIMARY KEY
| make | text |
| model | text |

makemodel_make_key lower(make) makemodel_model_key lower(model) makemodel_parts_id_key parts_id

Index - Schema public

## Table: menu_acl

Provides access control list entries for menu nodes.

menu_acl Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| id | serial | NOT NULL
| role_name | character varying | PRIMARY KEY
| acl_type | character varying | Nodes are hidden unless a role is found of which the user is a member, and where the acl_type for that role type and node is set to 'allow' and no acl is found for any role of which the user is a member, where the acl_type is set to 'deny'.
menu_node.id | node_id | integer | PRIMARY KEY

menu_acl Constraints
Name | Constraint
--- | ---
menu_acl_acl_type_check | CHECK ((((acl_type)::text = 'allow'::text) OR ((acl_type)::text = 'deny'::text)))

Index - Schema public

## Table: menu_attribute

This table stores the callback information for each menu item. The attributes are stored in key/value modelling because of the fact that this best matches the semantic structure of the information. Each node should have EITHER a menu or a module attribute, menu for a menu with sub-items, module for an executiable script. The module attribute identifies the perl script to be run. The action attribute identifies the entry point. Beyond this, any other attributes that should be passed in can be done as other attributes.

menu_attribute Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
menu_node.id | node_id | integer | PRIMARY KEY
| attribute | character varying | PRIMARY KEY
| value | character varying | NOT NULL
| id | serial | NOT NULL

Index - Schema public

## View: menu_friendly

A nice human-readable view for investigating the menu tree. Does not show menu attributes or acls.

menu_friendly Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| level | integer |
| path | text |
| list_order | integer |
| label | text |
| id | integer |
| position | integer |

    SELECT t.level
    , t.path
    , t.list_order
    , (repeat
         (' '::text
               , (2 * t.level)
         ) ||
         (n.label)::text
    ) AS label
    , n.id
    , n."position"
    FROM (connectby
         ('menu_node'::text
               ,'id'::text
               ,'parent'::text
               ,'position'::text
               ,'0'::text
               , 0
               ,','::text
         ) t
         (id integer
               , parent integer
               , level integer
               , path text
               , list_order integer
         )
      JOIN menu_node n
     USING (id)
    );

Index - Schema public

## Table: menu_node

This table stores the tree structure of the menu.

menu_node Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| id | serial | PRIMARY KEY
| label | character varying | NOT NULL
menu_node.id | parent | integer | UNIQUE#1
| position | integer | UNIQUE#1 NOT NULL

Tables referencing this one via Foreign Key Constraints:

- menu_acl

- menu_attribute

- menu_node

Index - Schema public

## Table: mime_type

This is a lookup table for storing MIME types.

mime_type Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| id | serial | UNIQUE NOT NULL
| mime_type | text | PRIMARY KEY

Tables referencing this one via Foreign Key Constraints:

- file_base

Index - Schema public

## Table: new_shipto

Tracks ship_to information for orders and invoices.

new_shipto Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| id | serial | PRIMARY KEY
transactions.id | trans_id | integer |
oe.id | oe_id | integer |
location.id | location_id | integer |

Index - Schema public

## Table: note

This is an abstract table which should have zero rows. It is inherited by other tables for specific notes.

note Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| id | serial | PRIMARY KEY
note_class.id | note_class | integer | NOT NULL
| note | text | NOT NULL Body of note.
| vector | tsvector | NOT NULL DEFAULT ''::tsvector tsvector for full text indexing, requires both setting up tsearch dictionaries and adding triggers to use at present.
| created | timestamp without time zone | NOT NULL DEFAULT now()
| created_by | text | DEFAULT "session_user"()
| ref_key | integer | NOT NULL Subclassed tables use this column as a foreign key against the table storing the record a note is attached to.
| subject | text |

Index - Schema public

## Table: note_class

Coordinate with others before adding entries.

note_class Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| id | serial | PRIMARY KEY
| class | text | NOT NULL

note_class Constraints
Name | Constraint
--- | ---
note_class_class_check | CHECK ((class ~ '[[:alnum:]_]'::text))

Tables referencing this one via Foreign Key Constraints:

- note

Index - Schema public

## Table: oe

Header information for: * Sales orders * Purchase Orders * Quotations * Requests for Quotation

oe Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| id | serial | PRIMARY KEY
| ordnumber | text |
| transdate | date | DEFAULT ('now'::text)::date
entity.id | entity_id | integer |
| amount | numeric |
| netamount | numeric |
| reqdate | date |
| taxincluded | boolean |
| shippingpoint | text |
| notes | text |
| curr | character(3) |
person.id | person_id | integer |
| closed | boolean | DEFAULT false
| quotation | boolean | DEFAULT false
| quonumber | text |
| intnotes | text |
| department_id | integer |
| shipvia | text |
| language_code | character varying(6) |
| ponumber | text |
| terms | smallint |
entity_credit_account.id | entity_credit_account | integer | NOT NULL
oe_class.id | oe_class_id | integer | NOT NULL

Tables referencing this one via Foreign Key Constraints:

- file_order

- file_order_to_order

- file_tx_to_order

- new_shipto

oe_id_key id oe_ordnumber_key ordnumber oe_transdate_key transdate

Index - Schema public

## Table: oe_class

Hardwired classifications for orders and quotations. Coordinate before adding.

oe_class Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| id | smallint | UNIQUE
| oe_class | text | PRIMARY KEY

oe_class Constraints
Name | Constraint
--- | ---
oe_class_id_check | CHECK ((id = ANY (ARRAY[1, 2, 3, 4])))

Tables referencing this one via Foreign Key Constraints:

- oe

Index - Schema public

## Table: open_forms

This is our primary anti-xsrf measure, as this allows us to require a full round trip to the web server in order to save data.

open_forms Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| id | serial | PRIMARY KEY
session.session_id | session_id | integer |

Index - Schema public

## Table: orderitems

Line items for sales/purchase orders and quotations.

orderitems Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| id | serial | PRIMARY KEY
| trans_id | integer |
| parts_id | integer |
| description | text |
| qty | numeric |
| sellprice | numeric |
| precision | integer |
| discount | numeric |
| unit | character varying(5) |
| project_id | integer |
| reqdate | date |
| ship | numeric |
| serialnumber | text |
| notes | text |

orderitems_id_key id orderitems_trans_id_key trans_id

Index - Schema public

## View: overpayments

overpayments Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| payment_id | integer |
| payment_reference | text |
| payment_class | integer |
| payment_closed | boolean |
| payment_date | date |
| chart_id | integer |
| accno | text |
| chart_description | text |
| department_id | integer |
| available | numeric |
| legal_name | text |
| entity_credit_id | integer |
| entity_id | integer |
| discount | numeric |
| meta_number | character varying(32) |

    SELECT p.id AS payment_id
    , p.reference AS payment_reference
    , p.payment_class
    , p.closed AS payment_closed
    , p.payment_date
    , ac.chart_id
    , c.accno
    , c.description AS chart_description
    , p.department_id
    , abs
    (sum
         (ac.amount)
    ) AS available
    , cmp.legal_name
    , eca.id AS entity_credit_id
    , eca.entity_id
    , eca.discount
    , eca.meta_number
    FROM (
         (
               (
                     (
                           (payment p
                              JOIN payment_links pl
                                ON (
                                       (pl.payment_id = p.id)
                                 )
                           )
                        JOIN acc_trans ac
                          ON (
                                 (ac.entry_id = pl.entry_id)
                           )
                     )
                  JOIN chart c
                    ON (
                           (c.id = ac.chart_id)
                     )
               )
            JOIN entity_credit_account eca
              ON (
                     (eca.id = p.entity_credit_id)
               )
         )
      JOIN company cmp
        ON (
               (cmp.entity_id = eca.entity_id)
         )
    )
    WHERE (
         (
               (p.gl_id IS NOT NULL)
             AND (
                     (pl.type = 2)
                    OR (pl.type = 0)
               )
         )
       AND (c.link ~~ '%overpayment%'::text)
    )
    GROUP BY p.id
    , c.accno
    , p.reference
    , p.payment_class
    , p.closed
    , p.payment_date
    , ac.chart_id
    , c.description
    , p.department_id
    , cmp.legal_name
    , eca.id
    , eca.entity_id
    , eca.discount
    , eca.meta_number;

Index - Schema public

## Table: parts

This stores detail information about goods and services. The type of part is currently defined according to the following rules: * If assembly is true, then an assembly * If inventory_accno_id, income_accno_id, and expense_accno_id are not null then a part. * If inventory_accno_id is null but the other two are not, then a service. * Otherwise, a labor/overhead entry.

parts Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| id | serial | PRIMARY KEY
| partnumber | text |
| description | text |
| unit | character varying(5) |
| listprice | numeric |
| sellprice | numeric |
| lastcost | numeric |
| priceupdate | date | DEFAULT ('now'::text)::date
| weight | numeric |
| onhand | numeric |
| notes | text |
| makemodel | boolean | DEFAULT false
| assembly | boolean | DEFAULT false
| alternate | boolean | DEFAULT false
| rop | numeric | Re-order point. Used to select parts for short inventory report.
| inventory_accno_id | integer |
| income_accno_id | integer |
| expense_accno_id | integer |
| bin | text | Text identifier for where a part is stored.
| obsolete | boolean | DEFAULT false
| bom | boolean | DEFAULT false Show on Bill of Materials.
| image | text | Hyperlink to product image.
| drawing | text |
| microfiche | text |
| partsgroup_id | integer |
| project_id | integer |
| avgcost | numeric |

Tables referencing this one via Foreign Key Constraints:

- assembly

- file_part

- invoice

- parts_translation

- partstax

parts_description_key lower(description) parts_id_key id parts_partnumber_key lower(partnumber)

Index - Schema public

## Table: parts_translation

Translation information for parts.

parts_translation Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
parts.id | trans_id | integer | PRIMARY KEY
| language_code | character varying(6) | PRIMARY KEY
| description | text |

Table parts_translation Inherits translation,

Index - Schema public

## Table: partscustomer

Tracks per-customer pricing. Discounts can be offered for periods of time and for pricegroups as well as per customer

partscustomer Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| parts_id | integer |
entity_credit_account.id | credit_id | integer |
pricegroup.id | pricegroup_id | integer |
| pricebreak | numeric |
| sellprice | numeric |
| validfrom | date |
| validto | date |
| curr | character(3) |
| entry_id | serial | PRIMARY KEY

Index - Schema public

## Table: partsgroup

Groups of parts for Point of Sale screen.

partsgroup Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| id | serial | PRIMARY KEY
| partsgroup | text |

Tables referencing this one via Foreign Key Constraints:

- partsgroup_translation

partsgroup_id_key id

Index - Schema public

## Table: partsgroup_translation

Translation information for partsgroups.

partsgroup_translation Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
partsgroup.id | trans_id | integer | PRIMARY KEY
| language_code | character varying(6) | PRIMARY KEY
| description | text |

Table partsgroup_translation Inherits translation,

Index - Schema public

## Table: partstax

Mapping of parts to taxes.

partstax Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
parts.id | parts_id | integer | PRIMARY KEY
account.id | chart_id | integer | PRIMARY KEY
taxcategory.taxcategory_id | taxcategory_id | integer |

partstax_parts_id_key parts_id

Index - Schema public

## Table: partsvendor

Tracks vendor's pricing, as well as vendor's part number, lead time required and currency.

partsvendor Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
entity_credit_account.id | credit_id | integer | NOT NULL
| parts_id | integer |
| partnumber | text |
| leadtime | smallint |
| lastcost | numeric |
| curr | character(3) |
| entry_id | serial | PRIMARY KEY

partsvendor_parts_id_key parts_id

Index - Schema public

## Table: payment

This table will store the main data on a payment, prepayment, overpayment, et

payment Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| id | serial | PRIMARY KEY
| reference | text | NOT NULL This field will store the code for both receipts and payment order
gl.id | gl_id | integer | A payment should always be linked to a GL movement
| payment_class | integer | NOT NULL
| payment_date | date | DEFAULT ('now'::text)::date
| closed | boolean | DEFAULT false This will store the current state of a payment/receipt order
entity_credit_account.id | entity_credit_id | integer |
person.id | employee_id | integer |
| currency | character(3) |
| notes | text |
| department_id | integer |

Tables referencing this one via Foreign Key Constraints:

- payment_links

payment_id_idx id

Index - Schema public

## Table: payment_links

An explanation to the type field. * A type 0 means the link is referencing an ar/ap and was created using an overpayment movement after the receipt was created * A type 1 means the link is referencing an ar/ap and was made on the payment creation, its not the product of an overpayment movement * A type 2 means the link is not referencing an ar/ap and its the product of the overpayment logic With this ideas in order we can do the following To get the payment amount we will sum the entries with type > 0. To get the linked amount we will sum the entries with type < 2. The overpayment account can be obtained from the entries with type = 2. This reasoning is hacky and i hope it can dissapear when we get to 1.4 - D.M.

payment_links Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
payment.id | payment_id | integer |
acc_trans.entry_id | entry_id | integer |
| type | integer |

Index - Schema public

## Table: payment_type

payment_type Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| id | serial | UNIQUE NOT NULL
| label | text | PRIMARY KEY

Index - Schema public

## Table: payments_queue

This is a holding table and hence not a candidate for normalization. Jobs should be deleted from this table when they complete successfully.

payments_queue Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| transactions | numeric[] |
| batch_id | integer |
| source | text |
| total | numeric |
| ar_ap_accno | text |
| cash_accno | text |
| payment_date | date |
| account_class | integer |
pending_job.id | job_id | integer | DEFAULT currval('pending_job_id_seq'::regclass)

payments_queue_job_id job_id

Index - Schema public

## Table: pending_job

Purpose: This table stores pending/queued jobs to be processed async. Additionally, this functions as a log of all such processing for purposes of internal audits, performance tuning, and the like.

pending_job Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| id | serial | UNIQUE NOT NULL
batch_class.id | batch_class | integer |
users.username | entered_by | text | NOT NULL DEFAULT "session_user"()
| entered_at | timestamp without time zone | DEFAULT now()
batch.id | batch_id | integer |
| completed_at | timestamp without time zone |
| success | boolean |
| error_condition | text |

pending_job Constraints
Name | Constraint
--- | ---
pending_job_check | CHECK (((completed_at IS NULL) OR (success IS NOT NULL)))
pending_job_check1 | CHECK (((success IS NOT FALSE) OR (error_condition IS NOT NULL)))

Tables referencing this one via Foreign Key Constraints:

- payments_queue

pending_job_batch_id_pending batch_id) WHERE (success IS NULL pending_job_entered_by entered_by

Index - Schema public

## View: periods

periods Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| id | text |
| label | text |
| date_to | date |
| date_from | date |

    SELECT'ytd' AS id
    ,'Year to Date' AS label
    , (now
         ()
    )::date AS date_to
    , (
         (
               (date_part
                     ('year'::text
                           , now
                           ()
                     )
               )::text || '-01-01'::text
         )
    )::date AS date_from
    UNIONSELECT'last_year' AS id
    ,'Last Year' AS label
    , (
         (
               (
                     (date_part
                           ('YEAR'::text
                                 , now
                                 ()
                           ) -
                           (1)::double precision
                     )
               )::text || '-12-31'::text
         )
    )::date AS date_to
    , (
         (
               (
                     (date_part
                           ('YEAR'::text
                                 , now
                                 ()
                           ) -
                           (1)::double precision
                     )
               )::text || '-01-01'::text
         )
    )::date AS date_from;

Index - Schema public

## Table: person

Every person, must have an entity to derive a common or display name. The correct way to get class information on a person would be person.entity_id->entity_class_to_entity.entity_id.

person Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| id | serial | PRIMARY KEY
entity.id | entity_id | integer | UNIQUE NOT NULL
salutation.id | salutation_id | integer |
| first_name | text | NOT NULL
| middle_name | text |
| last_name | text | NOT NULL
| created | date | NOT NULL DEFAULT ('now'::text)::date

person Constraints
Name | Constraint
--- | ---
person_first_name_check | CHECK ((first_name ~ '[[:alnum:]_]'::text))
person_last_name_check | CHECK ((last_name ~ '[[:alnum:]_]'::text))

Tables referencing this one via Foreign Key Constraints:

- audittrail

- entity_credit_account

- gl

- jcitems

- oe

- payment

- person_to_company

- person_to_contact

- person_to_entity

- person_to_location

Index - Schema public

## Table: person_to_company

currently unused in the front-end, but can be used to map persons to companies.

person_to_company Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
location.id | location_id | integer | PRIMARY KEY
person.id | person_id | integer | PRIMARY KEY
company.id | company_id | integer | NOT NULL

Index - Schema public

## Table: person_to_contact

This table stores contact information for companies

person_to_contact Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
person.id | person_id | integer | PRIMARY KEY
contact_class.id | contact_class_id | integer | PRIMARY KEY
| contact | text | PRIMARY KEY
| description | text |

person_to_contact Constraints
Name | Constraint
--- | ---
person_to_contact_contact_check | CHECK ((contact ~ '[[:alnum:]_]'::text))

Index - Schema public

## Table: person_to_entity

This provides a map so that entities can also be used like groups.

person_to_entity Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
person.id | person_id | integer | PRIMARY KEY
entity.id | entity_id | integer | PRIMARY KEY
| related_how | text |
| created | date | NOT NULL DEFAULT ('now'::text)::date

person_to_entity Constraints
Name | Constraint
--- | ---
person_to_entity_check | CHECK ((entity_id <> person_id))

Index - Schema public

## Table: person_to_location

person_to_location Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
location.id | location_id | integer | PRIMARY KEY
location_class.id | location_class | integer | NOT NULL
person.id | person_id | integer | PRIMARY KEY

Index - Schema public

## Table: pricegroup

Pricegroups are groups of customers who are assigned prices and discounts together.

pricegroup Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| id | serial | PRIMARY KEY
| pricegroup | text |

Tables referencing this one via Foreign Key Constraints:

- entity_credit_account

- partscustomer

pricegroup_id_key id pricegroup_pricegroup_key pricegroup

Index - Schema public

## Table: project

project Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| id | serial | PRIMARY KEY
| projectnumber | text |
| description | text |
| startdate | date |
| enddate | date |
| parts_id | integer | Job costing/manufacturing here not implemented.
| production | numeric |
| completed | numeric |
entity_credit_account.id | credit_id | integer |

Tables referencing this one via Foreign Key Constraints:

- project_translation

project_id_key id

Index - Schema public

## Table: project_translation

Translation information for projects.

project_translation Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
project.id | trans_id | integer | PRIMARY KEY
| language_code | character varying(6) | PRIMARY KEY
| description | text |

Table project_translation Inherits translation,

Index - Schema public

## View: recon_payee

recon_payee Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| payee | text |
| id | bigint |
| report_id | integer |
| scn | text |
| their_balance | numeric |
| our_balance | numeric |
| errorcode | integer |
| user | integer |
| clear_time | date |
| insert_time | timestamp with time zone |
| trans_type | text |
| post_date | date |
| ledger_id | integer |
| voucher_id | integer |
| overlook | boolean |
| cleared | boolean |

    SELECT n.name AS payee
    , rr.id
    , rr.report_id
    , rr.scn
    , rr.their_balance
    , rr.our_balance
    , rr.errorcode
    , rr."user"
    , rr.clear_time
    , rr.insert_time
    , rr.trans_type
    , rr.post_date
    , rr.ledger_id
    , rr.voucher_id
    , rr.overlook
    , rr.cleared
    FROM (
         (
               (cr_report_line rr
             LEFT JOIN acc_trans ac
                    ON (
                           (rr.ledger_id = ac.entry_id)
                     )
               )
       LEFT JOIN gl
              ON (
                     (ac.trans_id = gl.id)
               )
         )
    LEFT JOIN (
               (
                SELECT ap.id
                     , e.name
                  FROM (
                           (ap
                              JOIN entity_credit_account eca
                                ON (
                                       (ap.entity_credit_account = eca.id)
                                 )
                           )
                        JOIN entity e
                          ON (
                                 (eca.entity_id = e.id)
                           )
                     )
                 UNIONSELECT ar.id
                     , e.name
                  FROM (
                           (ar
                              JOIN entity_credit_account eca
                                ON (
                                       (ar.entity_credit_account = eca.id)
                                 )
                           )
                        JOIN entity e
                          ON (
                                 (eca.entity_id = e.id)
                           )
                     )
               )
           UNIONSELECT gl.id
               , gl.description
            FROM gl
         ) n
        ON (
               (n.id = ac.trans_id)
         )
    );

Index - Schema public

## Table: recurring

Stores recurring information on transactions which will recur in the future. Note that this means that only fully posted transactions can recur. I would highly recommend depricating this table and working instead on extending the template transaction addon to handle recurring information.

recurring Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| id | integer | PRIMARY KEY DEFAULT nextval('id'::regclass)
| reference | text |
| startdate | date |
| nextdate | date |
| enddate | date |
| repeat | smallint |
| unit | character varying(6) |
| howmany | integer |
| payment | boolean | DEFAULT false

Index - Schema public

## Table: recurringemail

Email to be sent out when recurring transaction is posted.

recurringemail Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| id | integer | PRIMARY KEY
| formname | text | PRIMARY KEY
| format | text |
| message | text |

Index - Schema public

## Table: recurringprint

Template, printer etc. to print to when recurring transaction posts.

recurringprint Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| id | integer | PRIMARY KEY
| formname | text | PRIMARY KEY
| format | text |
| printer | text |

Index - Schema public

## View: role_view

role_view Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| roleid | oid |
| member | oid |
| grantor | oid |
| admin_option | boolean |
| rolname | name |
| rolsuper | boolean |
| rolinherit | boolean |
| rolcreaterole | boolean |
| rolcreatedb | boolean |
| rolcatupdate | boolean |
| rolcanlogin | boolean |
| rolconnlimit | integer |
| rolpassword | text |
| rolvaliduntil | timestamp with time zone |
| rolconfig | text[] |
| oid | oid |

    SELECT m.roleid
    , m.member
    , m.grantor
    , m.admin_option
    , a.rolname
    , a.rolsuper
    , a.rolinherit
    , a.rolcreaterole
    , a.rolcreatedb
    , a.rolcatupdate
    , a.rolcanlogin
    , a.rolconnlimit
    , a.rolpassword
    , a.rolvaliduntil
    , a.rolconfig
    , a.oid
    FROM (pg_auth_members m
      JOIN pg_roles a
        ON (
               (m.roleid = a.oid)
         )
    );

Index - Schema public

## Table: salutation

salutation Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| id | serial | UNIQUE NOT NULL
| salutation | text | PRIMARY KEY

Tables referencing this one via Foreign Key Constraints:

- person

Index - Schema public

## Table: session

This table is used to track sessions on a database level across page requests. Because of the way LedgerSMB authentication works currently we do not time out authentication when the session times out. We do time out highly pessimistic locks used for large batch payment workflows.

session Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| session_id | serial | PRIMARY KEY
| token | character varying(32) |
| last_used | timestamp without time zone | DEFAULT now()
| ttl | integer | NOT NULL DEFAULT 3600
users.id | users_id | integer | NOT NULL
| notify_pasword | interval | NOT NULL DEFAULT '7 days'::interval

session Constraints
Name | Constraint
--- | ---
session_token_check | CHECK ((length((token)::text) = 32))

Tables referencing this one via Foreign Key Constraints:

- batch

- open_forms

- transactions

Index - Schema public

## Table: sic

This can be used SIC codes or any equivalent, such as ISIC, NAICS, etc.

sic Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| code | character varying(6) | PRIMARY KEY
| sictype | character(1) |
| description | text |

Index - Schema public

## Table: status

Whether AR/AP transactions and invoices have been emailed and/or printed

status Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| trans_id | integer | PRIMARY KEY
| formname | text | PRIMARY KEY
| printed | boolean | DEFAULT false
| emailed | boolean | DEFAULT false
| spoolfile | text |

status_trans_id_key trans_id

Index - Schema public

## Table: tax

Information on tax rates.

tax Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
account.id account.id | chart_id | integer | PRIMARY KEY
| rate | numeric |
| taxnumber | text |
| validto | timestamp without time zone | PRIMARY KEY DEFAULT 'infinity'::timestamp without time zone
| pass | integer | NOT NULL This is an integer indicating the pass of the tax. This is to support cumultative sales tax rules (for example, Quebec charging taxes on the federal taxes collected).
taxmodule.taxmodule_id | taxmodule_id | integer | NOT NULL DEFAULT 1

Index - Schema public

## Table: tax_extended

This stores extended information for manual tax calculations.

tax_extended Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| tax_basis | numeric |
| rate | numeric |
acc_trans.entry_id | entry_id | integer | PRIMARY KEY

Index - Schema public

## Table: taxcategory

taxcategory Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| taxcategory_id | serial | PRIMARY KEY
| taxcategoryname | text | NOT NULL
taxmodule.taxmodule_id | taxmodule_id | integer | NOT NULL

Tables referencing this one via Foreign Key Constraints:

- partstax

Index - Schema public

## Table: taxmodule

This is used to store information on tax modules. the module name is used to determine the Perl class for the taxes.

taxmodule Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| taxmodule_id | serial | PRIMARY KEY
| taxmodulename | text | NOT NULL

Tables referencing this one via Foreign Key Constraints:

- tax

- taxcategory

Index - Schema public

## Table: transactions

This table provides referential integrity between AR, AP, GL tables on one hand and acc_trans on the other, pending the refactoring of those tables. It also is used to provide discretionary locking of financial transactions across database connections, for example in batch payment workflows.

transactions Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| id | integer | PRIMARY KEY
| table_name | text |
session.session_id | locked_by | integer | This should only be used in pessimistic locking measures as required by large batch work flows.
entity.id | approved_by | integer |
| approved_at | timestamp without time zone |

Tables referencing this one via Foreign Key Constraints:

- acc_trans

- ap

- ar

- file_order_to_tx

- file_transaction

- gl

- invoice

- new_shipto

- voucher

transactions_locked_by_i locked_by

Index - Schema public

## Table: translation

abstract table for manual translation data. Should have zero rows.

translation Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| trans_id | integer | PRIMARY KEY
| language_code | character varying(6) | PRIMARY KEY
| description | text |

translation_trans_id_key trans_id

Index - Schema public

## View: user_listable

user_listable Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| id | integer |
| username | character varying(30) |
| created | date |

    SELECT u.id
    , u.username
    , e.created
    FROM (entity e
      JOIN users u
        ON (
               (u.entity_id = e.id)
         )
    );

Index - Schema public

## Table: user_preference

This table sets the basic preferences for formats, languages, printers, and user-selected stylesheets.

user_preference Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
users.id | id | integer | PRIMARY KEY
language.code | language | character varying(6) |
| stylesheet | text | NOT NULL DEFAULT 'ledgersmb.css'::text
| printer | text |
| dateformat | text | NOT NULL DEFAULT 'yyyy-mm-dd'::text
| numberformat | text | NOT NULL DEFAULT '1000.00'::text

Index - Schema public

## Table: users

username is the actual primary key here because we do not want duplicate users

users Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| id | serial | UNIQUE NOT NULL
| username | character varying(30) | PRIMARY KEY
| notify_password | interval | NOT NULL DEFAULT '7 days'::interval
entity.id | entity_id | integer | NOT NULL

Tables referencing this one via Foreign Key Constraints:

- lsmb_roles

- pending_job

- session

- user_preference

Index - Schema public

## Table: vendortax

Mapping vendor to taxes.

vendortax Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
entity_credit_account.id | vendor_id | integer | PRIMARY KEY
account.id | chart_id | integer | PRIMARY KEY

Index - Schema public

## Table: voucher

Mapping transactions to batches for batch approval.

voucher Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
transactions.id | trans_id | integer | NOT NULL
batch.id | batch_id | integer | NOT NULL
| id | serial | PRIMARY KEY This is simply a surrogate key for easy reference.
batch_class.id | batch_class | integer | NOT NULL This is the authoritative class of the voucher.

Tables referencing this one via Foreign Key Constraints:

- acc_trans

- cr_report_line

Index - Schema public

## Table: warehouse

warehouse Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
| id | serial | PRIMARY KEY
| description | text |

Tables referencing this one via Foreign Key Constraints:

- asset_item

- asset_report_line

Index - Schema public

## Table: yearend

An extension to the gl table to track transactionsactions which close out the books at yearend.

yearend Structure
F-Key | Name | Type | Description
--- | --- | --- | ---
gl.id | trans_id | integer | PRIMARY KEY
| reversed | boolean | DEFAULT false
| transdate | date |

Index - Schema public

## Function: _entity_location_save(in_country_code integer, in_mail_code integer, in_state integer, in_city text, in_line_three text, in_line_two text, in_line_one text, in_location_class text, in_location_id text, in_entity_id integer)

### Returns: integer

### Language: PLPGSQL

Private method for storing locations to an entity. Do not call directly. Returns the location id that was inserted or updated.

        DECLARE
            l_row location;
            l_id INT;
    	t_company_id int;
        BEGIN
    	SELECT id INTO t_company_id
    	FROM company WHERE entity_id = in_entity_id;

    	DELETE FROM company_to_location
    	WHERE company_id = t_company_id
    		AND location_class = in_location_class
    		AND location_id = in_location_id;

    	SELECT location_save(NULL, in_line_one, in_line_two, in_line_three, in_city,
    		in_state, in_mail_code, in_country_code)
    	INTO l_id;

    	INSERT INTO company_to_location
    		(company_id, location_class, location_id)
    	VALUES  (t_company_id, in_location_class, l_id);

    	RETURN l_id;
        END;

## Function: _get_parser_from_curcfg()

### Returns: text

### Language: SQL

    select prsname::text from pg_catalog.pg_ts_parser p join pg_ts_config c on cfgparser = p.oid where c.oid = show_curcfg();

## Function: account__get_by_link_desc(in_description text)

### Returns: SET OF account

### Language: SQL

Gets a list of accounts with a specific link description set. For example, for a dropdown list.

    SELECT * FROM account
    WHERE id IN (SELECT account_id FROM account_link WHERE description = $1);

## Function: account__get_from_accno(in_accno text)

### Returns: account

### Language: SQL

Returns the account where the accno field matches (excatly) the in_accno provided.

         select * from account where accno = $1;

## Function: account__get_taxes()

### Returns: SET OF account

### Language: SQL

Returns set of accounts where the tax attribute is true.

    SELECT * FROM account
     WHERE tax is true
    ORDER BY accno;

## Function: account__obtain_balance(in_account_id date, in_transdate integer)

### Returns: numeric

### Language: PLPGSQL

Returns the account balance at a given point in time, calculating forward from most recent check point.

    DECLARE balance numeric;
    BEGIN
    	SELECT coalesce(sum(ac.amount) + cp.amount, sum(ac.amount))
    	INTO balance
    	FROM acc_trans ac
    	JOIN (select id, approved from ar union
    		select id, approved from ap union
    		select id, approved from gl) a ON (a.id = ac.trans_id)
    	LEFT JOIN (select account_id, end_date, amount from account_checkpoint
    		WHERE account_id = in_account_id AND end_date < in_transdate
    		ORDER BY end_date desc limit 1
    	) cp ON (cp.account_id = ac.chart_id)
    	WHERE ac.chart_id = in_account_id
    		AND ac.transdate > coalesce(cp.end_date, ac.transdate - '1 day'::interval)
    		and ac.approved and a.approved
    		and ac.transdate <= in_transdate
    	GROUP BY cp.amount, ac.chart_id;

    	RETURN balance;
    END;

## Function: account__save_tax(in_old_validto integer, in_taxmodule_id date, in_pass numeric, in_taxnumber text, in_rate integer, in_validto integer, in_chart_id date)

### Returns: boolean

### Language: PLPGSQL

This saves tax rates.

    BEGIN
    	UPDATE tax SET validto = in_validto,
                   rate = in_rate,
                   taxnumber = in_taxnumber,
                   pass = in_pass,
                   taxmodule_id = in_taxmodule_id
             WHERE chart_id = in_chart_id and validto = in_old_validto;

             IF FOUND THEN
                 return true;
             END IF;

             INSERT INTO tax(chart_id, validto, rate, taxnumber, pass, taxmodule_id)
             VALUES (in_chart_id, in_validto, in_rate, in_taxnumber, in_pass,
                     in_taxmodule_id);

             RETURN TRUE;

    END;

## Function: account_get(in_id integer)

### Returns: SET OF chart

### Language: SQL

Returns an entry from the chart view which matches the id requested, and which is an account, not a heading.

    SELECT * from chart where id = $1 and charttype = 'A';

## Function: account_has_transactions(in_id integer)

### Returns: boolean

### Language: PLPGSQL

Checks to see if any transactions use this account. If so, returns true. If not, returns false.

    BEGIN
    	PERFORM trans_id FROM acc_trans WHERE chart_id = in_id LIMIT 1;
    	IF FOUND THEN
    		RETURN true;
    	ELSE
    		RETURN false;
    	END IF;
    END;

## Function: account_heading__list()

### Returns: SET OF account_heading

### Language: SQL

Returns a list of all account headings, currently ordered by account number.

     SELECT * FROM account_heading order by accno;

## Function: account_heading_get(in_id integer)

### Returns: chart

### Language: PLPGSQL

Returns an entry from the chart view which matches the id requested, and which is a heading, not an account.

    DECLARE
    	account chart%ROWTYPE;
    BEGIN
    	SELECT * INTO account FROM chart WHERE id = in_id AND charttype = 'H';
    	RETURN account;
    END;

## Function: account_heading_list()

### Returns: SET OF account_heading

### Language: SQL

Lists all existing account headings.

    SELECT * FROM account_heading order by accno;

## Function: account_heading_save(in_parent integer, in_description text, in_accno text, in_id integer)

### Returns: integer

### Language: PLPGSQL

Saves an account heading.

    BEGIN
    	UPDATE account_heading
    	SET accno = in_accno,
    		description = in_description,
    		parent_id = in_parent
    	WHERE id = in_id;

    	IF FOUND THEN
    		RETURN in_id;
    	END IF;
    	INSERT INTO account_heading (accno, description, parent_id)
    	VALUES (in_accno, in_description, in_parent);

    	RETURN currval('account_heading_id_seq');
    END;

## Function: account_save(in_link integer, in_tax text, in_contra text, in_heading bpchar, in_gifi_accno text, in_category integer, in_description boolean, in_accno boolean, in_id text[])

### Returns: integer

### Language: PLPGSQL

This deletes existing account_link entries, where the account_link.description is not designated as a custom one in the account_link_description table. If no account heading is provided, the account heading which has an accno field closest to but prior (by collation order) is used. Then it saves the account information, and rebuilds the account_link records based on the in_link array.

    DECLARE
    	t_heading_id int;
    	t_link record;
    	t_id int;
            t_tax bool;
    BEGIN

        SELECT count(*) > 0 INTO t_tax FROM tax WHERE in_id = chart_id;
        t_tax := t_tax OR in_tax;
    	-- check to ensure summary accounts are exclusive
            -- necessary for proper handling by legacy code
        FOR t_link IN SELECT description FROM account_link_description
        WHERE summary='t'
    	LOOP
    		IF t_link.description = ANY (in_link) and array_upper(in_link, 1) > 1 THEN
    			RAISE EXCEPTION 'Invalid link settings:  Summary';
    		END IF;
    	END LOOP;
    	-- heading settings
    	IF in_heading IS NULL THEN
    		SELECT id INTO t_heading_id FROM account_heading
    		WHERE accno < in_accno order by accno desc limit 1;
    	ELSE
    		t_heading_id := in_heading;
    	END IF;

        -- don't remove custom links.
    	DELETE FROM account_link
    	WHERE account_id = in_id
                  and description in ( select description
                                        from  account_link_description
                                        where custom = 'f');

    	UPDATE account
    	SET accno = in_accno,
    		description = in_description,
    		category = in_category,
    		gifi_accno = in_gifi_accno,
    		heading = t_heading_id,
    		contra = in_contra,
                    tax = t_tax
    	WHERE id = in_id;

    	IF FOUND THEN
    		t_id := in_id;
    	ELSE
    		INSERT INTO account (accno, description, category, gifi_accno,
    			heading, contra, tax)
    		VALUES (in_accno, in_description, in_category, in_gifi_accno,
    			t_heading_id, in_contra, in_tax);

    		t_id := currval('account_id_seq');
    	END IF;

    	FOR t_link IN
    		select in_link[generate_series] AS val
    		FROM generate_series(array_lower(in_link, 1),
    			array_upper(in_link, 1))
    	LOOP
    		INSERT INTO account_link (account_id, description)
    		VALUES (t_id, t_link.val);
    	END LOOP;

    	RETURN t_id;
    END;

## Function: add_custom_field(character varying, character varying, character varying)

### Returns: boolean

### Language: PLPGSQL

    DECLARE
    table_name ALIAS FOR $1;
    new_field_name ALIAS FOR $2;
    field_datatype ALIAS FOR $3;

    BEGIN
    	perform TABLE_ID FROM custom_table_catalog
    		WHERE extends = table_name;
    	IF NOT FOUND THEN
    		BEGIN
    			INSERT INTO custom_table_catalog (extends)
    				VALUES (table_name);
    			EXECUTE 'CREATE TABLE ' ||
                                   quote_ident('custom_' ||table_name) ||
    				' (row_id INT PRIMARY KEY)';
    		EXCEPTION WHEN duplicate_table THEN
    			-- do nothing
    		END;
    	END IF;
    	INSERT INTO custom_field_catalog (field_name, table_id)
    	values (new_field_name, (SELECT table_id
                                            FROM custom_table_catalog
    		WHERE extends = table_name));
    	EXECUTE 'ALTER TABLE '|| quote_ident('custom_'||table_name) ||
                    ' ADD COLUMN ' || quote_ident(new_field_name) || ' ' ||
                      quote_ident(field_datatype);
    	RETURN TRUE;
    END;

## Function: admin__add_function_to_group(in_role text, in_func text)

### Returns: integer

### Language: PLPGSQL

        declare
            stmt TEXT;
            a_role name;
            a_user name;
        BEGIN

            -- Issue the grant
            select rolname into a_role from pg_roles where rolname = in_role;

            IF NOT FOUND THEN
                RAISE EXCEPTION 'Cannot grant permissions of a non-existant role.';
            END IF;

            select rolname into a_user from pg_roles where rolname = in_username;

            IF NOT FOUND THEN
                RAISE EXCEPTION 'Cannot grant permissions to a non-existant user.';
            END IF;

            stmt := 'GRANT EXECUTE ON FUNCTION '|| quote_ident(in_func) ||' to '|| quote_ident(in_role);

            EXECUTE stmt;

            return 1;
        END;

## Function: admin__add_user_to_role(in_role text, in_username text)

### Returns: integer

### Language: PLPGSQL

        declare
            stmt TEXT;
            a_role name;
            a_user name;
        BEGIN

            -- Issue the grant
            select rolname into a_role from pg_roles where rolname = in_role;
            IF NOT FOUND THEN
                RAISE EXCEPTION 'Cannot grant permissions of a non-existant role.';
            END IF;

            select rolname into a_user from pg_roles where rolname = in_username;

            IF NOT FOUND THEN
                RAISE EXCEPTION 'Cannot grant permissions to a non-existant user.';
            END IF;

            stmt := 'GRANT '|| quote_ident(in_role) ||' to '|| quote_ident(in_username);

            EXECUTE stmt;
            insert into lsmb_roles (user_id, role)
            SELECT id, in_role from users where username = in_username;
            return 1;
        END;

## Function: admin__create_group(in_group_name text)

### Returns: integer

### Language: PLPGSQL

        DECLARE

            stmt text;
            t_dbname text;
        BEGIN
    	t_dbname := current_database();
            stmt := 'create role lsmb_'|| quote_ident(t_dbname || '__' || in_group_name);
            execute stmt;
            return 1;
        END;

## Function: admin__delete_group(in_group_name text)

### Returns: boolean

### Language: PLPGSQL

Deletes the input group from the database. Not designed to be used to remove a login-capable user.

        DECLARE
            stmt text;
            a_role role_view;
            t_dbname text;
        BEGIN
            t_dbname := current_database();

            select * into a_role from role_view where rolname = in_group_name;

            if not found then
                return 'f'::bool;
            else
                stmt := 'drop role lsmb_' || quote_ident(t_dbname || '__' || in_group_name);
                execute stmt;
                return 't'::bool;
            end if;
        END;

## Function: admin__delete_user(in_drop_role text, in_username boolean)

### Returns: integer

### Language: PLPGSQL

Drops the provided user, as well as deletes the user configuration data. It leaves the entity and person references. If in_drop_role is set, it drops the role too.

        DECLARE
            stmt text;
            a_user users;
        BEGIN

            select * into a_user from users where username = in_username;

            IF NOT FOUND THEN

                raise exception 'User not found.';
            ELSIF FOUND THEN
                IF in_drop_role IS TRUE then
                    stmt := ' drop user ' || quote_ident(a_user.username);
                    execute stmt;
                END IF;
                -- also gets user_connection
                delete from user_preference where id = (
                       select id from users where entity_id = a_user.entity_id);
                delete from users where entity_id = a_user.entity_id;
                return 1;
            END IF;
        END;

## Function: admin__drop_session(in_session_id integer)

### Returns: boolean

### Language: PLPGSQL

Drops the session identified, releasing all locks held.

    BEGIN
    	DELETE FROM "session" WHERE session_id = in_session_id;
    	RETURN FOUND;
    END;

## Function: admin__get_roles()

### Returns: SET OF pg_roles

### Language: PLPGSQL

    DECLARE
        v_rol record;
        t_dbname text;
    BEGIN
        t_dbname := current_database();
        FOR v_rol in
            SELECT *
            from
                pg_roles
            where
                rolname ~ ('^lsmb_' || t_dbname || '__')
                and rolcanlogin is false
            order by rolname ASC
        LOOP
            RETURN NEXT v_rol;
        END LOOP;
    END;

## Function: admin__get_roles_for_user(in_user_id integer)

### Returns: SET OF text

### Language: PLPGSQL

Returns a set of roles that a user is a part of.

        declare
            u_role record;
            a_user users;
        begin
            select * into a_user from admin__get_user(in_user_id);

            FOR u_role IN
            select r.rolname
            from
                pg_roles r,
                (select
                    m.roleid
                 from
                    pg_auth_members m, pg_roles b
                 where
                    m.member = b.oid
                 and
                    b.rolname = a_user.username
                ) as ar
             where
                r.oid = ar.roleid
             LOOP

                RETURN NEXT u_role.rolname::text;

            END LOOP;
            RETURN;
        end;

## Function: admin__get_user(in_user_id integer)

### Returns: SET OF users

### Language: PLPGSQL

Returns a set of (only one) user specified by the id.

        DECLARE
            a_user users;
        BEGIN

            select * into a_user from users where id = in_user_id;
            return next a_user;
            return;

        END;

## Function: admin__is_group(in_group_name text)

### Returns: boolean

### Language: PLPGSQL

        -- This needs some work.  CT
        DECLARE

            existant_role pg_roles;
            stmt text;

        BEGIN
            select * into existant_role from pg_roles
            where rolname = in_group_name AND rolcanlogin is false;

            if not found then
                return 'f'::bool;

            else
                return 't'::bool;
            end if;
        END;

## Function: admin__is_user(in_user text)

### Returns: boolean

### Language: PLPGSQL

Returns true if user is set up in LedgerSMB. False otherwise.

        BEGIN

            PERFORM * from users where username = in_user;
            RETURN found;

        END;

## Function: admin__list_roles(in_username text)

### Returns: SET OF text

### Language: PLPGSQL

    DECLARE out_rolename RECORD;
    BEGIN
    	FOR out_rolename IN
    		SELECT rolname FROM pg_roles
    		WHERE oid IN (SELECT id FROM connectby (
    			'(SELECT m.member, m.roleid, r.oid FROM pg_roles r
    			LEFT JOIN pg_auth_members m ON (r.oid = m.roleid)) a',
    			'oid', 'member', 'oid', '320461', '0', ','
    			) c(id integer, parent integer, "level" integer,
    				path text, list_order integer)
    			)
    	LOOP
    		RETURN NEXT out_rolename.rolname;
    	END LOOP;
    END;

## Function: admin__list_sessions()

### Returns: SET OF session_result

### Language: SQL

Lists all active sessions.

    SELECT s.session_id, u.username, s.last_used, count(t.id)
    FROM "session" s
    JOIN users u ON (s.users_id = u.id)
    LEFT JOIN transactions t ON (t.locked_by = s.session_id)
    GROUP BY s.session_id, u.username, s.last_used
    ORDER BY u.username;

## Function: admin__remove_function_from_group(in_role text, in_func text)

### Returns: integer

### Language: PLPGSQL

        declare
            stmt TEXT;
            a_role name;
            a_user name;
        BEGIN

            -- Issue the grant
            select rolname into a_role from pg_roles where rolname = in_role;

            IF NOT FOUND THEN
                RAISE EXCEPTION 'Cannot revoke permissions of non-existant role $.';
            END IF;

            select rolname into a_user from pg_roles where rolname = in_username;

            IF NOT FOUND THEN
                RAISE EXCEPTION 'Cannot revoke permissions from a non-existant function.';
            END IF;

            stmt := 'REVOKE EXECUTE ON FUNCTION '|| quote_ident(in_func) ||' FROM '|| quote_ident(in_role);

            EXECUTE stmt;

            return 1;
        END;

## Function: admin__remove_user_from_role(in_role text, in_username text)

### Returns: integer

### Language: PLPGSQL

        declare
            stmt TEXT;
            a_role name;
            a_user name;
        BEGIN

            -- Issue the grant
            select rolname into a_role from pg_roles where rolname = in_role;

            IF NOT FOUND THEN
                RAISE EXCEPTION 'Cannot revoke permissions of a non-existant role.';
            END IF;

            select rolname into a_user from pg_roles where rolname = in_username;

            IF NOT FOUND THEN
                RAISE EXCEPTION 'Cannot revoke permissions from a non-existant user.';
            END IF;

            stmt := 'REVOKE '|| quote_ident(in_role) ||' FROM '|| quote_ident(in_username);

            EXECUTE stmt;

            return 1;
        END;

## Function: admin__save_user(in_import integer, in_password integer, in_username text, in_entity_id text, in_id boolean)

### Returns: integer

### Language: PLPGSQL

Creates a user and relevant records in LedgerSMB and PostgreSQL.

        DECLARE

            a_user users;
            v_user_id int;
            p_id int;
            l_id int;
            stmt text;
            t_is_role bool;
            t_is_user bool;
        BEGIN
            -- WARNING TO PROGRAMMERS:  This function runs as the definer and runs
            -- utility statements via EXECUTE.
            -- PLEASE BE VERY CAREFUL ABOUT SQL-INJECTION INSIDE THIS FUNCTION.

           PERFORM rolname FROM pg_roles WHERE rolname = in_username;
           t_is_role := found;
           t_is_user := admin__is_user(in_username);

           IF t_is_role is true and t_is_user is false and in_import is false THEN
              RAISE EXCEPTION 'Duplicate user';
            END IF;

            if t_is_role and in_password is not null then
                    execute 'ALTER USER ' || quote_ident( in_username ) ||
                         ' WITH ENCRYPTED PASSWORD ' || quote_literal (in_password)
                         || $e$ valid until $e$ ||
                          quote_literal(now() + '1 day'::interval);
            elsif in_import is false AND t_is_user is false
                  AND in_password IS NULL THEN
                    RAISE EXCEPTION 'No password';
            elsif  t_is_role is false THEN
                -- create an actual user
                    execute 'CREATE USER ' || quote_ident( in_username ) ||
                         ' WITH ENCRYPTED PASSWORD ' || quote_literal (in_password)
                         || $e$ valid until $e$ || quote_literal(now() + '1 day'::interval);
           END IF;

            select * into a_user from users lu where lu.id = in_id;
            IF FOUND THEN
                return a_user.id;
            ELSE
                -- Insert cycle

                --- The entity is expected to already BE created. See admin.pm.

                v_user_id := nextval('users_id_seq');
                insert into users (id, username, entity_id) VALUES (
                    v_user_id,
                    in_username,
                    in_entity_id
                );

                insert into user_preference (id) values (v_user_id);

                IF NOT exists(SELECT * FROM entity_employee WHERE entity_id = in_entity_id) THEN
                    INSERT into entity_employee (entity_id) values (in_entity_id);
                END IF;
                -- Finally, issue the create user statement

                return v_user_id ;

            END IF;

        END;

## Function: admin__search_users(in_dob text, in_ssn text, in_last_name text, in_first_name text, in_username date)

### Returns: SET OF user_result

### Language: PLPGSQL

Returns a list of users matching search criteria. Nulls match all values. only username is not an exact match.

    DECLARE t_return_row user_result;
    BEGIN
    	FOR t_return_row IN
    		SELECT u.id, u.username, p.first_name, p.last_name, e.ssn, e.dob
    		FROM users u
    		JOIN person p ON (u.entity_id = p.entity_id)
    		JOIN entity_employee e ON (e.entity_id = p.entity_id)
    		WHERE u.username LIKE '%' || coalesce(in_username,'') || '%' AND
    			(p.first_name = in_first_name or in_first_name is null)
    			AND (p.last_name = in_last_name or in_last_name is null)
    			AND (in_ssn is NULL or in_ssn = e.ssn)
    			AND (e.dob = in_dob::date or in_dob is NULL)
    	LOOP
    		RETURN NEXT t_return_row;
    	END LOOP;
    END;

## Function: as_array(anyelement)

### Returns: anyarray

### Language: INTERNAL

A basic array aggregate to take elements and return a one-dimensional array. Example: SELECT as_array(id) from entity_class;

    aggregate_dummy

## Function: asset__get(in_tag integer, in_id text)

### Returns: asset_item

### Language: PLPGSQL

Retrieves a given asset either by id or tag. Both are complete matches. Note that the behavior is undefined if both id and tag are provided.

    DECLARE ret_val asset_item;
    BEGIN
    	SELECT * into ret_val from asset_item WHERE id = in_id OR in_tag = tag
            ORDER BY id desc limit 1;
    	return ret_val;
    END;

## Function: asset__import_from_disposal(in_id integer)

### Returns: boolean

### Language: PLPGSQL

Imports items from partial disposal reports. This function should not be called dirctly by programmers but rather through the other disposal approval api's.

    DECLARE t_report asset_report;
            t_import asset_report;
    BEGIN

        SELECT * INTO t_report from asset_report where id = in_id;

        if t_report.report_class <> 4 THEN RETURN FALSE;
        END IF;

        SELECT *
          INTO t_import
          FROM  asset_report__begin_import
                (t_report.asset_class::int, t_report.report_date);

        PERFORM asset_report__import(
    	ai.description,
    	ai.tag,
    	ai.purchase_value * rld.percent_disposed / 100,
    	ai.salvage_value * rld.percent_disposed / 100,
    	ai.usable_life,
    	ai.purchase_date,
            ai.start_depreciation,
    	ai.location_id,
    	ai.department_id,
    	ai.asset_account_id,
    	ai.dep_account_id,
    	ai.exp_account_id,
    	ai.asset_class_id,
            ai.invoice_id,
            t_import.id,
            r.accum_depreciation * rld.percent_disposed / 100,
            TRUE)
        FROM asset_item ai
        JOIN asset_report__get_disposal(t_report.id) r  ON (ai.id = r.id)
        JOIN asset_report_line rl ON (rl.asset_id = ai.id AND rl.report_id = in_id)
        join asset_rl_to_disposal_method rld
             ON (rl.report_id = rld.report_id and ai.id = rld.asset_id)
       where rld.percent_disposed is null or percent_disposed < 100;
       RETURN TRUE;
    END;

## Function: asset__save(in_exp_account_id integer, in_dep_account_id integer, in_asset_account_id text, in_invoice_id text, in_department_id date, in_warehouse_id numeric, in_start_depreciation numeric, in_salvage_value numeric, in_usable_life date, in_purchase_value integer, in_purchase_date integer, in_tag integer, in_description integer, in_asset_class integer, in_id integer)

### Returns: asset_item

### Language: PLPGSQL

Saves the asset with the information provided. If the id is provided, overwrites the record with the id. Otherwise, or if that record is not found, inserts. Returns the row inserted or updated.

    DECLARE ret_val asset_item;
    BEGIN
    	UPDATE asset_item
    	SET asset_class_id = in_asset_class,
    		description = in_description,
    		tag = in_tag,
    		purchase_date = in_purchase_date,
    		purchase_value = in_purchase_value,
    		usable_life = in_usable_life,
    		location_id = in_warehouse_id,
    		department_id = in_department_id,
    		invoice_id = in_invoice_id,
    		salvage_value = in_salvage_value,
                    asset_account_id = in_asset_account_id,
                    exp_account_id = in_exp_account_id,
                    start_depreciation =
                             coalesce(in_start_depreciation, in_purchase_date),
                    dep_account_id = in_dep_account_id
    	WHERE id = in_id;
    	IF FOUND THEN
    		SELECT * INTO ret_val FROM asset_item WHERE id = in_id;
    		return ret_val;
    	END IF;

    	INSERT INTO asset_item (asset_class_id, description, tag, purchase_date,
    		purchase_value, usable_life, salvage_value, department_id,
    		location_id, invoice_id, asset_account_id, dep_account_id,
                    start_depreciation, exp_account_id)
    	VALUES (in_asset_class, in_description, in_tag, in_purchase_date,
    		in_purchase_value, in_usable_life, in_salvage_value,
    		in_department_id, in_warehouse_id, in_invoice_id,
                    in_asset_account_id, in_dep_account_id,
                    coalesce(in_start_depreciation, in_purchase_date),
                    in_exp_account_id);

    	SELECT * INTO ret_val FROM asset_item
    	WHERE id = currval('asset_item_id_seq');
    	RETURN ret_val;
    END;

## Function: asset__search(in_salvage_value integer, in_usable_life text, in_purchase_value text, in_purchase_date date, in_tag numeric, in_description numeric, in_asset_class numeric)

### Returns: SET OF asset_item

### Language: PLPGSQL

Searches for assets. Nulls match all records. Asset class is exact, as is purchase date, purchase value, and salvage value. Tag and description are partial matches.

    DECLARE out_val asset_item;
    BEGIN
    	FOR out_val IN
    		SELECT * FROM asset_item
    		WHERE (in_asset_class is null
    			or asset_class_id = in_asset_class)
    			AND (in_description is null or description
    				LIKE '%' || in_description || '%')
    			and (in_tag is not null or tag like '%'||in_tag||'%')
    			AND (in_purchase_date is null
    				or purchase_date = in_purchase_date)
    			AND (in_purchase_value is null
    				or in_purchase_value = purchase_value)
    			AND (in_usable_life is null
    				or in_usable_life = usable_life)
    			AND (in_salvage_value is null
    				OR in_salvage_value = salvage_value)
    	LOOP
    		RETURN NEXT out_val;
    	END LOOP;
    END;

## Function: asset_class__get(in_id integer)

### Returns: asset_class

### Language: PLPGSQL

returns the row from asset_class identified by in_id.

    DECLARE ret_val asset_class;
    BEGIN
    	SELECT * INTO ret_val FROM asset_class WHERE id = in_id;
    	RETURN ret_val;
    END;

## Function: asset_class__get_asset_accounts()

### Returns: SET OF account

### Language: SQL

Returns a list of fixed asset accounts, ordered by account number

    SELECT * FROM account
    WHERE id IN
    	(select account_id from account_link where description = 'Fixed_Asset')
    ORDER BY accno;

## Function: asset_class__get_dep_accounts()

### Returns: SET OF account

### Language: SQL

Returns a list of asset depreciation accounts, ordered by account number

    SELECT * FROM account
    WHERE id IN
    	(select account_id from account_link where description = 'Asset_Dep')
    ORDER BY accno;

## Function: asset_class__get_dep_method(in_asset_class integer)

### Returns: asset_dep_method

### Language: SQL

Returns the depreciation method associated with the asset class.

    SELECT * from asset_dep_method
    WHERE id = (select method from asset_class where id = $1);

## Function: asset_class__get_dep_methods()

### Returns: SET OF asset_dep_method

### Language: SQL

Returns a set of asset_dep_methods ordered by the method label.

    SELECT * FROM asset_dep_method ORDER BY method;

## Function: asset_class__list()

### Returns: SET OF asset_class

### Language: SQL

Returns an alphabetical list of asset classes.

    SELECT * FROM asset_class ORDER BY label;

## Function: asset_class__save(in_unit_label integer, in_label integer, in_method integer, in_dep_account_id integer, in_asset_account_id text, in_id text)

### Returns: asset_class

### Language: PLPGSQL

Saves this data as an asset_class record. If in_id is NULL or is not found in the table, inserts a new row. Returns the row saved.

    DECLARE ret_val asset_class;
    BEGIN
    	UPDATE asset_class
    	SET asset_account_id = in_asset_account_id,
    		dep_account_id = in_dep_account_id,
    		method = in_method,
    		label = in_label
    	WHERE id = in_id;

    	IF FOUND THEN
    		SELECT * INTO ret_val FROM asset_class where id = in_id;
    		RETURN ret_val;
    	END IF;

    	INSERT INTO asset_class (asset_account_id, dep_account_id, method,
    		label)
    	VALUES (in_asset_account_id, in_dep_account_id, in_method,
    		in_label);

    	SELECT * INTO ret_val FROM asset_class
    	WHERE id = currval('asset_class_id_seq');

    	RETURN ret_val;
    END;

## Function: asset_class__search(in_label integer, in_method integer, in_dep_account_id integer, in_asset_account_id text)

### Returns: SET OF asset_class_result

### Language: PLPGSQL

Returns a list of matching asset classes. The account id's are exact matches as is the method, but the label is a partial match. NULL's match all.

    DECLARE out_var asset_class_result;
    BEGIN
    	FOR out_var IN
    		SELECT ac.id, ac.asset_account_id, aa.accno, aa.description,
    			ad.accno, ad.description, m.method, ac.method,
    			ac.label
    		FROM asset_class ac
    		JOIN account aa ON (aa.id = ac.asset_account_id)
    		JOIN account ad ON (ad.id = ac.dep_account_id)
    		JOIN asset_dep_method m ON (ac.method = m.id)
    		WHERE
    			(in_asset_account_id is null
    				or in_asset_account_id = ac.asset_account_id)
    			AND (in_dep_account_id is null OR
    				in_dep_account_id = ac.dep_account_id)
    			AND (in_method is null OR in_method = ac.method)
    			AND (in_label IS NULL OR ac.label LIKE
    				'%' || in_label || '%')
                   ORDER BY label
    	LOOP
    		RETURN NEXT out_var;
    	END LOOP;
    END;

## Function: asset_dep__straight_line_base(in_dep_to_date numeric, in_basis numeric, in_used numeric, in_life numeric, in_base_life numeric)

### Returns: numeric

### Language: SQL

This function is a basic function which does the actual calculation for straight line depreciation.

    SELECT CASE WHEN $3/$1 * $4 < $4 - $5 THEN $3/$1 * $4
                ELSE $4 - $5
                END;

## Function: asset_dep__used_months(in_usable_life date, in_dep_date date, in_last_dep numeric)

### Returns: numeric

### Language: SQL

This checks the interval between the two dates, and if longer than the usable life, returns the months in that interval. Otherwise returns the usable life.

    select CASE WHEN extract('MONTHS' FROM (date_trunc('day', $2) - date_trunc('day', $1)))
                     > $3
                THEN $3
                ELSE extract('MONTHS' FROM (date_trunc('day', $2) - date_trunc('day', $1)))::numeric
                END;

## Function: asset_dep_get_usable_life_yr(in_dep_date numeric, in_start_date date, in_usable_life date)

### Returns: numeric

### Language: SQL

If the interval is less than 0 then 0. If the interval is greater than the usable life, then the usable life. Otherwise, return the interval as a fractional year.

       SELECT CASE WHEN $3 IS NULL or get_fractional_year($2, $3) > $1
                   then $1
                   WHEN get_fractional_year($2, $3) < 0
                   THEN 0
                   ELSE get_fractional_year($2, $3)
              END;

## Function: asset_dep_straight_line_month(in_report_id integer[], in_report_date date, in_asset_ids integer)

### Returns: boolean

### Language: SQL

Performs straight line depreciation, selecting depreciation amounts, etc. into a report for further review and approval. Usable life is in months, and depreciation is an equal amount every month.

         INSERT INTO asset_report_line (asset_id, report_id, amount, department_id,
                                       warehouse_id)
         SELECT ai.id, $3,
                asset_dep__straight_line_base(
                      ai.usable_life,
                      ai.usable_life --months
                      - months_passed(coalesce(start_depreciation, purchase_date),
                                      coalesce(max(report_date),
                                               start_depreciation,
                                               purchase_date)),
                      months_passed(coalesce(max(report_date),
                                             start_depreciation,
                                             purchase_date),
                                    $2),
                      purchase_value - salvage_value,
                      coalesce(sum(l.amount), 0)),
                ai.department_id, ai.location_id
           FROM asset_item ai
      LEFT JOIN asset_report_line l ON (l.asset_id = ai.id)
      LEFT JOIN asset_report r ON (l.report_id = r.id)
          WHERE ai.id = ANY($1)
       GROUP BY ai.id, ai.start_depreciation, ai.purchase_date, ai.purchase_value,
                ai.salvage_value, ai.department_id, ai.location_id, ai.usable_life;

        UPDATE asset_report SET report_class = 1 WHERE id = $3;

        select true;

## Function: asset_dep_straight_line_yr_m(in_report_id integer[], in_report_date date, in_asset_ids integer)

### Returns: boolean

### Language: SQL

Performs straight line depreciation on a set of selected assets, selecting the depreciation values into a report. Assumes the usable life is measured in years, and is depreciated eavenly every month.

         INSERT INTO asset_report_line (asset_id, report_id, amount, department_id,
                                       warehouse_id)
         SELECT ai.id, $3,
                asset_dep__straight_line_base(
                      ai.usable_life * 12,
                      ai.usable_life * 12 --months
                      - months_passed(coalesce(start_depreciation, purchase_date),
                                      coalesce(max(report_date),
                                               start_depreciation,
                                               purchase_date)),
                      months_passed(coalesce(max(report_date),
                                             start_depreciation,
                                             purchase_date),
                                    $2),
                      purchase_value - salvage_value,
                      coalesce(sum(l.amount), 0)),
                ai.department_id, ai.location_id
           FROM asset_item ai
      LEFT JOIN asset_report_line l ON (l.asset_id = ai.id)
      LEFT JOIN asset_report r ON (l.report_id = r.id)
          WHERE ai.id = ANY($1)
       GROUP BY ai.id, ai.start_depreciation, ai.purchase_date, ai.purchase_value,
                ai.salvage_value, ai.department_id, ai.location_id, ai.usable_life;

        UPDATE asset_report SET report_class = 1 WHERE id = $3;

        select true;

## Function: asset_depreciation__approve(in_expense_acct integer, in_report_id integer)

### Returns: asset_report

### Language: PLPGSQL

Approves an asset depreciation report and creats the GL draft.

    declare retval asset_report;
    begin

    retval := asset_report__record_approve(in_report_id);

    INSERT INTO gl (reference, description, approved)
    select 'Asset Report ' || in_id, 'Asset Depreciation Report for ' || report_date,
           false
     FROM asset_report where id = in_id;

    INSERT INTO acc_trans (amount, chart_id, transdate, approved, trans_id)
    SELECT l.amount, a.dep_account_id, r.report_date, true, currval('id')
      FROM asset_report r
      JOIN asset_report_line l ON (r.id = l.report_id)
      JOIN asset_item a ON (a.id = l.asset_id)
     WHERE r.id = in_id;

    INSERT INTO acc_trans (amount, chart_id, transdate, approved, trans_id)
    SELECT sum(l.amount) * -1, in_expense_acct, r.report_date, approved,
           currval('id')
      FROM asset_report r
      JOIN asset_report_line l ON (r.id = l.report_id)
      JOIN asset_item a ON (a.id = l.asset_id)
     WHERE r.id = in_id
     GROUP BY r.report_date;

    return retval;

    end;

## Function: asset_disposal__approve(in_asset_acct integer, in_loss_acct integer, in_gain_acct integer, in_id integer)

### Returns: asset_report

### Language: PLPGSQL

This approves the asset_report for disposals, creating relevant GL drafts. If the report is a partial disposal report, imports remaining percentages as new asset items.

    DECLARE
       retval asset_report;
       iter record;
       t_disposed_percent numeric;
    begin
    -- this code is fairly opaque and needs more documentation that would be
    -- otherwise optimal. This is mostly due to the fact that we have fairly
    -- repetitive insert/select routines and the fact that the accounting
    -- requirements are not immediately intuitive.  Inserts marked functionally along
    -- with typical debit/credit designations.  Note debits are always negative.

    retval := asset_report__record_approve(in_id);
    if retval.report_class = 2 then
         t_disposed_percent := 100;
    end if;

    INSERT INTO gl (reference, description, approved, transdate)
    select 'Asset Report ' || in_id, 'Asset Disposal Report for ' || report_date,
           false, report_date
     FROM asset_report where id = in_id;

    -- REMOVING ASSETS FROM ACCOUNT (Credit)
    insert into acc_trans (trans_id, chart_id, amount, approved, transdate)
    SELECT currval('id'), a.asset_account_id,
           a.purchase_value
           * (coalesce(t_disposed_percent, m.percent_disposed)/100),
           true, r.report_date
     FROM  asset_item a
     JOIN  asset_report_line l ON (l.asset_id = a.id)
     JOIN  asset_report r ON (r.id = l.report_id)
     JOIN  asset_rl_to_disposal_method m
            ON (l.report_id = m.report_id and l.asset_id = m.asset_id)
     WHERE r.id = in_id;

    -- REMOVING ACCUM DEP. (Debit)
    INSERT into acc_trans (trans_id, chart_id, amount, approved, transdate)
    SELECT currval('id'), a.dep_account_id,
           sum(dl.amount) * -1
           * (coalesce(t_disposed_percent, m.percent_disposed)/100),
           true, r.report_date
     FROM  asset_item a
     JOIN  asset_report_line l ON (l.asset_id = a.id)
     JOIN  asset_report r ON (r.id = l.report_id)
     JOIN  asset_report_line dl ON (l.asset_id = dl.asset_id)
     JOIN  asset_rl_to_disposal_method m
            ON (l.report_id = m.report_id and l.asset_id = m.asset_id)
     JOIN  asset_report dr ON (dl.report_id = dr.id
                               and dr.report_class = 1
                               and dr.approved_at is not null)
     WHERE r.id = in_id
    group by a.dep_account_id, m.percent_disposed, r.report_date;

    -- INSERT asset/proceeds (Debit, credit for negative values)
    INSERT INTO acc_trans (trans_id, chart_id, amount, approved, transdate)
    SELECT currval('id'), in_asset_acct, coalesce(l.amount, 0) * -1, true, r.report_date
     FROM  asset_item a
     JOIN  asset_report_line l ON (l.asset_id = a.id)
     JOIN  asset_report r ON (r.id = l.report_id)
     JOIN  asset_rl_to_disposal_method m
            ON (l.report_id = m.report_id and l.asset_id = m.asset_id)
     WHERE r.id = in_id;

    -- INSERT GAIN/LOSS (Credit for gain, debit for loss)
    INSERT INTO acc_trans(trans_id, chart_id, amount, approved, transdate)
    select currval('id'),
                CASE WHEN sum(amount) > 0 THEN in_loss_acct
                else in_gain_acct
            END,
            sum(amount) * -1 , true,
            retval.report_date
      FROM acc_trans
      WHERE trans_id = currval('id');

    IF retval.report_class = 4 then
       PERFORM asset__import_from_disposal(retval.id);
    end if;

    return retval;
    end;

## Function: asset_item__add_note(in_note integer, in_subject text, in_id text)

### Returns: asset_note

### Language: SQL

Adds a note to an asset item

    INSERT INTO asset_note (ref_key, subject, note) values ($1, $2, $3);
    SELECT * FROM asset_note WHERE id = currval('note_id_seq');

## Function: asset_item__search(in_dep_account_id integer, in_asset_account_id integer, in_invoice_id text, in_department_id text, in_warehouse_id date, in_start_depreciation numeric, in_salvage_value numeric, in_usable_life numeric, in_purchase_value date, in_purchase_date integer, in_tag integer, in_description integer, in_asset_class integer, in_id integer)

### Returns: SET OF asset_item

### Language: PLPGSQL

Returns a list of matching asset items. Nulls match all records. Tag and description allow for partial match. All other matches are exact.

    DECLARE retval asset_item;
    BEGIN
        FOR retval IN
             SELECT * FROM asset_item
              WHERE (id = in_id or in_id is null)
                    and (asset_class_id = in_asset_class or in_asset_class is null)
                    and (description like '%'||in_description||'%'
                         or in_description is null)
                    and (tag like '%' || in_tag || '%' or in_tag is null)
                    and (purchase_value = in_purchase_value
                        or in_purchase_value is null)
                    and (in_purchase_date = purchase_date
                        or in_purchase_date is null)
                    and (start_depreciation = in_start_depreciation
                        or in_start_depreciation is null)
                    and (in_warehouse_id = location_id OR in_warehouse_id is null)
                    and (department_id = in_department_id
                        or in_department_id is null)
                    and (in_invoice_id = invoice_id OR in_invoice_id IS NULL)
                    and (asset_account_id = in_asset_account_id
                        or in_asset_account_id is null)
                    and (dep_account_id = in_dep_account_id
                        or in_dep_account_id is null)
       LOOP
           return next retval;
       end loop;
    END;

## Function: asset_nbv_report()

### Returns: SET OF asset_nbv_line

### Language: SQL

Returns the current net book value report.

       SELECT ai.id, ai.tag, ai.description, coalesce(ai.start_depreciation, ai.purchase_date),
              adm.short_name, ai.usable_life
               - months_passed(coalesce(ai.start_depreciation, ai.purchase_date),
                                      coalesce(max(r.report_date),
                                               ai.start_depreciation,
                                               ai.purchase_date))/ 12,
              ai.purchase_value - ai.salvage_value, ai.salvage_value, max(r.report_date),
              sum(rl.amount), ai.purchase_value - sum(rl.amount)
         FROM asset_item ai
         JOIN asset_class ac ON (ai.asset_class_id = ac.id)
         JOIN asset_dep_method adm ON (adm.id = ac.method)
    LEFT JOIN asset_report_line rl ON (ai.id = rl.asset_id)
    LEFT JOIN asset_report r on (rl.report_id = r.id)
        WHERE r.approved_at IS NOT NULL
     GROUP BY ai.id, ai.tag, ai.description, ai.start_depreciation, ai.purchase_date,
              adm.short_name, ai.usable_life, ai.purchase_value, salvage_value
       HAVING (NOT 2 = ANY(as_array(r.report_class)))
              AND (NOT 4 = ANY(as_array(r.report_class)))
              OR max(r.report_class) IS NULL
     ORDER BY ai.id, ai.tag, ai.description;

## Function: asset_report__approve(in_loss_acct integer, in_gain_acct integer, in_expense_acct integer, in_id integer)

### Returns: asset_report

### Language: PLPGSQL

This function approves an asset report (whether depreciation or disposal). Also generates relevant GL drafts for review and posting.

    DECLARE ret_val asset_report;
    BEGIN
            UPDATE asset_report
               SET approved_at = now(),
                   approved_by = person__get_my_entity_id()
             where id = in_id;
    	SELECT * INTO ret_val FROM asset_report WHERE id = in_id;
            if ret_val.dont_approve is not true then
                    if ret_val.report_class = 1 THEN
                        PERFORM asset_report__generate_gl(in_id, in_expense_acct);
                    ELSIF ret_val.report_class = 2 THEN
                        PERFORM asset_report__disposal_gl(
                                     in_id, in_gain_acct, in_loss_acct);
                    ELSIF ret_val.report_class = 4 THEN
                        PERFORM asset_disposal__approve(in_id, in_gain_acct, in_loss_acct, (select asset_account_id from asset_class
                                                                                             where id = ret_val.asset_class)
                                                       );
                    ELSE RAISE EXCEPTION 'Invalid report class';
                    END IF;
            end if;
    	SELECT * INTO ret_val FROM asset_report WHERE id = in_id;
    	RETURN ret_val;
    end;

## Function: asset_report__begin_disposal(in_report_class integer, in_report_date date, in_asset_class integer)

### Returns: asset_report

### Language: PLPGSQL

Creates the asset report recofd for the asset disposal report.

    DECLARE retval asset_report;

    begin

    INSERT INTO asset_report (asset_class, report_date, entered_at, entered_by,
                report_class)
         VALUES (in_asset_class, in_report_date, now(), person__get_my_entity_id(),
                in_report_class);

    SELECT * INTO retval FROM asset_report where id = currval('asset_report_id_seq');

    return retval;

    end;

## Function: asset_report__begin_import(in_report_date integer, in_asset_class date)

### Returns: asset_report

### Language: SQL

Creates the outline of an asset import report

    INSERT INTO asset_report (asset_class, report_date, entered_at, entered_by,
                report_class, dont_approve)
         VALUES ($1, $2, now(), person__get_my_entity_id(),
                3, true);

    SELECT * FROM asset_report where id = currval('asset_report_id_seq');

## Function: asset_report__disposal_gl(in_loss_acct integer, in_gain_acct integer, in_id integer)

### Returns: boolean

### Language: SQL

Generates GL transactions for ful disposal reports.

      INSERT
        INTO gl (reference, description, transdate, approved)
      SELECT setting_increment('glnumber'), 'Asset Report ' || asset_report.id,
    		report_date, false
        FROM asset_report
        JOIN asset_report_line ON (asset_report.id = asset_report_line.report_id)
        JOIN asset_item        ON (asset_report_line.asset_id = asset_item.id)
       WHERE asset_report.id = $1
    GROUP BY asset_report.id, asset_report.report_date;

      INSERT
        INTO acc_trans (chart_id, trans_id, amount, approved, transdate)
      SELECT a.dep_account_id, currval('id')::int, sum(r.accum_depreciation) * -1,
             TRUE, r.disposed_on
        FROM asset_report__get_disposal($1) r
        JOIN asset_item a ON (r.id = a.id)
    GROUP BY a.dep_account_id, r.disposed_on;

      -- GAIN is negative since it is a debit
      INSERT
        INTO acc_trans (chart_id, trans_id, amount, approved, transdate)
      SELECT case when sum(r.gain_loss) > 0 THEN $3 else $2 end,
             currval('id')::int, sum(r.gain_loss),
             TRUE, r.disposed_on
        FROM asset_report__get_disposal($1) r
        JOIN asset_item ai ON (r.id = ai.id)
    GROUP BY r.disposed_on;

      INSERT
        INTO acc_trans (chart_id, trans_id, amount, approved, transdate)
      SELECT a.asset_account_id, currval('id')::int, sum(r.purchase_value),
             TRUE, r.disposed_on
        FROM asset_report__get_disposal($1) r
        JOIN asset_item a ON (r.id = a.id)
    GROUP BY a.asset_account_id, r.disposed_on;

      SELECT TRUE;

## Function: asset_report__dispose(in_percent_disposed integer, in_dm integer, in_amount numeric, in_asset_id integer, in_id numeric)

### Returns: boolean

### Language: PLPGSQL

Disposes of an asset. in_dm is the disposal method id.

    BEGIN
        INSERT
          INTO asset_report_line (report_id, asset_id, amount)
        values (in_id, in_asset_id, in_amount);

        INSERT
          INTO asset_rl_to_disposal_method
               (report_id, asset_id, disposal_method_id, percent_disposed)
        VALUES (in_id, in_asset_id, in_dm, in_percent_disposed);

        RETURN TRUE;
        END;

## Function: asset_report__generate(in_report_date boolean, in_asset_class integer, in_depreciation date)

### Returns: SET OF asset_item

### Language: SQL

Generates lines to select/deselect for the asset report (depreciation or disposal).

       SELECT ai.*
         FROM asset_item ai
         JOIN asset_class ac ON (ai.asset_class_id = ac.id)
    LEFT JOIN asset_report_line arl ON (arl.asset_id = ai.id)
    LEFT JOIN asset_report ar ON (arl.report_id = ar.id)
        WHERE COALESCE(ai.start_depreciation, ai.purchase_date) <= $3 AND ac.id = $2
              AND obsolete_by IS NULL
     GROUP BY ai.id, ai.tag, ai.description, ai.purchase_value, ai.usable_life,
              ai.purchase_date, ai.location_id, ai.invoice_id, ai.asset_account_id,
              ai.dep_account_id, ai.asset_class_id, ai.start_depreciation,
              ai.salvage_value, ai.department_id, ai.exp_account_id, ai.obsolete_by
       HAVING (count(ar.report_class) = 0 OR
              (2 <> ALL(as_array(ar.report_class))
              and 4 <> ALL(as_array(ar.report_class))))
              AND ((ai.purchase_value - coalesce(sum(arl.amount), 0)
                   > ai.salvage_value) and ai.obsolete_by is null)
                   OR $1 is not true;

## Function: asset_report__generate_gl(in_accum_account_id integer, in_report_id integer)

### Returns: integer

### Language: PLPGSQL

Generates a GL transaction when the Asset report is approved. Currently this creates GL drafts, not approved transctions

    DECLARE
    	t_report_dept record;
    	t_dep_amount numeric;

    Begin
    	INSERT INTO gl (reference, description, transdate, approved)
    	SELECT setting_increment('glnumber'), 'Asset Report ' || asset_report.id,
    		report_date, false
    	FROM asset_report
    	JOIN asset_report_line
    		ON (asset_report.id = asset_report_line.report_id)
    	JOIN asset_item
    		ON (asset_report_line.asset_id = asset_item.id)
    	WHERE asset_report.id = in_report_id
    	GROUP BY asset_report.id, asset_report.report_date;

    	INSERT INTO acc_trans (trans_id, chart_id, transdate, approved, amount)
    	SELECT gl.id, a.exp_account_id, r.report_date, true, sum(amount) * -1
    	FROM asset_report r
    	JOIN asset_report_line l ON (r.id = l.report_id)
    	JOIN asset_item a ON (l.asset_id = a.id)
    	JOIN gl ON (gl.description = 'Asset Report ' || l.report_id)
    	WHERE r.id = in_report_id
    	GROUP BY gl.id, r.report_date, a.exp_account_id;

    	INSERT INTO acc_trans (trans_id, chart_id, transdate, approved, amount)
    	SELECT gl.id, a.dep_account_id, r.report_date, true, sum(amount)
    	FROM asset_report r
    	JOIN asset_report_line l ON (r.id = l.report_id)
    	JOIN asset_item a ON (l.asset_id = a.id)
    	JOIN gl ON (gl.description = 'Asset Report ' || l.report_id)
    	WHERE r.id = in_report_id
    	GROUP BY gl.id, a.dep_account_id, r.report_date, a.tag, a.description;

    	RETURN in_report_id;
    END;

## Function: asset_report__get(in_id integer)

### Returns: asset_report

### Language: SQL

Returns the asset_report line identified by id.

    select * from asset_report where id = $1;

## Function: asset_report__get_disposal(in_id integer)

### Returns: SET OF asset_disposal_report_line

### Language: SQL

Returns a set of lines of disposed assets in a disposal report, specified by the report id.

       SELECT ai.id, ai.tag, ai.description, ai.start_depreciation, r.report_date,
              dm.short_label, ai.purchase_value,
              sum (CASE WHEN pr.report_class in (1,3) THEN prl.amount ELSE 0 END)
              as accum_dep,
              l.amount,
              ai.purchase_value - sum(CASE WHEN pr.report_class in (1,3)
                                           THEN prl.amount
                                           ELSE 0
                                       END) as adjusted_basis,
              l.amount - ai.purchase_value + sum(CASE WHEN pr.report_class in (1,3)
                                                      THEN prl.amount
                                                      ELSE 0
                                                  END) as gain_loss
         FROM asset_item ai
         JOIN asset_report_line l   ON (l.report_id = $1 AND ai.id = l.asset_id)
         JOIN asset_report r        ON (l.report_id = r.id)
    LEFT JOIN asset_rl_to_disposal_method adm
                                 USING (report_id, asset_id)
         JOIN asset_disposal_method dm
                                    ON (adm.disposal_method_id = dm.id)
    LEFT JOIN asset_report_line prl ON (prl.report_id <> $1
                                       AND ai.id = prl.asset_id)
    LEFT JOIN asset_report pr       ON (prl.report_id = pr.id)
     GROUP BY ai.id, ai.tag, ai.description, ai.start_depreciation, r.report_date,
              ai.purchase_value, l.amount, dm.short_label
     ORDER BY ai.id, ai.tag;

## Function: asset_report__get_disposal_methods()

### Returns: SET OF asset_disposal_method

### Language: SQL

Returns a list of asset_disposal_method items ordered by label.

    SELECT * FROM asset_disposal_method order by label;

## Function: asset_report__get_expense_accts()

### Returns: SET OF account

### Language: SQL

Lists all asset expense reports.

        SELECT * FROM account__get_by_link_desc('asset_expense');

## Function: asset_report__get_gain_accts()

### Returns: SET OF account

### Language: SQL

Returns a list of gain accounts for asset depreciation and disposal reports.

        SELECT * FROM account__get_by_link_desc('asset_gain');

## Function: asset_report__get_lines(in_id integer)

### Returns: SET OF asset_report_line_result

### Language: SQL

Returns the lines of an asset depreciation report.

       select ai.tag, coalesce(ai.start_depreciation, ai.purchase_date), ai.purchase_value, m.short_name,
              ai.usable_life,
              ai.purchase_value - ai.salvage_value, max(pr.report_date),
              sum(case when pr.report_date < r.report_date then prl.amount
                       else 0
                    end),
              rl.amount,
              sum (case when extract(year from pr.report_date)
                             = extract(year from r.report_date)
                             AND pr.report_date < r.report_date
                        then prl.amount
                        else 0
                    end),
              sum(prl.amount),
              ai.description, ai.purchase_date
         FROM asset_item ai
         JOIN asset_class c ON (ai.asset_class_id = c.id)
         JOIN asset_dep_method m ON (c.method = m.id)
         JOIN asset_report_line rl ON (rl.asset_id = ai.id)
         JOIN asset_report r ON (rl.report_id = r.id)
    LEFT JOIN asset_report_line prl ON (prl.asset_id = ai.id)
    LEFT JOIN asset_report pr ON (prl.report_id = pr.id)
        WHERE rl.report_id = $1
     GROUP BY ai.tag, ai.start_depreciation, ai.purchase_value, m.short_name,
              ai.usable_life, ai.salvage_value, r.report_date, rl.amount,
              ai.description, ai.purchase_date;

## Function: asset_report__get_loss_accts()

### Returns: SET OF account

### Language: SQL

Returns a list of loss accounts for asset depreciation and disposal reports.

        SELECT * FROM account__get_by_link_desc('asset_loss');

## Function: asset_report__import(in_obsolete_other text, in_accum_dep text, in_dep_report_id numeric, in_invoice_id numeric, in_asset_class_id numeric, in_exp_account_id date, in_dep_account_id date, in_asset_account_id integer, in_department_id integer, in_location_id integer, in_start_depreciation integer, in_purchase_date integer, in_usable_life integer, in_salvage_value integer, in_purchase_value integer, in_tag numeric, in_description boolean)

### Returns: boolean

### Language: SQL

Imports an asset with the supplied information. If in_obsolete_other is false, this creates a new depreciable asset. If it is true, it sets up the other asset as obsolete. This is the way partial disposal reports are handled.

    SET CONSTRAINTS asset_item_obsolete_by_fkey DEFERRED;
    -- This fails a deferrable fkey constraint but avoids a partial unique index
    -- so in this case, the foreign key is deferred for the duration of this
    -- specific stored proc call.

    UPDATE asset_item
       SET obsolete_by = -1
     WHERE tag = $2 and $17 is true;

    INSERT
      INTO asset_report_line
           (report_id, asset_id, amount, department_id, warehouse_id)
    select $15, id, $16, department_id, location_id
      from asset__save
           (NULL, $13, $1, $2, $6, $3, $5, coalesce($4, 0), $7, $8, $9, $14, $10, $11, $12);

    UPDATE asset_item
       SET obsolete_by = currval('asset_item_id_seq')
     WHERE obsolete_by = -1;

    -- enforce fkeys now and raise exception if fail
    SET CONSTRAINTS asset_item_obsolete_by_fkey IMMEDIATE;
    SELECT true;

## Function: asset_report__record_approve(in_id integer)

### Returns: asset_report

### Language: SQL

Marks the asset_report record approved. Not generally recommended to call directly.

    UPDATE asset_report
       set approved_by = person__get_my_entity_id(),
           approved_at = now()
     where id = $1;

    select * from asset_report where id = $1;

## Function: asset_report__save(in_submit integer, in_asset_class date, in_report_class integer, in_report_date integer, in_id boolean)

### Returns: asset_report

### Language: PLPGSQL

Creates or updates an asset report with the information presented. Note that approval values are not set here, and that one cannot unsubmit a report though this function.

    DECLARE
    	ret_val asset_report;
    	item record;
    	method_text text;
    BEGIN
    	DELETE FROM asset_report_line where report_id = in_id;

    	UPDATE asset_report
    	set asset_class = in_asset_class,
    		report_class = in_report_class,
    		report_date = in_report_date,
    		submitted = (in_submit or submitted)
    	WHERE id = in_id;

    	IF FOUND THEN
    		SELECT * INTO ret_val FROM asset_report WHERE id = in_id;
    	ELSE
    		INSERT INTO asset_report(report_class, asset_class, report_date,
    			submitted)
    		values (in_report_class, in_asset_class, in_report_date,
    			coalesce(in_submit, true));

    		SELECT * INTO ret_val FROM asset_report
    		WHERE id = currval('asset_report_id_seq');

    	END IF;
            RETURN ret_val;

    END;

## Function: asset_report__search(in_entered_by date, in_approved date, in_asset_class integer, in_end_date boolean, in_start_date integer)

### Returns: SET OF asset_report_result

### Language: SQL

Searches for asset reports. Nulls match all rows. Approved, asset class, and entered_by are exact matches. Start_date and end_date define the beginning and end of the search date.

      SELECT r.id, r.report_date, r.gl_id, r.asset_class, r.report_class,
             r.entered_by, r.approved_by, r.entered_at, r.approved_at,
             r.depreciated_qty, r.dont_approve, r.submitted, sum(l.amount)
        FROM asset_report r
        JOIN asset_report_line l ON (l.report_id = r.id)
       where ($1 is null or $1 <= report_date)
             and ($2 is null or $2 >= report_date)
             and ($3 is null or $3 = asset_class)
             and ($4 is null
                  or ($4 is true and approved_by is not null)
                  or ($4 is false and approved_by is null))
             and ($5 is null or $5 = entered_by)
    GROUP BY r.id, r.report_date, r.gl_id, r.asset_class, r.report_class,
             r.entered_by, r.approved_by, r.entered_at, r.approved_at,
             r.depreciated_qty, r.dont_approve, r.submitted;

## Function: asset_report_partial_disposal_details(in_id integer)

### Returns: SET OF partial_disposal_line

### Language: SQL

Returns the partial disposal details for a partial disposal report.

    SELECT ai.id, ai.tag, ai.start_depreciation, ai.purchase_value, ai.description,
           ar.report_date, arld.percent_disposed,
           (arld.percent_disposed / 100) * ai.purchase_value,
           100 - arld.percent_disposed,
           ((100 - arld.percent_disposed)/100) * ai.purchase_value
      FROM asset_item ai
      JOIN asset_report_line l ON (ai.id = l.asset_id)
      JOIN asset_report ar ON (ar.id = l.report_id)
      JOIN asset_rl_to_disposal_method arld
           ON  ((arld.report_id, arld.asset_id) = (l.report_id, l.asset_id))
     WHERE ar.id = $1;

## Function: avgcost(integer)

### Returns: double precision

### Language: PLPGSQL

    DECLARE

    v_cost float;
    v_qty float;
    v_parts_id alias for $1;

    BEGIN

      SELECT INTO v_cost, v_qty SUM(i.sellprice * i.qty), SUM(i.qty)
      FROM invoice i
      JOIN ap a ON (a.id = i.trans_id)
      WHERE i.parts_id = v_parts_id;

      IF v_cost IS NULL THEN
        v_cost := 0;
      END IF;

      IF NOT v_qty IS NULL THEN
        IF v_qty = 0 THEN
          v_cost := 0;
        ELSE
          v_cost := v_cost/v_qty;
        END IF;
      END IF;

    RETURN v_cost;
    END;

## Function: batch_create(in_batch_date text, in_batch_class text, in_description text, in_batch_number date)

### Returns: integer

### Language: PLPGSQL

Inserts the batch into the table.

    BEGIN
    	INSERT INTO
    		batch (batch_class_id, default_date, description, control_code,
    			created_by)
    	VALUES ((SELECT id FROM batch_class WHERE class = in_batch_class),
    		in_batch_date, in_description, in_batch_number,
    			(select entity_id FROM users WHERE username = session_user));

    	return currval('batch_id_seq');
    END;

## Function: batch_delete(in_batch_id integer)

### Returns: integer

### Language: PLPGSQL

If the batch is found and unapproved, deletes it and returns 1. Otherwise raises an exception.

    DECLARE
    	t_transaction_ids int[];
    BEGIN
    	-- Adjust AR/AP tables for payment and payment reversal vouchers
    	-- voucher_id is only set in acc_trans on payment/receipt vouchers and
    	-- their reversals. -CT
            perform * from batch where id = in_batch_id and approved_on IS NULL;
            IF NOT FOUND THEN
                RAISE EXCEPTION 'Batch not found';
            END IF;
    	update ar set paid = amount +
    		(select sum(amount) from acc_trans
    		join chart ON (acc_trans.chart_id = chart.id)
    		where link = 'AR' AND trans_id = ar.id
    			AND (voucher_id IS NULL OR voucher_id NOT IN
    				(select id from voucher
    				WHERE batch_id = in_batch_id)))
    	where id in (select trans_id from acc_trans where voucher_id IN
    		(select id from voucher where batch_id = in_batch_id));

    	update ap set paid = amount - (select sum(amount) from acc_trans
    		join chart ON (acc_trans.chart_id = chart.id)
    		where link = 'AP' AND trans_id = ap.id
    			AND (voucher_id IS NULL OR voucher_id NOT IN
    				(select id from voucher
    				WHERE batch_id = in_batch_id)))
    	where id in (select trans_id from acc_trans where voucher_id IN
    		(select id from voucher where batch_id = in_batch_id));

            DELETE FROM ac_tax_form WHERE entry_id IN
                   (select entry_id from acc_trans where voucher_id in
                           (select id from voucher where batch_id = in_batch_id)
                   );

    	DELETE FROM acc_trans WHERE voucher_id IN
    		(select id FROM voucher where batch_id = in_batch_id);

    	-- The rest of this function involves the deletion of actual
    	-- transactions, vouchers, and batches, and jobs which are in progress.
    	-- -CT
    	SELECT as_array(trans_id) INTO t_transaction_ids
    	FROM voucher WHERE batch_id = in_batch_id AND batch_class IN (1, 2, 5);

            DELETE FROM ac_tax_form WHERE entry_id in
                   (select entry_id from acc_trans
                     where trans_id = any(t_transaction_ids));

    	DELETE FROM acc_trans WHERE trans_id = ANY(t_transaction_ids);
    	DELETE FROM ap WHERE id = ANY(t_transaction_ids);
    	DELETE FROM gl WHERE id = ANY(t_transaction_ids);
    	DELETE FROM voucher WHERE batch_id = in_batch_id;
    	DELETE FROM payments_queue WHERE batch_id = in_batch_id;
    	DELETE FROM pending_job WHERE batch_id = in_batch_id;
    	DELETE FROM batch WHERE id = in_batch_id;
    	DELETE FROM transactions WHERE id = ANY(t_transaction_ids);

    	RETURN 1;
    END;

## Function: batch_get_class_id(in_type text)

### Returns: integer

### Language: SQL

returns the batch class id associated with the in_type label provided.

    SELECT id FROM batch_class WHERE class = $1;

## Function: batch_get_users()

### Returns: SET OF users

### Language: PLPGSQL

Returns a sim[ple set of user objects. This should be renamed so that it is more obvious it is a general purpose function.

    DECLARE out_record users%ROWTYPE;
    BEGIN
    	FOR out_record IN
    		SELECT * from users WHERE entity_id IN (select created_by from batch)
    	LOOP
    		RETURN NEXT out_record;
    	END LOOP;
    END;

## Function: batch_list_classes()

### Returns: SET OF batch_class

### Language: PLPGSQL

Returns a list of all batch classes.

    DECLARE out_val record;
    BEGIN
    	FOR out_val IN select * from batch_class order by id
     	LOOP
    		return next out_val;
    	END LOOP;
    END;

## Function: batch_post(in_batch_id integer)

### Returns: date

### Language: PLPGSQL

Posts the specified batch to the books. Only posted batches should show up on standard financial reports.

    BEGIN
    	UPDATE ar SET approved = true
    	WHERE id IN (select trans_id FROM voucher
    		WHERE batch_id = in_batch_id
    		AND batch_class = 2);

    	UPDATE ap SET approved = true
    	WHERE id IN (select trans_id FROM voucher
    		WHERE batch_id = in_batch_id
    		AND batch_class = 1);

    	UPDATE gl SET approved = true
    	WHERE id IN (select trans_id FROM voucher
    		WHERE batch_id = in_batch_id
    		AND batch_class = 5);

    	UPDATE acc_trans SET approved = true
    	WHERE voucher_id IN (select id FROM voucher
    		WHERE batch_id = in_batch_id
    		AND batch_class IN (3, 4, 6, 7));

    	UPDATE batch
    	SET approved_on = now(),
    		approved_by = (select entity_id FROM users
    			WHERE username = SESSION_USER)
    	WHERE id = in_batch_id;

    	RETURN now()::date;
    END;

## Function: batch_search(in_approved integer, in_amount_lt text, in_amount_gt integer, in_date_to date, in_date_from date, in_created_by_eid numeric, in_description numeric, in_class_id boolean)

### Returns: SET OF batch_list_item

### Language: PLPGSQL

Returns a list of batches and amounts processed on the batch. Nulls match all values. in_date_from and in_date_to specify date ranges. in_description is a partial match. All other criteria are exact matches.

    DECLARE out_value batch_list_item;
    BEGIN
    	FOR out_value IN
    		SELECT b.id, c.class, b.control_code, b.description, u.username,
    			b.created_on, b.default_date,
    			sum(
    				CASE WHEN vc.id = 5 AND al.amount < 0 -- GL
    				     THEN al.amount
    				     WHEN vc.id  = 1
    				     THEN ap.amount
    				     WHEN vc.id = 2
                                         THEN ap.amount
    				     ELSE 0
                                    END) AS transaction_total,
    			sum(
    				CASE WHEN alc.link = 'AR' AND vc.id IN (6, 7)
    				     THEN al.amount
    				     WHEN alc.link = 'AP' AND vc.id IN (3, 4)
    				     THEN al.amount * -1
    				     ELSE 0
    				END
    			   ) AS payment_total
    		FROM batch b
    		JOIN batch_class c ON (b.batch_class_id = c.id)
    		LEFT JOIN users u ON (u.entity_id = b.created_by)
    		LEFT JOIN voucher v ON (v.batch_id = b.id)
    		LEFT JOIN batch_class vc ON (v.batch_class = vc.id)
    		LEFT JOIN ar ON (vc.id = 2 AND v.trans_id = ar.id)
    		LEFT JOIN ap ON (vc.id = 1 AND v.trans_id = ap.id)
    		LEFT JOIN acc_trans al ON
    			((vc.id = 5 AND v.trans_id = al.trans_id) OR
    				(vc.id IN (3, 4, 6, 7)
    					AND al.voucher_id = v.id))
    		LEFT JOIN chart alc ON (al.chart_id = alc.id)
    		WHERE (c.id = in_class_id OR in_class_id IS NULL) AND
    			(b.description LIKE
    				'%' || in_description || '%' OR
    				in_description IS NULL) AND
    			(in_created_by_eid = b.created_by OR
    				in_created_by_eid IS NULL) AND
    			((in_approved = false OR in_approved IS NULL AND
    				approved_on IS NULL) OR
    				(in_approved = true AND approved_on IS NOT NULL)
    			)
    			and (in_date_from IS NULL
    				or b.default_date >= in_date_from)
    			and (in_date_to IS NULL
    				or b.default_date <= in_date_to)
    		GROUP BY b.id, c.class, b.description, u.username, b.created_on,
    			b.control_code, b.default_date
    		HAVING
    			(in_amount_gt IS NULL OR
    			sum(coalesce(ar.amount - ar.paid, ap.amount - ap.paid,
    				al.amount))
    			>= in_amount_gt)
    			AND
    			(in_amount_lt IS NULL OR
    			sum(coalesce(ar.amount - ar.paid, ap.amount - ap.paid,
    				al.amount))
    			<= in_amount_lt)
    		ORDER BY b.control_code, b.description

    	LOOP
    		RETURN NEXT out_value;
    	END LOOP;
    END;

## Function: batch_search_empty(in_approved integer, in_amount_lt text, in_amount_gt integer, in_created_by_eid numeric, in_description numeric, in_class_id boolean)

### Returns: SET OF batch_list_item

### Language: PLPGSQL

This is a full search for the batches, listing them by amount processed. in_amount_gt and in_amount_lt provide a range to search for. in_description is a partial match field. Other fields are exact matches. NULLs match all values.

    DECLARE out_value batch_list_item;
    BEGIN
    	FOR out_value IN
                   SELECT b.id, c.class, b.control_code, b.description, u.username,
                            b.created_on, b.default_date, 0, 0
                    FROM batch b
                    JOIN batch_class c ON (b.batch_class_id = c.id)
                    JOIN users u ON (u.entity_id = b.created_by)
                    LEFT JOIN voucher v ON (v.batch_id = b.id)
                   where v.id is null
                         and(u.entity_id = in_created_by_eid
                         or in_created_by_eid is null) and
                         (in_description is null or b.description
                         like '%'  || in_description || '%') and
                         (in_class_id is null or c.id = in_class_id)
                GROUP BY b.id, c.class, b.description, u.username, b.created_on,
                         b.control_code, b.default_date
                ORDER BY b.control_code, b.description

    	LOOP
    		RETURN NEXT out_value;
    	END LOOP;
    END;

## Function: batch_search_mini(in_approved integer, in_created_by_eid text, in_description integer, in_class_id boolean)

### Returns: SET OF batch_list_item

### Language: PLPGSQL

This performs a simple search of open batches created by the entity_id in question. This is used to pull up batches that were currently used so that they can be picked up and more vouchers added. NULLs match all values. in_description is a partial match All other inouts are exact matches.

    DECLARE out_value batch_list_item;
    BEGIN
    	FOR out_value IN
    		SELECT b.id, c.class, b.control_code, b.description, u.username,
    			b.created_on, b.default_date, NULL
    		FROM batch b
    		JOIN batch_class c ON (b.batch_class_id = c.id)
    		LEFT JOIN users u ON (u.entity_id = b.created_by)
    		WHERE (c.id = in_class_id OR in_class_id IS NULL) AND
    			(b.description LIKE
    				'%' || in_description || '%' OR
    				in_description IS NULL) AND
    			(in_created_by_eid = b.created_by OR
    				in_created_by_eid IS NULL) AND
    			((in_approved = false OR in_approved IS NULL AND
    				approved_on IS NULL) OR
    				(in_approved = true AND approved_on IS NOT NULL)
    			)
    		GROUP BY b.id, c.class, b.description, u.username, b.created_on,
    			b.control_code, b.default_date
    	LOOP
    		RETURN NEXT out_value;
    	END LOOP;
    END;

## Function: business_type__list()

### Returns: SET OF business

### Language: PLPGSQL

Returns a list of all business types. Ordered by description by default.

    DECLARE out_row business%ROWTYPE;
    BEGIN
    	FOR out_row IN SELECT * FROM business ORDER BY description LOOP
    		RETURN NEXT out_row;
    	END LOOP;
    END;

## Function: chart_get_ar_ap(in_account_class integer)

### Returns: SET OF chart

### Language: PLPGSQL

This function returns the cash account acording with in_account_class which must be 1 or 2. If in_account_class is 1 then it returns a list of AP accounts, and if in_account_class is 2, then a list of AR accounts.

    DECLARE out_row chart%ROWTYPE;
    BEGIN
    	IF in_account_class NOT IN (1, 2) THEN
    		RAISE EXCEPTION 'Bad Account Type';
    	END IF;
           FOR out_row IN
                   SELECT * FROM chart
                   WHERE link = CASE WHEN in_account_class = 1 THEN 'AP'
                                   WHEN in_account_class = 2 THEN 'AR'
                                   END
                   ORDER BY accno
           LOOP
                   RETURN NEXT out_row;
           END LOOP;
    END;

## Function: chart_list_all()

### Returns: SET OF chart

### Language: PLPGSQL

Generates a list of chart view entries.

    DECLARE out_row chart%ROWTYPE;
    BEGIN
    	FOR out_row IN
    		SELECT * FROM chart ORDER BY accno
    	LOOP
    		RETURN next out_row;
    	END LOOP;
    END;

## Function: chart_list_cash(in_account_class integer)

### Returns: SET OF chart

### Language: PLPGSQL

This function returns the overpayment accounts acording with in_account_class which must be 1 or 2. If in_account_class is 1 it returns a list of AP cash accounts and if 2, AR cash accounts.

     DECLARE resultrow record;
             link_string text;
     BEGIN
             IF in_account_class = 1 THEN
                link_string := '%AP_paid%';
             ELSE
                link_string := '%AR_paid%';
             END IF;

             FOR resultrow IN
               SELECT *  FROM chart
               WHERE link LIKE link_string
               ORDER BY accno
               LOOP
               return next resultrow;
             END LOOP;
     END;

## Function: chart_list_discount(in_account_class integer)

### Returns: SET OF chart

### Language: PLPGSQL

This function returns the discount accounts acording with in_account_class which must be 1 or 2. If in_account_class is 1, returns AP discount accounts, if 2, AR discount accounts.

    DECLARE resultrow record;
            link_string text;
    BEGIN
            IF in_account_class = 1 THEN
               link_string := '%AP_discount%';
            ELSE
               link_string := '%AR_discount%';
            END IF;

            FOR resultrow IN
              SELECT *  FROM chart
              WHERE link LIKE link_string
              ORDER BY accno
              LOOP
              return next resultrow;
            END LOOP;
    END;

## Function: chart_list_overpayment(in_account_class integer)

### Returns: SET OF chart

### Language: PLPGSQL

Returns a list of AP_overpayment accounts if in_account_class is 1 Otherwise it returns a list of AR_overpayment accounts.

    DECLARE resultrow record;
            link_string text;
    BEGIN
            IF in_account_class = 1 THEN
               link_string := '%AP_overpayment%';
            ELSE
               link_string := '%AR_overpayment%';
            END IF;

            FOR resultrow IN
              SELECT *  FROM chart
              WHERE link LIKE link_string
              ORDER BY accno
              LOOP
              return next resultrow;
            END LOOP;
    END;

## Function: chart_list_search(in_link_desc text, in_search text)

### Returns: SET OF account

### Language: PLPGSQL

This returns a list of account entries where the description or account number begins with in_search. If in_link_desc is provided, the list is further filtered by which accounts are set to an account_link.description equal to that provided.

    DECLARE out_row account%ROWTYPE;
    BEGIN
    	FOR out_row IN
    		SELECT * FROM account
                     WHERE (accno ~* ('^'||in_search)
                           OR description ~* ('^'||in_search))
                           AND (in_link_desc IS NULL
                               or id in
                              (select account_id from account_link
                                where description = in_link_desc))
                  ORDER BY accno
    	LOOP
    		RETURN next out_row;
    	END LOOP;
    END;

## Function: check_department()

### Returns: trigger

### Language: PLPGSQL

    declare
      dpt_id int;

    begin

      if new.department_id = 0 then
        delete from dpt_trans where trans_id = new.id;
        return NULL;
      end if;

      select into dpt_id trans_id from dpt_trans where trans_id = new.id;

      if dpt_id > 0 then
        update dpt_trans set department_id = new.department_id where trans_id = dpt_id;
      else
        insert into dpt_trans (trans_id, department_id) values (new.id, new.department_id);
      end if;
    return NULL;

    end;

## Function: check_expiration()

### Returns: boolean

### Language: PLPGSQL

This checks whether the user needs to be notified of a pending expiration of his/her password. Returns true if needed, false if not. The function also records the next time when the notification will again need to be displayed.

    DECLARE test_result BOOL;
    	expires_in interval;
    	notify_again interval;
    BEGIN
    	expires_in := user__check_my_expiration();

    	SELECT expires_in < notify_password INTO test_result
    	FROM users WHERE username = SESSION_USER;

    	IF test_result THEN
    		IF expires_in < '1 week' THEN
    			notify_again := '1 hour';
    		ELSE
    			notify_again := '1 day';
    		END IF;

    		UPDATE users
    		SET notify_password = expires_in - notify_again
    		WHERE username = SESSION_USER;
    	END IF;
    	RETURN test_result;
    END;

## Function: company__delete_contact(in_contact integer, in_contact_class_id integer, in_company_id text)

### Returns: boolean

### Language: PLPGSQL

Returns true if at least one record was deleted. False if no records were affected.

    BEGIN

    DELETE FROM company_to_contact
     WHERE company_id = in_company_id and contact_class_id = in_contact_class_id
           and contact= in_contact;
    RETURN FOUND;

    END;

## Function: company__delete_location(in_location_class integer, in_location_id integer, in_company_id integer)

### Returns: boolean

### Language: PLPGSQL

Deletes the record identified. Returns true if successful, false if no record found.

    BEGIN

    DELETE FROM eca_to_location
     WHERE company_id = in_company_id AND location_id = in_location_id
           AND location_class = in_location_class;

    RETURN FOUND;

    END;

## Function: company__get_all_accounts(in_entity_class integer, in_entity_id integer)

### Returns: SET OF entity_credit_account

### Language: SQL

Returns a list of all entity credit accounts attached to that entity.

        SELECT *
          FROM entity_credit_account
         WHERE entity_id = $1
           AND entity_class = $2;

## Function: company__list_bank_account(in_entity_id integer)

### Returns: SET OF entity_bank_account

### Language: PLPGSQL

Lists all bank accounts for the entity.

    DECLARE out_row entity_bank_account%ROWTYPE;
    BEGIN
    	FOR out_row IN
    		SELECT * from entity_bank_account where entity_id = in_entity_id
    	LOOP
    		RETURN NEXT out_row;
    	END LOOP;
    END;

## Function: company__list_contacts(in_entity_id integer)

### Returns: SET OF contact_list

### Language: PLPGSQL

Lists all contact info for the entity.

    DECLARE out_row contact_list;
    BEGIN
    	FOR out_row IN
    		SELECT cl.class, cl.id, c.description, c.contact
    		FROM company_to_contact c
    		JOIN contact_class cl ON (c.contact_class_id = cl.id)
    		WHERE company_id =
    			(select id FROM company
    			WHERE entity_id = in_entity_id)
    	LOOP
    		return next out_row;
    	END LOOP;
    END;

## Function: company__list_locations(in_entity_id integer)

### Returns: SET OF location_result

### Language: PLPGSQL

Lists all locations for an entity.

    DECLARE out_row RECORD;
    BEGIN
    	FOR out_row IN
    		SELECT l.id, l.line_one, l.line_two, l.line_three, l.city,
    			l.state, l.mail_code, c.id, c.name, lc.id, lc.class
    		FROM location l
    		JOIN company_to_location ctl ON (ctl.location_id = l.id)
    		JOIN location_class lc ON (ctl.location_class = lc.id)
    		JOIN country c ON (c.id = l.country_id)
    		WHERE ctl.company_id = (select id from company where entity_id = in_entity_id)
    		ORDER BY lc.id, l.id, c.name
    	LOOP
    		RETURN NEXT out_row;
    	END LOOP;
    END;

## Function: company__list_notes(in_entity_id integer)

### Returns: SET OF entity_note

### Language: PLPGSQL

Returns a set of notes (including content) attached to the entity.

    DECLARE out_row record;
    BEGIN
    	FOR out_row IN
    		SELECT *
    		FROM entity_note
    		WHERE ref_key = in_entity_id
    		ORDER BY created
    	LOOP
    		RETURN NEXT out_row;
    	END LOOP;
    END;

## Function: company__location_save(in_created integer, in_country_code integer, in_mail_code integer, in_state text, in_city text, in_line_two text, in_line_one text, in_location_class text, in_location_id integer, in_entity_id date)

### Returns: integer

### Language: PLPGSQL

Saves a location to a company. Returns the location id.

        BEGIN
        return _entity_location_save(
            in_entity_id, in_location_id,
            in_location_class, in_line_one, in_line_two,
            '', in_city , in_state, in_mail_code, in_country_code);
        END;

## Function: company__next_id()

### Returns: bigint

### Language: SQL

        select nextval('company_id_seq');

## Function: company__save_contact(in_contact integer, in_description integer, in_contact_class text, in_entity_id text)

### Returns: integer

### Language: PLPGSQL

Saves company contact information. The return value is meaningless.

    DECLARE out_id int;
    BEGIN
    	INSERT INTO company_to_contact(company_id, contact_class_id,
    		description, contact)
    	SELECT id, in_contact_class, in_description, in_contact FROM company
    	WHERE entity_id = in_entity_id;

    	RETURN 1;
    END;

## Function: company__search(in_control_code integer, in_legal_name text, in_business_id text[], in_date_to text, in_date_from text, in_country text, in_mail_code text, in_state text, in_city text, in_address date, in_meta_number date, in_contact_info integer, in_contact text, in_account_class text)

### Returns: SET OF company_search_result

### Language: PLPGSQL

    DECLARE
    	out_row company_search_result;
    	loop_count int;
    	t_contact_info text[];
    BEGIN
    	t_contact_info = in_contact_info;

    	FOR out_row IN
    		SELECT e.id, e.control_code, c.id, ec.id, ec.meta_number,
    			ec.description, ec.entity_class,
    			c.legal_name, c.sic_code, b.description , ec.curr::text
    		FROM (select * from entity where in_control_code = control_code
                          union
                          select * from entity where in_control_code is null) e
    		JOIN (SELECT * FROM company
                           WHERE legal_name ilike  '%' || in_legal_name || '%'
                          UNION ALL
                          SELECT * FROM company
                           WHERE in_legal_name IS NULL) c ON (e.id = c.entity_id)
    		LEFT JOIN (SELECT * FROM entity_credit_account
                           WHERE meta_number = in_meta_number
                          UNION ALL
                          SELECT * from entity_credit_account
                           WHERE in_meta_number IS NULL) ec ON (ec.entity_id = e.id)
    		LEFT JOIN business b ON (ec.business_id = b.id)
    		WHERE ec.entity_class = in_account_class
    			AND (c.id IN (select company_id FROM company_to_contact
    				WHERE contact ILIKE ALL(t_contact_info))
    				OR '' ILIKE ALL(t_contact_info))

    			AND (c.legal_name ilike '%' || in_legal_name || '%'
    				OR in_legal_name IS NULL)
    			AND ((in_address IS NULL AND in_city IS NULL
    					AND in_state IS NULL
    					AND in_country IS NULL)
    				OR (c.id IN
    				(select company_id FROM company_to_location
    				WHERE location_id IN
    					(SELECT id FROM location
    					WHERE line_one
    						ilike '%' ||
    							coalesce(in_address, '')
    							|| '%'
    						AND city ILIKE
    							'%' ||
    							coalesce(in_city, '')
    							|| '%'
    						AND state ILIKE
    							'%' ||
    							coalesce(in_state, '')
    							|| '%'
    						AND mail_code ILIKE
    							'%' ||
    							coalesce(in_mail_code,
    								'')
    							|| '%'
    						AND country_id IN
    							(SELECT id FROM country
    							WHERE name ILIKE '%' ||
    								in_country ||'%'
    								OR short_name
    								ilike
    								in_country)))))
    			AND (ec.business_id =
    				coalesce(in_business_id, ec.business_id)
    				OR (ec.business_id IS NULL
    					AND in_business_id IS NULL))
    			AND (ec.startdate <= coalesce(in_date_to,
    						ec.startdate)
    				OR (ec.startdate IS NULL))
    			AND (ec.enddate >= coalesce(in_date_from, ec.enddate)
    				OR (ec.enddate IS NULL))
    	LOOP
    		RETURN NEXT out_row;
    	END LOOP;
    END;

## Function: company_get_billing_info(in_id integer)

### Returns: company_billing_info

### Language: PLPGSQL

Returns billing information (billing name and address) for a given credit account.

    DECLARE out_var company_billing_info;
    	t_id INT;
    BEGIN
    	select coalesce(eca.pay_to_name, c.legal_name), eca.meta_number,
    		e.control_code, c.tax_id, a.line_one, a.line_two, a.line_three,
    		a.city, a.state, a.mail_code, cc.name
    	into out_var
    	FROM company c
    	JOIN entity e ON (c.entity_id = e.id)
    	JOIN entity_credit_account eca ON (eca.entity_id = e.id)
    	LEFT JOIN eca_to_location cl ON (eca.id = cl.credit_id)
    	LEFT JOIN location a ON (a.id = cl.location_id)
    	LEFT JOIN country cc ON (cc.id = a.country_id)
    	WHERE eca.id = in_id AND (location_class = 1 or location_class is null);

    	RETURN out_var;
    END;

## Function: company_retrieve(in_entity_id integer)

### Returns: company

### Language: PLPGSQL

Returns all attributes for the company attached to the entity.

    DECLARE t_company company;
    BEGIN
    	SELECT * INTO t_company FROM company WHERE entity_id = in_entity_id;
    	RETURN t_company;
    END;

## Function: company_save(in_country_id_t integer, in_sic_code text, in_entity_id integer, in_tax_id text, in_name text, in_entity_class integer, in_control_code text, in_id integer)

### Returns: integer

### Language: PLPGSQL

Saves a company. Returns the id number of the record stored.

    DECLARE t_entity_id INT;
    	t_company_id INT;
    	t_control_code TEXT;
    BEGIN
    	t_company_id := in_id;

    	IF in_control_code IS NULL THEN
    		t_control_code := setting_increment('company_control');
    	ELSE
    		t_control_code := in_control_code;
    	END IF;

    	UPDATE entity
    	SET name = in_name,
    		entity_class = in_entity_class,
    		control_code = in_control_code
    	WHERE id = in_entity_id;

    	IF FOUND THEN
    		t_entity_id = in_entity_id;
    	ELSE
    		INSERT INTO entity (name, entity_class, control_code,country_id)
    		VALUES (in_name, in_entity_class, t_control_code,in_country_id_t);
    		t_entity_id := currval('entity_id_seq');
    	END IF;

    	UPDATE company
    	SET legal_name = in_name,
    		tax_id = in_tax_id,
    		sic_code = in_sic_code
    	WHERE id = t_company_id;

    	IF NOT FOUND THEN
    		INSERT INTO company(entity_id, legal_name, tax_id, sic_code)
    		VALUES (t_entity_id, in_name, in_tax_id, in_sic_code);

    	END IF;
    	RETURN t_entity_id;
    END;

## Function: compound_array(anyarray)

### Returns: anyarray

### Language: INTERNAL

Returns an n dimensional array. Example: SELECT as_array(ARRAY[id::text, class]) from contact_class

    aggregate_dummy

## Function: concat(tsvector, tsvector)

### Returns: tsvector

### Language: INTERNAL

    tsvector_concat

## Function: concat_colon(text)

### Returns: text

### Language: INTERNAL

This is a sumple aggregate to return values from the database in a colon-separated list. Other programs probably should not rely on this since it is primarily included for the chart view.

    aggregate_dummy

## Function: concat_colon(text, text)

### Returns: text

### Language: SQL

This function takes two arguments and creates a list out of them. It's useful as an aggregate base (see aggregate concat_colon). However this is a temporary function only and should not be relied upon.

    select CASE WHEN $1 IS NULL THEN $2 ELSE $1 || ':' || $2 END;

## Function: connectby(text, text, text, text, integer)

### Returns: SET OF record

### Language: C

    connectby_text

## Function: connectby(text, text, text, text, integer, text)

### Returns: SET OF record

### Language: C

    connectby_text

## Function: connectby(text, text, text, text, text, integer)

### Returns: SET OF record

### Language: C

    connectby_text_serial

## Function: connectby(text, text, text, text, text, integer, text)

### Returns: SET OF record

### Language: C

    connectby_text_serial

## Function: cr_coa_to_account_save(in_description text, in_accno text)

### Returns: void

### Language: PLPGSQL

Provides default rules for setting reconciliation labels. Currently saves a label of accno ||'--' || description.

        DECLARE
           v_chart_id int;
        BEGIN
            -- Check for existence of the account already
            PERFORM * FROM cr_coa_to_account WHERE account = in_accno;

            IF NOT FOUND THEN
               -- This is a new account. Insert the relevant data.
               SELECT id INTO v_chart_id FROM chart WHERE accno = in_accno;
               INSERT INTO cr_coa_to_account (chart_id, account) VALUES (v_chart_id, in_accno||'--'||in_description);
            END IF;
            -- Already found, no need to do anything. =)
        END;

## Function: cr_report_block_changing_approved()

### Returns: trigger

### Language: PLPGSQL

This is a simple filter that prevents updating or deleting reconciliation reports that have already been approved. To purge old reconciliations you must disable the block_change_when_approved trigger on cr_report.

    BEGIN
       IF OLD.approved IS TRUE THEN
           RAISE EXCEPTION 'Report is approved.  Cannot change!';
       END IF;
       IF TG_OP = 'DELETE' THEN
           RETURN OLD;
       ELSE
          RETURN NEW;
       END IF;
    END;

## Function: crosstab(text)

### Returns: SET OF record

### Language: C

    crosstab

## Function: crosstab(text, integer)

### Returns: SET OF record

### Language: C

    crosstab

## Function: crosstab(text, text)

### Returns: SET OF record

### Language: C

    crosstab_hash

## Function: crosstab2(text)

### Returns: SET OF tablefunc_crosstab_2

### Language: C

    crosstab

## Function: crosstab3(text)

### Returns: SET OF tablefunc_crosstab_3

### Language: C

    crosstab

## Function: crosstab4(text)

### Returns: SET OF tablefunc_crosstab_4

### Language: C

    crosstab

## Function: currency_get_exchangerate(in_account_class bpchar, in_date date, in_currency integer)

### Returns: numeric

### Language: PLPGSQL

This function return the exchange rate of a given currency, date and exchange rate class (buy or sell).

    DECLARE
        out_exrate exchangerate.buy%TYPE;
        default_currency char(3);

        BEGIN
            SELECT * INTO default_currency  FROM defaults_get_defaultcurrency();
            IF default_currency = in_currency THEN
               RETURN 1;
            END IF;
            IF in_account_class = 1 THEN
              SELECT buy INTO out_exrate
              FROM exchangerate
              WHERE transdate = in_date AND curr = in_currency;
            ELSE
              SELECT sell INTO out_exrate
              FROM exchangerate
              WHERE transdate = in_date AND curr = in_currency;
            END IF;
            RETURN out_exrate;
        END;

## Function: customer_location_save(in_country_id integer, in_mail_code integer, in_state text, in_city text, in_line_three text, in_line_two text, in_line_one text, in_location_class text, in_entity_id integer)

### Returns: integer

### Language: PLPGSQL

        BEGIN
        return _entity_location_save(
            in_entity_id, NULL,
            in_location_class, in_line_one, in_line_two, in_line_three,
            in_city, in_state, in_mail_code, in_country_id);
        END;

## Function: date_get_all_years()

### Returns: SET OF integer

### Language: PLPGSQL

This function return each year inside transdate in transactions. Currently it uses a sparse index scan because the number of rows returned is very small and the table can be very large.

    DECLARE next_record int;
    BEGIN

    SELECT MIN(EXTRACT ('YEAR' FROM transdate))::INT
    INTO next_record
    FROM acc_trans;

    LOOP

      EXIT WHEN next_record IS NULL;
      RETURN NEXT next_record;
      SELECT MIN(EXTRACT ('YEAR' FROM transdate))::INT AS YEAR
      INTO next_record
      FROM acc_trans
      WHERE EXTRACT ('YEAR' FROM transdate) > next_record;

    END LOOP;

    END;

## Function: days_in_month(in_date date)

### Returns: integer

### Language: SQL

Returns the number of days in the month that includes in_date.

    SELECT (extract(DOM FROM date_trunc('month', $1)
                             + '1 month - 1 second'::interval)
          )::int;

## Function: defaults_get_defaultcurrency()

### Returns: SET OF bpchar

### Language: PLPGSQL

This function return the default currency asigned by the program.

    DECLARE defaultcurrency defaults.value%TYPE;
          BEGIN
               SELECT INTO defaultcurrency substr(value,1,3)
               FROM defaults
               WHERE setting_key = 'curr';
               RETURN NEXT defaultcurrency;
          END;

## Function: del_department()

### Returns: trigger

### Language: PLPGSQL

    begin
      delete from dpt_trans where trans_id = old.id;
      return NULL;
    end;

## Function: del_exchangerate()

### Returns: trigger

### Language: PLPGSQL

    declare
      t_transdate date;
      t_curr char(3);
      t_id int;
      d_curr text;

    begin

      select into d_curr substr(value,1,3) from defaults where setting_key = 'curr';

      if TG_RELNAME = 'ar' then
        select into t_curr, t_transdate curr, transdate from ar where id = old.id;
      end if;
      if TG_RELNAME = 'ap' then
        select into t_curr, t_transdate curr, transdate from ap where id = old.id;
      end if;
      if TG_RELNAME = 'oe' then
        select into t_curr, t_transdate curr, transdate from oe where id = old.id;
      end if;

      if d_curr != t_curr then

        select into t_id a.id from acc_trans ac
        join ar a on (a.id = ac.trans_id)
        where a.curr = t_curr
        and ac.transdate = t_transdate

        except select a.id from ar a where a.id = old.id

        union

        select a.id from acc_trans ac
        join ap a on (a.id = ac.trans_id)
        where a.curr = t_curr
        and ac.transdate = t_transdate

        except select a.id from ap a where a.id = old.id

        union

        select o.id from oe o
        where o.curr = t_curr
        and o.transdate = t_transdate

        except select o.id from oe o where o.id = old.id;

        if not found then
          delete from exchangerate where curr = t_curr and transdate = t_transdate;
        end if;
      end if;
    return old;

    end;

## Function: del_recurring()

### Returns: trigger

### Language: PLPGSQL

    BEGIN
      DELETE FROM recurring WHERE id = old.id;
      DELETE FROM recurringemail WHERE id = old.id;
      DELETE FROM recurringprint WHERE id = old.id;
      RETURN NULL;
    END;

## Function: del_yearend()

### Returns: trigger

### Language: PLPGSQL

    begin
      delete from yearend where trans_id = old.id;
      return NULL;
    end;

## Function: department__list_all()

### Returns: SET OF department

### Language: SQL

    SELECT * FROM department order by description;

## Function: department_list(in_role bpchar)

### Returns: SET OF department

### Language: PLPGSQL

This function returns all department that match the role provided as the argument.

    DECLARE out_department department%ROWTYPE;
    BEGIN
           FOR out_department IN
                   SELECT * from department
                   WHERE role = coalesce(in_role, role)
           LOOP
                   return next out_department;
           END LOOP;
    END;

## Function: dex_init(internal)

### Returns: internal

### Language: C

    tsa_dex_init

## Function: dex_lexize(internal, internal, integer)

### Returns: internal

### Language: C

    tsa_dex_lexize

## Function: draft__search(in_amount_ge text, in_amount_le text, in_to_date date, in_from_date date, in_with_accno numeric, in_type numeric)

### Returns: SET OF draft_search_result

### Language: PLPGSQL

Searches for drafts. in_type may be any of 'ar', 'ap', or 'gl'.

    DECLARE out_row RECORD;
    BEGIN
    	FOR out_row IN
    		SELECT trans.id, trans.transdate, trans.reference,
    			trans.description,
    			sum(case when lower(in_type) = 'ap' AND chart.link = 'AP'
    				 THEN line.amount
    				 WHEN lower(in_type) = 'ar' AND chart.link = 'AR'
    				 THEN line.amount * -1
    				 WHEN lower(in_type) = 'gl' AND line.amount > 0
    				 THEN line.amount
    			 	 ELSE 0
    			    END) as amount
    		FROM (
    			SELECT id, transdate, reference,
    				description,
                                    approved from gl
    			WHERE lower(in_type) = 'gl'
    			UNION
    			SELECT id, transdate, invnumber as reference,
    				(SELECT name FROM eca__get_entity(entity_credit_account)),
    				approved from ap
    			WHERE lower(in_type) = 'ap'
    			UNION
    			SELECT id, transdate, invnumber as reference,
    				description,
    				approved from ar
    			WHERE lower(in_type) = 'ar'
    			) trans
    		JOIN acc_trans line ON (trans.id = line.trans_id)
    		JOIN chart ON (line.chart_id = chart.id)
               LEFT JOIN voucher v ON (v.trans_id = trans.id)
    		WHERE (in_from_date IS NULL or trans.transdate >= in_from_date)
    			AND (in_to_date IS NULL
    				or trans.transdate <= in_to_date)
    			AND trans.approved IS FALSE
    			AND v.id IS NULL
    		GROUP BY trans.id, trans.transdate, trans.description, trans.reference
    		HAVING (in_with_accno IS NULL or in_with_accno =
    			ANY(as_array(chart.accno)))
    		ORDER BY trans.reference
    	LOOP
    		RETURN NEXT out_row;
    	END LOOP;
    END;

## Function: draft_approve(in_id integer)

### Returns: boolean

### Language: PLPGSQL

Deletes the draft from the book. Only will delete unapproved transactions. Otherwise an exception is raised and the transaction terminated.

    declare
    	t_table text;
    begin
    	SELECT table_name into t_table FROM transactions where id = in_id;

            IF (t_table = 'ar') THEN
    		UPDATE ar set approved = true where id = in_id;
    	ELSIF (t_table = 'ap') THEN
    		UPDATE ap set approved = true where id = in_id;
    	ELSIF (t_table = 'gl') THEN
    		UPDATE gl set approved = true where id = in_id;
    	ELSE
    		raise exception 'Invalid table % in draft_approve for transaction %', t_table, in_id;
    	END IF;

    	IF NOT FOUND THEN
    		RETURN FALSE;
    	END IF;

    	UPDATE transactions
    	SET approved_by =
    			(select entity_id FROM users
    			WHERE username = SESSION_USER),
    		approved_at = now()
    	WHERE id = in_id;

    	RETURN TRUE;
    END;

## Function: draft_delete(in_id integer)

### Returns: boolean

### Language: PLPGSQL

    declare
    	t_table text;
    begin
    	DELETE FROM ac_tax_form
    	WHERE entry_id IN
    		(SELECT entry_id FROM acc_trans WHERE trans_id = in_id);

            DELETE FROM acc_trans WHERE trans_id = in_id;
    	SELECT lower(table_name) into t_table FROM transactions where id = in_id;

            IF t_table = 'ar' THEN
    		DELETE FROM ar WHERE id = in_id AND approved IS FALSE;
    	ELSIF t_table = 'ap' THEN
    		DELETE FROM ap WHERE id = in_id AND approved IS FALSE;
    	ELSIF t_table = 'gl' THEN
    		DELETE FROM gl WHERE id = in_id AND approved IS FALSE;
    	ELSE
    		raise exception 'Invalid table % in draft_delete for transaction %', t_table, in_id;
    	END IF;
    	IF NOT FOUND THEN
    		RAISE EXCEPTION 'Invalid transaction id %', in_id;
    	END IF;
    	RETURN TRUE;
    END;

## Function: drop_custom_field(character varying, character varying)

### Returns: boolean

### Language: PLPGSQL

    DECLARE
    table_name ALIAS FOR $1;
    custom_field_name ALIAS FOR $2;
    BEGIN
    	DELETE FROM custom_field_catalog
    	WHERE field_name = custom_field_name AND
    		table_id = (SELECT table_id FROM custom_table_catalog
    			WHERE extends = table_name);
    	EXECUTE 'ALTER TABLE ' || quote_ident('custom_' || table_name) ||
    		' DROP COLUMN ' || quote_ident(custom_field_name);
    	RETURN TRUE;
    END;

## Function: eca__delete_contact(in_contact integer, in_contact_class_id integer, in_credit_id text)

### Returns: boolean

### Language: PLPGSQL

Returns true if at least one record was deleted. False if no records were affected.

    BEGIN

    DELETE FROM eca_to_contact
     WHERE credit_id = in_credit_id and contact_class_id = in_contact_class_id
           and contact= in_contact;
    RETURN FOUND;

    END;

## Function: eca__delete_location(in_location_class integer, in_location_id integer, in_credit_id integer)

### Returns: boolean

### Language: PLPGSQL

Deletes the record identified. Returns true if successful, false if no record found.

    BEGIN

    DELETE FROM eca_to_location
     WHERE credit_id = in_credit_id AND location_id = in_location_id
           AND location_class = in_location_class;

    RETURN FOUND;

    END;

## Function: eca__get_entity(in_credit_id integer)

### Returns: SET OF entity

### Language: PLPGSQL

Returns a set of (only one) entity to which the entity credit account is attached.

    declare
        v_row entity;
    BEGIN
        SELECT entity.* INTO v_row FROM entity_credit_account JOIN entity ON entity_credit_account.entity_id = entity.id WHERE entity_credit_account.id = in_credit_id;
        IF NOT FOUND THEN
            raise exception 'Could not find entity with ID %', in_credit_id;
        ELSE
            return next v_row;
        END IF;
    END;

## Function: eca__get_taxes(in_credit_id integer)

### Returns: SET OF customertax

### Language: SQL

Returns a set of taxable account id's.

    select * from customertax where customer_id = $1
    union
    select * from vendortax where vendor_id = $1;

## Function: eca__list_contacts(in_credit_id integer)

### Returns: SET OF contact_list

### Language: PLPGSQL

Returns a list of contact info attached to the entity credit account.

    DECLARE out_row contact_list;
    BEGIN
    	FOR out_row IN
    		SELECT cl.class, cl.id, c.description, c.contact
    		FROM eca_to_contact c
    		JOIN contact_class cl ON (c.contact_class_id = cl.id)
    		WHERE credit_id = in_credit_id
    	LOOP
    		return next out_row;
    	END LOOP;
    END;

## Function: eca__list_locations(in_credit_id integer)

### Returns: SET OF location_result

### Language: PLPGSQL

Returns a list of locations attached to the credit account.

    DECLARE out_row RECORD;
    BEGIN
    	FOR out_row IN
    		SELECT l.id, l.line_one, l.line_two, l.line_three, l.city,
    			l.state, l.mail_code, c.id, c.name, lc.id, lc.class
    		FROM location l
    		JOIN eca_to_location ctl ON (ctl.location_id = l.id)
    		JOIN location_class lc ON (ctl.location_class = lc.id)
    		JOIN country c ON (c.id = l.country_id)
    		WHERE ctl.credit_id = in_credit_id
    		ORDER BY lc.id, l.id, c.name
    	LOOP
    		RETURN NEXT out_row;
    	END LOOP;
    END;

## Function: eca__list_notes(in_credit_id integer)

### Returns: SET OF note

### Language: PLPGSQL

Returns a list of notes attached to the entity credit account.

    DECLARE out_row record;
    	t_entity_id int;
    BEGIN
            -- ALERT: security definer function.  Be extra careful about EXECUTE
            -- in here. --CT
    	SELECT entity_id INTO t_entity_id
    	FROM entity_credit_account
    	WHERE id = in_credit_id;

    	FOR out_row IN
    		SELECT *
    		FROM note
    		WHERE (note_class = 3 and ref_key = in_credit_id) or
    			(note_class = 1 and ref_key = t_entity_id)
    		ORDER BY created
    	LOOP
    		RETURN NEXT out_row;
    	END LOOP;
    END;

## Function: eca__location_save(in_old_location_class integer, in_country_code integer, in_mail_code integer, in_state text, in_city text, in_line_three text, in_line_two text, in_line_one text, in_location_class text, in_location_id integer, in_credit_id integer)

### Returns: integer

### Language: PLPGSQL

Saves a location to an entity credit account. Returns id of saved record.

        DECLARE
            l_row location;
            l_id INT;
            l_orig_id INT;
        BEGIN

            UPDATE eca_to_location
               SET location_class = in_location_class
             WHERE credit_id = in_credit_id
               AND location_class = in_old_location_class
               AND location_id = in_location_id;

             IF FOUND THEN
                SELECT location_save(
                    in_location_id,
                    in_line_one,
                    in_line_two,
                    in_line_three,
                    in_city,
                    in_state,
                    in_mail_code,
                    in_country_code
                )
            	INTO l_id;
            ELSE
                SELECT location_save(
                    NULL,
                    in_line_one,
                    in_line_two,
                    in_line_three,
                    in_city,
                    in_state,
                    in_mail_code,
                    in_country_code
                )
            	INTO l_id;
                INSERT INTO eca_to_location
            		(credit_id, location_class, location_id)
            	VALUES  (in_credit_id, in_location_class, l_id);

            END IF;

    	RETURN l_id;
        END;

## Function: eca__save_bank_account(in_bank_account_id integer, in_iban integer, in_bic text, in_credit_id text, in_entity_id integer)

### Returns: integer

### Language: PLPGSQL

Saves bank account to the credit account.

    DECLARE out_id int;
    BEGIN
            UPDATE entity_bank_account
               SET bic = in_bic,
                   iban = in_iban
             WHERE id = in_bank_account_id;

            IF FOUND THEN
                    out_id = in_bank_account_id;
            ELSE
    	  	INSERT INTO entity_bank_account(entity_id, bic, iban)
    		VALUES(in_entity_id, in_bic, in_iban);
    	        SELECT CURRVAL('entity_bank_account_id_seq') INTO out_id ;
    	END IF;

    	IF in_credit_id IS NOT NULL THEN
    		UPDATE entity_credit_account SET bank_account = out_id
    		WHERE id = in_credit_id;
    	END IF;

    	RETURN out_id;
    END;

## Function: eca__save_contact(in_old_contact_class integer, in_old_contact integer, in_contact text, in_description text, in_contact_class text, in_credit_id integer)

### Returns: integer

### Language: PLPGSQL

Saves the contact record at the entity credit account level. Returns 1.

    DECLARE out_id int;
    BEGIN

        PERFORM *
           FROM eca_to_contact
          WHERE credit_id = in_credit_id
            AND contact_class_id = in_old_contact_class
            AND contact = in_old_contact;

        IF FOUND THEN
            UPDATE eca_to_contact
               SET contact = in_contact,
                   description = in_description,
                   contact_class_id = in_contact_class
             WHERE credit_id = in_credit_id
               AND contact_class_id = in_old_contact_class
               AND contact = in_old_contact;
        ELSE
            INSERT INTO eca_to_contact(credit_id, contact_class_id,
                    description, contact)
            VALUES (in_credit_id, in_contact_class, in_description, in_contact);

        END IF;

    	RETURN 1;
    END;

## Function: eca__save_notes(in_subject integer, in_note text, in_credit_id text)

### Returns: integer

### Language: PLPGSQL

Saves an entity credit account-level note. Such a note is valid for only one credit account. Returns the id of the note.

    DECLARE out_id int;
    BEGIN
    	-- TODO, change this to create vector too
    	INSERT INTO eca_note (ref_key, note_class, note, vector, subject)
    	VALUES (in_credit_id, 3, in_note, '', in_subject);

    	SELECT currval('note_id_seq') INTO out_id;
    	RETURN out_id;
    END;

## Function: eca__set_taxes(in_tax_ids integer, in_credit_id integer[])

### Returns: boolean

### Language: PLPGSQL

Sets the tax values for the customer or vendor. The entity credit account must exist before calling this function, and must have a type of either 1 or 2.

    DECLARE
        eca entity_credit_account;
        iter int;
    BEGIN
         SELECT * FROM entity_credit_account into eca WHERE id = in_credit_id;

         IF eca.entity_class = 1 then
            DELETE FROM vendortax WHERE vendor_id = in_credit_id;
            FOR iter in array_lower(in_tax_ids, 1) .. array_upper(in_tax_ids, 1)
            LOOP
                 INSERT INTO vendortax (vendor_id, chart_id)
                 values (in_credit_id, in_tax_ids[iter]);
            END LOOP;
         ELSIF eca.entity_class = 2 then
            DELETE FROM customertax WHERE customer_id = in_credit_id;
            FOR iter in array_lower(in_tax_ids, 1) .. array_upper(in_tax_ids, 1)
            LOOP
                 INSERT INTO customertax (customer_id, chart_id)
                 values (in_credit_id, in_tax_ids[iter]);
            END LOOP;
         ELSE
            RAISE EXCEPTION 'Wrong entity class or credit account not found!';
         END IF;
         RETURN TRUE;
    end;

## Function: eca_history(in_inc_closed text, in_inc_open text, in_account_class text, in_start_to text, in_start_from text, in_type text, in_to_date text, in_from_date text, in_country_id text, in_notes integer, in_salesperson date, in_zip date, in_state bpchar, in_city date, in_address_line date, in_contact_info integer, in_meta_number boolean, in_name boolean)

### Returns: SET OF eca_history_result

### Language: SQL

This produces a history detail report, i.e. a list of all products purchased by a customer over a specific date range. meta_number is an exact match, as are in_open and inc_closed. All other fields allow for partial matches. NULL matches all values.

         SELECT eca.id, e.name, eca.meta_number,
                a.id as invoice_id, a.invnumber, a.curr::text,
                p.id AS parts_id, p.partnumber,
                i.description, i.qty, i.unit::text, i.sellprice, i.discount,
                i.deliverydate, pr.id as project_id, pr.projectnumber,
                i.serialnumber,
                case when $16 = 1 then xr.buy else xr.sell end as exchange_rate,
                ee.id as salesperson_id,
                ep.last_name || ', ' || ep.first_name as salesperson_name
         FROM (select * from entity_credit_account
                where meta_number = $2
               UNION
              select * from entity_credit_account WHERE $2 is null
              ) eca  -- broken into unions for performance
         join entity e on eca.entity_id = e.id
         JOIN (select  invnumber, curr, transdate, entity_credit_account, id,
                       person_id
                 FROM ar
                where $16 = 2 and $13 = 'i'
                      and (($17 and amount = paid) or ($18 and amount <> paid))
                UNION
               select invnumber, curr, transdate, entity_credit_account, id,
                      person_id
                 FROM ap
                where $16 = 1 and $13 = 'i'
                      and (($17 and amount = paid) or ($18 and amount <> paid))
               union
               select ordnumber, curr, transdate, entity_credit_account, id,
                      person_id
               from oe
               where ($16= 1 and oe.oe_class_id = 2 and $13 = 'o'
                      and quotation is not true)
                      and (($17 and not closed) or ($18 and closed))
               union
               select ordnumber, curr, transdate, entity_credit_account, id,
                      person_id
               from oe
               where ($16= 2 and oe.oe_class_id = 1 and $13 = 'o'
                      and quotation is not true)
                      and (($17 and not closed) or ($18 and closed))
               union
               select quonumber, curr, transdate, entity_credit_account, id,
                      person_id
               from oe
               where($16= 1 and oe.oe_class_id = 4 and $13 = 'q'
                    and quotation is true)
                      and (($17 and not closed) or ($18 and closed))
               union
               select quonumber, curr, transdate, entity_credit_account, id,
                      person_id
               from oe
               where($16= 2 and oe.oe_class_id = 4 and $13 = 'q'
                     and quotation is true)
                      and (($17 and not closed) or ($18 and closed))
              ) a ON (a.entity_credit_account = eca.id) -- broken into unions
                                                        -- for performance
         JOIN ( select trans_id, parts_id, qty, description, unit, discount,
                       deliverydate, serialnumber, project_id, sellprice
                 FROM  invoice where $13 = 'i'
                union
                select trans_id, parts_id, qty, description, unit, discount,
                       reqdate, serialnumber, project_id, sellprice
                 FROM orderitems where $13 <> 'i'
              ) i on i.trans_id = a.id
         JOIN parts p ON (p.id = i.parts_id)
    LEFT JOIN exchangerate ex ON (ex.transdate = a.transdate)
    LEFT JOIN project pr ON (pr.id = i.project_id)
    LEFT JOIN entity ee ON (a.person_id = ee.id)
    LEFT JOIN person ep ON (ep.entity_id = ee.id)
         JOIN exchangerate xr ON a.transdate = xr.transdate
        -- these filters don't perform as well on large databases
        WHERE (e.name ilike '%' || $1 || '%' or $1 is null)
              and ($3 is null or eca.id in
                     (select credit_id from eca_to_contact
                       where contact ilike '%' || $3 || '%'))
              and (($4 is null and $5 is null and $6 is null and $7 is null)
                   or eca.id in
                      (select credit_id from eca_to_location
                        where location_id in
                              (select id from location
                                where ($4 is null or line_one ilike '%' || $4 || '%'
                                       or line_two ilike '%' || $4 || '%')
                                      and ($5 is null or city
                                                         ilike '%' || $5 || '%')
                                      and ($6 is null or state
                                                        ilike '%' || $6 || '%')
                                      and ($7 is null or mail_code
                                                        ilike '%' || $7 || '%')
                                      and ($10 is null or country_id = $10))
                       )
                  )
              and (a.transdate >= $11 or $11 is null)
              and (a.transdate <= $12 or $12 is null)
              and (eca.startdate >= $14 or $14 is null)
              and (eca.startdate <= $15 or $15 is null)
     ORDER BY eca.meta_number;

## Function: eca_history_summary(in_inc_closed text, in_inc_open text, in_account_class text, in_start_to text, in_start_from text, in_type text, in_to_date text, in_from_date text, in_country_id text, in_notes integer, in_salesperson date, in_zip date, in_state bpchar, in_city date, in_address_line date, in_contact_info integer, in_meta_number boolean, in_name boolean)

### Returns: SET OF eca_history_result

### Language: SQL

Creates a summary account (no quantities, just parts group by invoice). meta_number must match exactly or be NULL. inc_open and inc_closed are exact matches too. All other values specify ranges or may match partially.

    SELECT id, name, meta_number, null::int, null::text, curr, parts_id, partnumber,
           description, sum(qty), unit, null::numeric, null::numeric, null::date,
           null::int, null::text, null::text, null::numeric,
           null::int, null::text
    FROM   eca_history($1, $2, $3, $4, $5, $6, $7, $8, $9,
                       $10, $11, $12, $13, $14, $15, $16, $17, $18)
     group by id, name, meta_number, curr, parts_id, partnumber, description, unit
     order by meta_number;

## Function: employee__get(in_entity_id integer)

### Returns: employee_result

### Language: SQL

Returns an employee_result tuple with information specified by the entity_id.

       SELECT p.entity_id, p.id, s.salutation,
              p.first_name, p.middle_name, p.last_name,
              ee.startdate, ee.enddate, ee.role, ee.ssn, ee.sales, ee.manager_id,
              mp.first_name, mp.last_name, ee.employeenumber, ee.dob
         FROM person p
         JOIN entity_employee ee on (ee.entity_id = p.entity_id)
    LEFT JOIN salutation s on (p.salutation_id = s.id)
    LEFT JOIN person mp ON ee.manager_id = p.entity_id
        WHERE p.entity_id = $1;

## Function: employee__get_user(in_entity_id integer)

### Returns: SET OF users

### Language: SQL

Returns username, user_id, etc. information if the employee is a user.

    SELECT * FROM users WHERE entity_id = $1;

## Function: employee__list_managers(in_id integer)

### Returns: SET OF employees

### Language: PLPGSQL

Returns a list of managers, that is employees with the 'manager' role set.

    DECLARE
    	emp employees%ROWTYPE;
    BEGIN
    	FOR emp IN
    		SELECT
    		    e.salutation,
    		    e.first_name,
    		    e.last_name,
    		    ee.*
    		FROM entity_employee ee
    		JOIN entity e on e.id = ee.entity_id
    		WHERE ee.sales = 't'::bool AND ee.role='manager'
    			AND ee.entity_id <> coalesce(in_id, -1)
    		ORDER BY name
    	LOOP
    		RETURN NEXT emp;
    	END LOOP;
    END;

## Function: employee__save(in_employee_number integer, in_manager_id date, in_sales date, in_ssn date, in_role text, in_dob text, in_end_date boolean, in_start_date integer, in_entity_id text)

### Returns: integer

### Language: PLPGSQL

Saves an employeerecord with the specified information.

    DECLARE out_id INT;
    BEGIN
    	UPDATE entity_employee
    	SET startdate = coalesce(in_start_date, now()::date),
    		enddate = in_end_date,
    		dob = in_dob,
    		role = in_role,
    		ssn = in_ssn,
    		manager_id = in_manager_id,
    		employeenumber = in_employee_number
    	WHERE entity_id = in_entity_id;

    	out_id = in_entity_id;

    	IF NOT FOUND THEN
    		INSERT INTO entity_employee
    			(startdate, enddate, dob, role, ssn, manager_id,
    				employeenumber, entity_id)
    		VALUES
    			(coalesce(in_start_date, now()::date), in_end_date,
                                    in_dob, in_role, in_ssn,
    				in_manager_id, in_employee_number,
                                    in_entity_id);
    		RETURN in_entity_id;
    	END IF;
            RETURN out_id;
    END;

## Function: employee__search(in_notes text, in_last_name date, in_middle_name date, in_first_name text, in_startdate_to text, in_startdate_from text, in_employeenumber text)

### Returns: SET OF employee_result

### Language: SQL

Returns a list of employee_result records matching the search criteria. employeenumber is an exact match. stardate_from and startdate_to specify the start dates for employee searches All others are partial matches. NULLs match all values.

    SELECT p.entity_id, p.id, s.salutation,
              p.first_name, p.middle_name, p.last_name,
              ee.startdate, ee.enddate, ee.role, ee.ssn, ee.sales, ee.manager_id,
              mp.first_name, mp.last_name, ee.employeenumber, ee.dob
         FROM person p
         JOIN entity_employee ee on (ee.entity_id = p.entity_id)
    LEFT JOIN salutation s on (p.salutation_id = s.id)
    LEFT JOIN person mp ON ee.manager_id = p.entity_id
        WHERE ($7 is null or p.entity_id in (select ref_key from entity_note
                                              WHERE note ilike '%' || $7 || '%'))
              and ($1 is null or $1 = ee.employeenumber)
              and ($2 is null or $2 <= ee.startdate)
              and ($3 is null or $3 >= ee.startdate)
              and ($4 is null or p.first_name ilike '%' || $4 || '%')
              and ($5 is null or p.middle_name ilike '%' || $5 || '%')
              and ($6 is null or p.last_name ilike '%' || $6 || '%');

## Function: employee_search(in_sales date, in_enddatefrom date, in_enddateto character varying, in_notes text, in_name date, in_startdateto date, in_startdatefrom boolean)

### Returns: SET OF employee_search

### Language: PLPGSQL

    DECLARE
    	emp employee_search%ROWTYPE;
    BEGIN
    	FOR emp IN
    		SELECT * FROM employee_search
    		WHERE coalesce(startdate, 'infinity'::timestamp)
    			>= coalesce(in_startdateto, '-infinity'::timestamp)
    			AND coalesce(startdate, '-infinity'::timestamp) <=
    				coalesce(in_startdatefrom,
    						'infinity'::timestamp)
    			AND coalesce(enddate, '-infinity'::timestamp) <=
    				coalesce(in_enddateto, 'infinity'::timestamp)
    			AND coalesce(enddate, 'infinity'::timestamp) >=
    				coalesce(in_enddatefrom, '-infinity'::timestamp)
    			AND (name % in_name
    			    OR note % in_notes)
    			AND (sales = 't' OR coalesce(in_sales, 'f') = 'f')
    	LOOP
    		RETURN NEXT emp;
    	END LOOP;
    	return;
    END;

## Function: employee_set_location(in_location integer, in_employee integer)

### Returns: void

### Language: SQL

        INSERT INTO person_to_location (person_id,location_id)
            VALUES ($1, $2);

## Function: entity__delete_bank_account(in_id integer, in_entity_id integer)

### Returns: boolean

### Language: PLPGSQL

Deletes the bank account identitied by in_id if it is attached to the entity identified by entity_id. Returns true if a record is deleted, false if not.

    BEGIN

    UPDATE entity_credit_account SET bank_account = NULL
     WHERE entity_id = in_entity_id AND bank_account = in_id;

    DELETE FROM entity_bank_account
     WHERE id = in_id AND entity_id = in_entity_id;

    RETURN FOUND;

    END;

## Function: entity__get_by_cc(in_control_code text)

### Returns: SET OF entity

### Language: SQL

Returns the entity row attached to the control code.

    SELECT * FROM entity WHERE control_code = $1

## Function: entity__get_entity(in_entity_id integer)

### Returns: SET OF entity

### Language: PLPGSQL

Returns a set of (only one) entity record with the entity id.

    declare
        v_row entity;
    BEGIN
        -- Removing the exception when not found handling.  Applications are
        -- perfectly capable of handling whether an entity was not found.  No need
        -- for a database-level exception here. Moreover such results may be useful
        -- --CT

        SELECT * INTO v_row FROM entity WHERE id = in_entity_id;
        return next v_row;
    END;

## Function: entity__list_classes()

### Returns: SET OF entity_class

### Language: PLPGSQL

Returns a list of entity classes, ordered by assigned ids

    DECLARE out_row entity_class;
    BEGIN
    	FOR out_row IN
    		SELECT * FROM entity_class
    		WHERE active
    		ORDER BY id
    	LOOP
    		RETURN NEXT out_row;
    	END LOOP;
    END;

## Function: entity__list_credit(in_entity_class integer, in_entity_id integer)

### Returns: SET OF entity_credit_retrieve

### Language: PLPGSQL

Returns a list of entity credit account entries for the entity and of the entity class.

    DECLARE out_row entity_credit_retrieve;
    BEGIN

    	FOR out_row IN
    		SELECT  c.id, e.id, ec.entity_class, ec.discount,
    			ec.taxincluded, ec.creditlimit, ec.terms,
    			ec.meta_number, ec.description, ec.business_id,
    			ec.language_code,
    			ec.pricegroup_id, ec.curr, ec.startdate,
    			ec.enddate, ec.ar_ap_account_id, ec.cash_account_id,
    			ec.threshold, e.control_code, ec.id, ec.pay_to_name,
                            ec.taxform_id
    		FROM company c
    		JOIN entity e ON (c.entity_id = e.id)
    		JOIN entity_credit_account ec ON (c.entity_id = ec.entity_id)
    		WHERE e.id = in_entity_id
    			AND ec.entity_class =
    				CASE WHEN in_entity_class = 3 THEN 2
    				     WHEN in_entity_class IS NULL
    					THEN ec.entity_class
    				ELSE in_entity_class END
    	LOOP

    		RETURN NEXT out_row;
    	END LOOP;
    END;

## Function: entity__save_bank_account(in_bank_account_id integer, in_iban text, in_bic text, in_entity_id integer)

### Returns: integer

### Language: PLPGSQL

Saves a bank account to the entity.

    DECLARE out_id int;
    BEGIN
            UPDATE entity_bank_account
               SET bic = in_bic,
                   iban = in_iban
             WHERE id = in_bank_account_id;

            IF FOUND THEN
                    out_id = in_bank_account_id;
            ELSE
    	  	INSERT INTO entity_bank_account(entity_id, bic, iban)
    		VALUES(in_entity_id, in_bic, in_iban);
    	        SELECT CURRVAL('entity_bank_account_id_seq') INTO out_id ;
    	END IF;

    	RETURN out_id;
    END;

## Function: entity__save_notes(in_subject integer, in_note text, in_entity_id text)

### Returns: integer

### Language: PLPGSQL

Saves an entity-level note. Such a note is valid for all credit accounts attached to that entity. Returns the id of the note.

    DECLARE out_id int;
    BEGIN
    	-- TODO, change this to create vector too
    	INSERT INTO entity_note (ref_key, note_class, entity_id, note, vector, subject)
    	VALUES (in_entity_id, 1, in_entity_id, in_note, '', in_subject);

    	SELECT currval('note_id_seq') INTO out_id;
    	RETURN out_id;
    END;

## Function: entity_credit__get(in_id integer)

### Returns: entity_credit_account

### Language: SQL

Returns the entity credit account info.

    SELECT * FROM entity_credit_account WHERE id = $1;

## Function: entity_credit_get_id(in_meta_number integer, in_entity_class integer, in_entity_id text)

### Returns: integer

### Language: PLPGSQL

Returns an entity credit id, based on entity_id, entity_class, and meta_number. This is the preferred way to locate an account if all three of these are known

    DECLARE out_var int;
    BEGIN
    	SELECT id INTO out_var FROM entity_credit_account
    	WHERE entity_id = in_entity_id
    		AND in_entity_class = entity_class
    		AND in_meta_number = meta_number;

    	RETURN out_var;
    END;

## Function: entity_credit_get_id_by_meta_number(in_account_class text, in_meta_number integer)

### Returns: integer

### Language: PLPGSQL

Returns the credit id from the meta_number and entity_class.

    DECLARE out_credit_id int;
    BEGIN
    	SELECT id INTO out_credit_id
    	FROM entity_credit_account
    	WHERE meta_number = in_meta_number
    		AND entity_class = in_account_class;

    	RETURN out_credit_id;
    END;

## Function: entity_credit_save(in_taxform_id integer, in_pay_to_name integer, in_cash_account_id integer, in_ar_ap_account_id text, in_threshold numeric, in_enddate boolean, in_startdate numeric, in_curr integer, in_pricegroup_id integer, in_language character varying, in_business_id integer, in_meta_number character varying, in_terms integer, in_discount_terms bpchar, in_creditlimit date, in_taxincluded date, in_discount numeric, in_description integer, in_entity_id integer, in_entity_class text, in_credit_id integer)

### Returns: integer

### Language: PLPGSQL

Saves an entity credit account. Returns the id of the record saved.

        DECLARE
            t_entity_class int;
            l_id int;
    	t_meta_number text;
    	t_mn_default_key text;
        BEGIN
    	-- TODO:  Move to mapping table.
                IF in_entity_class = 1 THEN
    	       t_mn_default_key := 'vendornumber';
    	    ELSIF in_entity_class = 2 THEN
    	       t_mn_default_key := 'customernumber';
    	    END IF;
    	    IF in_meta_number IS NULL THEN
    		t_meta_number := setting_increment(t_mn_default_key);
    	    ELSE
    		t_meta_number := in_meta_number;
    	    END IF;
                update entity_credit_account SET
                    discount = in_discount,
                    taxincluded = in_taxincluded,
                    creditlimit = in_creditlimit,
    		description = in_description,
                    terms = in_terms,
                    ar_ap_account_id = in_ar_ap_account_id,
                    cash_account_id = in_cash_account_id,
                    meta_number = t_meta_number,
                    business_id = in_business_id,
                    language_code = in_language,
                    pricegroup_id = in_pricegroup_id,
                    curr = in_curr,
                    startdate = in_startdate,
                    enddate = in_enddate,
                    threshold = in_threshold,
    		discount_terms = in_discount_terms,
    		pay_to_name = in_pay_to_name,
    		taxform_id = in_taxform_id
                where id = in_credit_id;

             IF FOUND THEN
                RETURN in_credit_id;
             ELSE
                INSERT INTO entity_credit_account (
                    entity_id,
                    entity_class,
                    discount,
                    description,
                    taxincluded,
                    creditlimit,
                    terms,
                    meta_number,
                    business_id,
                    language_code,
                    pricegroup_id,
                    curr,
                    startdate,
                    enddate,
                    discount_terms,
                    threshold,
    		ar_ap_account_id,
                    pay_to_name,
                    taxform_id,
                    cash_account_id

                )
                VALUES (
                    in_entity_id,
                    in_entity_class,
                    in_discount,
                    in_description,
                    in_taxincluded,
                    in_creditlimit,
                    in_terms,
                    t_meta_number,
                    in_business_id,
                    in_language,
                    in_pricegroup_id,
                    in_curr,
                    in_startdate,
                    in_enddate,
                    in_discount_terms,
                    in_threshold,
                    in_ar_ap_account_id,
                    in_pay_to_name,
                    in_taxform_id,
    		in_cash_account_id
                );
                RETURN currval('entity_credit_account_id_seq');
           END IF;

        END;

## Function: entity_list_contact_class()

### Returns: SET OF contact_class

### Language: PLPGSQL

Returns a list of contact classes ordered by ID.

    DECLARE out_row RECORD;
    BEGIN
    	FOR out_row IN
    		SELECT * FROM contact_class ORDER BY id
    	LOOP
    		RETURN NEXT out_row;
    	END LOOP;
    END;

## Function: entity_save(in_entity_class integer, in_name text, in_entity_id integer)

### Returns: integer

### Language: PLPGSQL

Currently unused. Left in because it is believed it may be helpful. This saves an entity, with the control code being the next available via the defaults table.

        DECLARE
            e entity;
            e_id int;

        BEGIN

            select * into e from entity where id = in_entity_id;

            update
                entity
            SET
                name = in_name,
                entity_class = in_entity_class
            WHERE
                id = in_entity_id;
            IF NOT FOUND THEN
                -- do the insert magic.
                e_id = nextval('entity_id_seq');
                insert into entity (id, name, entity_class) values
                    (e_id,
                    in_name,
                    in_entity_class
                    );
                return e_id;
            END IF;
            return in_entity_id;

        END;

## Function: eoy_close_books(in_retention_acc_id date, in_description text, in_reference text, in_end_date integer)

### Returns: boolean

### Language: PLPGSQL

Zeroes accounts and then creates a checkpoint. in_end_date is the date when the books are to be closed, in_reference and in_description become the reference and description of the gl transaction, and in_retention_acc_id is the retained earnings account id.

    BEGIN
    	IF eoy_zero_accounts(in_end_date, in_reference, in_description, in_retention_acc_id) > 0 THEN
    		PERFORM eoy_create_checkpoint(in_end_date);
    		RETURN TRUE;
    	ELSE
    		RETURN FALSE;
    	END IF;
    END;

## Function: eoy_create_checkpoint(in_end_date date)

### Returns: integer

### Language: PLPGSQL

Creates checkpoints for each account at a specific date. Books are considered closed when they occur before the latest checkpoint timewise. This means that balances (and credit/debit amounts) can be calculated starting at a checkpoint and moving forward (thus providing a mechanism for expunging old data while keeping balances correct at some future point).

    DECLARE ret_val int;
    	approval_check int;
    	cp_date        date;
    BEGIN
    	IF in_end_date > now()::date THEN
    		RAISE EXCEPTION 'Invalid date:  Must be earlier than present';
    	END IF;

    	SELECT count(*) into approval_check
    	FROM acc_trans ac
    	JOIN (
    		select id, approved, transdate FROM ar UNION
    		SELECT id, approved, transdate FROM gl UNION
    		SELECT id, approved, transdate FROM ap
    	) gl ON (gl.id = ac.trans_id)
    	WHERE (ac.approved IS NOT TRUE AND ac.transdate <= in_end_date)
    		OR (gl.approved IS NOT TRUE AND gl.transdate <= in_end_date);

    	if approval_check > 0 THEN
    		RAISE EXCEPTION 'Unapproved transactions in closed period';
    	END IF;

    	SELECT max(end_date) INTO cp_date FROM account_checkpoint WHERE
    	end_date < in_end_date;

    	INSERT INTO
    	account_checkpoint (end_date, account_id, amount, debits, credits)
        SELECT in_end_date, COALESCE(a.chart_id, cp.account_id),
    	    COALESCE(SUM (a.amount),0) + coalesce(MAX (cp.amount), 0),
    	    COALESCE(SUM (CASE WHEN (a.amount < 0) THEN a.amount ELSE 0 END), 0) +
    	     COALESCE( MIN (cp.debits), 0),
    	    COALESCE(SUM (CASE WHEN (a.amount > 0) THEN a.amount ELSE 0 END), 0) +
    	     COALESCE( MAX (cp.credits), 0)
    	FROM
    	(SELECT * FROM acc_trans WHERE transdate <= in_end_date AND
    	 transdate > COALESCE(cp_date, '1200-01-01')) a
    	FULL OUTER JOIN (
    		select account_id, end_date, amount, debits, credits
    		from account_checkpoint
    		WHERE end_date = cp_date
    		) cp on (a.chart_id = cp.account_id)
    	group by COALESCE(a.chart_id, cp.account_id);

    	SELECT count(*) INTO ret_val FROM account_checkpoint
    	where end_date = in_end_date;

    	return ret_val;
    END;

## Function: eoy_earnings_accounts()

### Returns: SET OF account

### Language: SQL

Lists equity accounts for the retained earnings dropdown.

        SELECT *
          FROM account
         WHERE category = 'Q'
         ORDER BY accno;

## Function: eoy_reopen_books(in_end_date date)

### Returns: boolean

### Language: PLPGSQL

Removes checkpoints and reverses yearend transactions starting on in_end_date

    BEGIN
    	PERFORM count(*) FROM account_checkpoint WHERE end_date = in_end_date;

    	IF NOT FOUND THEN
    		RETURN FALSE;
    	END IF;

    	DELETE FROM account_checkpoint WHERE end_date = in_end_date;

    	PERFORM count(*) FROM yearend
    	WHERE transdate = in_end_date and reversed is not true;

    	IF FOUND THEN
    		INSERT INTO gl (reference, description, approved)
    		SELECT 'Reversing ' || reference, 'Reversing ' || description,
    			true
    		FROM gl WHERE id = (select trans_id from yearend
    			where transdate = in_end_date and reversed is not true);

    		INSERT INTO acc_trans (chart_id, amount, transdate, trans_id,
    			approved)
    		SELECT chart_id, amount * -1, currval('id'), true
    		FROM acc_trans where trans_id = (select trans_id from yearend
    			where transdate = in_end_date and reversed is not true);

    		UPDATE yearend SET reversed = true where transdate = in_end_date
    			and reversed is not true;
    	END IF;

    	DELETE FROM account_checkpoint WHERE end_date = in_end_date;
    	RETURN TRUE;
    END;

## Function: eoy_zero_accounts(in_retention_acc_id date, in_description text, in_reference text, in_end_date integer)

### Returns: integer

### Language: PLPGSQL

Posts a transaction which zeroes the income and expense accounts, moving the net balance there into a retained earnings account identified by in_retention_acc_id.

    DECLARE ret_val int;
    BEGIN
    	INSERT INTO gl (transdate, reference, description, approved)
    	VALUES (in_end_date, in_reference, in_description, true);

    	INSERT INTO yearend (trans_id, transdate) values (currval('id'), in_end_date);
    	INSERT INTO acc_trans (transdate, chart_id, trans_id, amount)
    	SELECT in_end_date, a.chart_id, currval('id'),
    		(sum(a.amount) + coalesce(max(cp.amount), 0)) * -1
    	FROM acc_trans a
    	LEFT JOIN (
    		select account_id, end_date, amount from account_checkpoint
    		WHERE end_date = (select max(end_date) from account_checkpoint
    				where end_date < in_end_date)
    		) cp on (a.chart_id = cp.account_id)
    	JOIN account acc ON (acc.id = a.chart_id)
    	WHERE a.transdate <= in_end_date
    		AND a.transdate > coalesce(cp.end_date, a.transdate - 1)
    		AND acc.category IN ('I', 'E')
    	GROUP BY a.chart_id;

    	INSERT INTO acc_trans (transdate, trans_id, chart_id, amount)
    	SELECT in_end_date, currval('id'), in_retention_acc_id,
    		coalesce(sum(amount) * -1, 0)
    	FROM acc_trans WHERE trans_id = currval('id');

    	SELECT count(*) INTO ret_val from acc_trans
    	where trans_id = currval('id');

    	RETURN ret_val;
    end;

## Function: file__attach_to_order(in_file_class bytea, in_ref_key integer, in_id text, in_description text, in_file_name integer, in_mime_type_id integer, in_content integer)

### Returns: file_base

### Language: PLPGSQL

Attaches or links a file to an order. in_content OR id can be set. Setting both raises an exception.

    DECLARE retval file_base;
    BEGIN
       IF in_id IS NOT NULL THEN
           IF in_content THEN
              RAISE EXCEPTION $e$Conflicting options file_id and content$e$;
           END IF;
           IF in_file_class = 1 THEN
               INSERT INTO file_tx_to_order
                      (file_id, source_class, ref_key, dest_class, attached_by,
                      attached_at)
               VALUES (in_id, 1, in_ref_key, 2, person__get_my_entity_id(), now());
           ELSIF in_file_class = 2 THEN
               INSERT INTO file_order_to_order
                      (file_id, source_class, ref_key, dest_class, attached_by,
                      attached_at)
               VALUES (in_id, 2, in_ref_key, 2, person__get_my_entity_id(), now());
           ELSE
               RAISE EXCEPTION $E$Invalid file class$E$;
           END IF;
           SELECT * INTO retval FROM file_base where id = in_id;
           RETURN retval;
       ELSE
           INSERT INTO file_transaction
                       (content, mime_type_id, file_name, description, ref_key,
                       file_class, uploaded_by, uploaded_at)
                VALUES (in_content, in_mime_type_id, in_file_name, in_description,
                       in_ref_key, in_file_class, person__get_my_entity_id(),
                       now());
            SELECT * INTO retval FROM file_base
             where id = currval('file_base_id_seq');

            RETURN retval;
        END IF;
    END;

## Function: file__attach_to_part(in_file_class bytea, in_ref_key integer, in_id text, in_description text, in_file_name integer, in_mime_type_id integer, in_content integer)

### Returns: file_base

### Language: PLPGSQL

Attaches or links a file to a good or service. in_content OR id can be set. Setting both raises an exception. Note that currently links (setting id) is NOT supported because we dont have a use case of linking files to parts

    DECLARE retval file_base;
    BEGIN
       IF in_id IS NOT NULL THEN
           IF in_content THEN
              RAISE EXCEPTION $e$Can't specify id and content in attachment$e$;--'
           END IF;
           RAISE EXCEPTION 'links not implemented';
           RETURN retval;
       ELSE
           INSERT INTO file_part
                       (content, mime_type_id, file_name, description, ref_key,
                       file_class, uploaded_by, uploaded_at)
                VALUES (in_content, in_mime_type_id, in_file_name, in_description,
                       in_ref_key, in_file_class, person__get_my_entity_id(),
                       now());
            SELECT * INTO retval FROM file_base
             where id = currval('file_base_id_seq');

            RETURN retval;
        END IF;
    END;

## Function: file__attach_to_tx(in_file_class bytea, in_ref_key integer, in_id text, in_description text, in_file_name integer, in_mime_type_id integer, in_content integer)

### Returns: file_base

### Language: PLPGSQL

Attaches or links a file to a transaction. in_content OR id can be set. Setting both raises an exception.

    DECLARE retval file_base;
    BEGIN
       IF in_id IS NOT NULL THEN
           IF in_content THEN
              RAISE EXCEPTION $e$Can't specify id and content in attachment$e$;--'
           END IF;
           INSERT INTO file_order_to_tx
                  (file_id, source_class, ref_key, dest_class, attached_by,
                  attached_at)
           VALUES (in_id, 2, in_ref_key, 1, person__get_my_entity_id(), now());

           SELECT * INTO retval FROM file_base where id = in_id;
           RETURN retval;
       ELSE
           INSERT INTO file_transaction
                       (content, mime_type_id, file_name, description, ref_key,
                       file_class, uploaded_by, uploaded_at)
                VALUES (in_content, in_mime_type_id, in_file_name, in_description,
                       in_ref_key, in_file_class, person__get_my_entity_id(),
                       now());
            SELECT * INTO retval FROM file_base
             where id = currval('file_base_id_seq');

            RETURN retval;
        END IF;
    END;

## Function: file__get(in_file_class integer, in_id integer)

### Returns: file_base

### Language: SQL

Retrieves the file information specified including content.

    SELECT * FROM file_base where id = $1 and file_class = $2;

## Function: file__get_mime_type(in_mime_type_text integer, in_mime_type_id text)

### Returns: mime_type

### Language: SQL

Retrieves mime type information associated with a file object.

    select * from mime_type
     where ($1 IS NULL OR id = $1) AND ($2 IS NULL OR mime_type = $2);

## Function: file__list_by(in_file_class integer, in_ref_key integer)

### Returns: SET OF file_list_item

### Language: SQL

Returns a list of files attached to a database object. No content is retrieved.

    SELECT m.mime_type, f.file_name, f.description, f.uploaded_by, e.name,
           f.uploaded_at, f.id, f.ref_key, f.file_class,
           case when m.mime_type = 'text/x-uri' THEN f.content ELSE NULL END
      FROM mime_type m
      JOIN file_base f ON f.mime_type_id = m.id
      JOIN entity e ON f.uploaded_by = e.id
     WHERE f.ref_key = $1 and f.file_class = $2;

## Function: file__list_links(in_file_class integer, in_ref_key integer)

### Returns: SET OF file_links

### Language: SQL

This function retrieves a list of file attachments on a specified object.

     select * from file_links where ref_key = $1 and dest_class = $2;

## Function: file_links_vrebuild()

### Returns: boolean

### Language: PLPGSQL

    DECLARE
       viewline file_view_catalog%rowtype;
       stmt text;
    BEGIN
       stmt := '';
       FOR viewline IN
           select * from file_view_catalog
       LOOP
           IF stmt = '' THEN
               stmt := 'SELECT * FROM ' || quote_ident(viewline.view_name) || '
    ';
           ELSE
               stmt := stmt || ' UNION
    SELECT * FROM '|| quote_ident(viewline.view_name) || '
    ';
           END IF;
       END LOOP;
       EXECUTE 'CREATE OR REPLACE VIEW file_links AS
    ' || stmt;
       RETURN TRUE;
    END;

## Function: form_check(in_form_id integer, in_session_id integer)

### Returns: boolean

### Language: SQL

This checks to see if an open form (record in open_forms) exists with the form_id and session_id provided. Returns true if exists, false if not.

    SELECT count(*) = 1
      FROM open_forms f
      JOIN "session" s USING (session_id)
      JOIN users u ON (s.users_id = u.id)
     WHERE f.session_id = $1 and f.id = $2 and u.username = SESSION_USER;

## Function: form_close(in_form_id integer, in_session_id integer)

### Returns: boolean

### Language: PLPGSQL

Closes out the form by deleting it from the open_forms table. Returns true if found, false if not.

    DECLARE form_test bool;
    BEGIN
    	form_test := form_check(in_session_id, in_form_id);

    	IF form_test is true THEN
    		DELETE FROM open_forms
    		WHERE session_id = in_session_id AND id = in_form_id;

    		RETURN TRUE;

    	ELSE RETURN FALSE;
    	END IF;
    END;

## Function: form_open(in_session_id integer)

### Returns: integer

### Language: PLPGSQL

This opens a form, and returns the id of the form opened.

    DECLARE usertest bool;
    BEGIN
            SELECT count(*) = 1 INTO usertest FROM session
             WHERE session_id = in_session_id
                   AND users_id IN (select id from users
                                    WHERE username = SESSION_USER);

            IF usertest is not true THEN
                RAISE EXCEPTION 'Invalid session';
            END IF;

    	INSERT INTO open_forms (session_id) VALUES (in_session_id);
    	RETURN currval('open_forms_id_seq');
    END;

## Function: get_covers(tsvector, tsquery)

### Returns: text

### Language: C

    tsa_get_covers

## Function: get_fractional_month(in_date_second date, in_date_first date)

### Returns: numeric

### Language: SQL

Returns the number of months between two dates in numeric form.

    SELECT CASE WHEN is_same_month($1, $2)
                THEN ($2 - $1)::numeric
                     / days_in_month($1)
                ELSE (get_fractional_month(
                       $1, (date_trunc('MONTH', $1)
                           + '1 month - 1 second'::interval)::date)
                     + get_fractional_month(date_trunc('MONTH', $2)::date, $2)
                     + (extract ('YEAR' from $2) - extract ('YEAR' from $1) * 12)
                     + extract ('MONTH' from $1) - extract ('MONTH' from $2)
                     - 1)::numeric
                END;

## Function: get_fractional_year(in_date_to date, in_date_from date)

### Returns: numeric

### Language: SQL

Returns the decimal representation of the fractional year.

       select ($2 - $1
                - leap_days(next_leap_year_calc($1, false),
                           next_leap_year_calc($2, true)))
                /365::numeric;

## Function: get_link_descriptions()

### Returns: SET OF account_link_description

### Language: SQL

Gets a set of all valid account_link descriptions.

        SELECT * FROM account_link_description;

## Function: gin_extract_trgm(text, internal)

### Returns: internal

### Language: C

    gin_extract_trgm

## Function: gin_extract_trgm(text, internal, smallint, internal, internal)

### Returns: internal

### Language: C

    gin_extract_trgm

## Function: gin_trgm_consistent(internal, smallint, text, integer, internal, internal)

### Returns: boolean

### Language: C

    gin_trgm_consistent

## Function: gl_audit_trail_append()

### Returns: trigger

### Language: PLPGSQL

This provides centralized support for insertions into audittrail.

    DECLARE
       t_reference text;
       t_row RECORD;
    BEGIN

    IF TG_OP = 'INSERT' then
       t_row := NEW;
    ELSE
       t_row := OLD;
    END IF;

    IF TG_RELNAME IN ('ar', 'ap') THEN
        t_reference := t_row.invnumber;
    ELSE
        t_reference := t_row.reference;
    END IF;

    INSERT INTO audittrail (trans_id, reference, action, person_id)
    values (t_row.id, t_reference, TG_OP, person__get_my_entity_id());

    return null; -- AFTER TRIGGER ONLY, SAFE
    END;

## Function: gtrgm_compress(internal)

### Returns: internal

### Language: C

    gtrgm_compress

## Function: gtrgm_consistent(internal, text, integer, oid, internal)

### Returns: boolean

### Language: C

    gtrgm_consistent

## Function: gtrgm_decompress(internal)

### Returns: internal

### Language: C

    gtrgm_decompress

## Function: gtrgm_in(cstring)

### Returns: gtrgm

### Language: C

    gtrgm_in

## Function: gtrgm_out(public.gtrgm)

### Returns: cstring

### Language: C

    gtrgm_out

## Function: gtrgm_penalty(internal, internal, internal)

### Returns: internal

### Language: C

    gtrgm_penalty

## Function: gtrgm_picksplit(internal, internal)

### Returns: internal

### Language: C

    gtrgm_picksplit

## Function: gtrgm_same(public.gtrgm, public.gtrgm, internal)

### Returns: internal

### Language: C

    gtrgm_same

## Function: gtrgm_union(bytea, internal)

### Returns: integer[]

### Language: C

    gtrgm_union

## Function: headline(oid, text, tsquery)

### Returns: text

### Language: INTERNAL

    ts_headline_byid

## Function: headline(oid, text, tsquery, text)

### Returns: text

### Language: INTERNAL

    ts_headline_byid_opt

## Function: headline(text, text, tsquery)

### Returns: text

### Language: C

    tsa_headline_byname

## Function: headline(text, text, tsquery, text)

### Returns: text

### Language: C

    tsa_headline_byname

## Function: headline(text, tsquery)

### Returns: text

### Language: INTERNAL

    ts_headline

## Function: headline(text, tsquery, text)

### Returns: text

### Language: INTERNAL

    ts_headline_opt

## Function: invoice__get_by_vendor_number(in_invoice_number text, in_meta_nunber text)

### Returns: ap

### Language: PLPGSQL

    DECLARE retval ap;
    BEGIN
    	SELECT * INTO retval FROM ap WHERE entity_credit_id =
    		(select id from entity_credit_account where entity_class = 1
    		AND meta_number = in_meta_number)
    		AND invnumber = in_invoice_number;
    	RETURN retval;
    END;

## Function: is_leapyear(in_date date)

### Returns: boolean

### Language: SQL

Returns true if date is in a leapyear. False if not. Uses the built-in PostgreSQL date handling, and no direct detection is done in our code.

        select extract('day' FROM (
                               (extract('year' FROM $1)::text
                               || '-02-28')::date + '1 day'::interval)::date)
               = 29;

## Function: is_same_month(in_date2 date, in_date1 date)

### Returns: boolean

### Language: SQL

Returns true if the two dates are in the same month and year. False otherwise.

    SELECT is_same_year($1, $2)
           and extract ('MONTH' from $1) = extract ('MONTH' from $2);

## Function: is_same_year(in_date2 date, in_date1 date)

### Returns: boolean

### Language: SQL

Returns true if the two dates are in the same year, false otherwise.

    SELECT  extract ('YEAR' from $1) = extract ('YEAR' from $2);

## Function: je_get_default_lines()

### Returns: character varying

### Language: SQL

    SELECT value FROM menu_attribute where node_id = 74 and attribute = 'rowcount';

## Function: je_set_default_lines(in_rowcount integer)

### Returns: integer

### Language: PLPGSQL

    BEGIN
        UPDATE menu_attribute set value = $1
         where node_id = 74 and attribute='rowcount';

        IF NOT FOUND THEN
             INSERT INTO menu_attribute (node_id, attribute, value)
                  values (74, 'rowcount', $1);
        END IF;
        RETURN $1;
    END;

## Function: lastcost(integer)

### Returns: double precision

### Language: PLPGSQL

    DECLARE

    v_cost float;
    v_parts_id alias for $1;

    BEGIN

      SELECT INTO v_cost sellprice FROM invoice i
      JOIN ap a ON (a.id = i.trans_id)
      WHERE i.parts_id = v_parts_id
      ORDER BY a.transdate desc, a.id desc
      LIMIT 1;

      IF v_cost IS NULL THEN
        v_cost := 0;
      END IF;

    RETURN v_cost;
    END;

## Function: leap_days(in_year_to integer, in_year_from integer)

### Returns: integer

### Language: SQL

Returns the number of leap years between the two year inputs, inclusive.

       SELECT count(*)::int
       FROM generate_series($1, $2)
       WHERE is_leapyear((generate_series::text || '-01-01')::date);

## Function: length(tsvector)

### Returns: integer

### Language: INTERNAL

    tsvector_length

## Function: lexize(oid, text)

### Returns: text[]

### Language: INTERNAL

    ts_lexize

## Function: lexize(text)

### Returns: text[]

### Language: C

    tsa_lexize_bycurrent

## Function: lexize(text, text)

### Returns: text[]

### Language: C

    tsa_lexize_byname

## Function: list_taxforms(in_entity_id integer)

### Returns: SET OF country_tax_form

### Language: PLPGSQL

Returns a list of tax forms for the entity's country.

    DECLARE t_country_tax_form country_tax_form;
    BEGIN

    	FOR t_country_tax_form IN

    		      SELECT *
    		            FROM country_tax_form where country_id in(SELECT country_id from entity where id=in_entity_id)
            LOOP

    	RETURN NEXT t_country_tax_form;

    	END LOOP;

    END;

## Function: location__get(in_id integer)

### Returns: location

### Language: PLPGSQL

Returns the location specified by in_id.

    DECLARE
    	out_location location%ROWTYPE;
    BEGIN
    	SELECT * INTO out_location FROM location WHERE id = in_id;
    	RETURN out_location;
    END;

## Function: location_delete(in_id integer)

### Returns: void

### Language: PLPGSQL

DELETES the location specified by in_id. Does not return a value.

    BEGIN
    	DELETE FROM location WHERE id = in_id;
    END;

## Function: location_list_all()

### Returns: SET OF location

### Language: PLPGSQL

Returns all locations, ordered by country, state, and city.

    DECLARE
    	out_location location%ROWTYPE;
    BEGIN
    	FOR out_location IN
    		SELECT * FROM location
    		ORDER BY country, state, city
    	LOOP
    		RETURN NEXT out_location;
    	END LOOP;
    END;

## Function: location_list_class()

### Returns: SET OF location_class

### Language: PLPGSQL

Lists location classes, by default in order entered.

    DECLARE out_row RECORD;
    BEGIN
    	FOR out_row IN
    		SELECT * FROM location_class ORDER BY id
    	LOOP
    		RETURN NEXT out_row;
    	END LOOP;
    END;

## Function: location_list_country()

### Returns: SET OF country

### Language: PLPGSQL

Lists countries, by default in alphabetical order.

    DECLARE out_row RECORD;
    BEGIN
    	FOR out_row IN
    		SELECT * FROM country ORDER BY name
    	LOOP
    		RETURN NEXT out_row;
    	END LOOP;
    END;

## Function: location_save(in_country integer, in_zipcode text, in_state text, in_city text, in_address3 text, in_address2 text, in_address1 text, in_location_id integer)

### Returns: integer

### Language: PLPGSQL

Note that this does NOT override the data in the database unless in_location_id is specified. Instead we search for locations matching the desired specifications and if none are found, we insert one. Either way, the return value of the location can be used for mapping to other things. This is necessary because locations are only loosly coupled with entities, etc.

    DECLARE
    	location_id integer;
    	location_row RECORD;
    BEGIN

    	IF in_location_id IS NULL THEN
    	    SELECT id INTO location_id FROM location
    	    WHERE line_one = in_address1 AND line_two = in_address2
    	          AND line_three = in_address3 AND in_city = city
    	          AND in_state = state AND in_zipcode = mail_code
    	          AND in_country = country_id
    	    LIMIT 1;

    	    IF NOT FOUND THEN
    	    -- Straight insert.
    	    location_id = nextval('location_id_seq');
    	    INSERT INTO location (
    	        id,
    	        line_one,
    	        line_two,
    	        line_three,
    	        city,
    	        state,
    	        mail_code,
    	        country_id)
    	    VALUES (
    	        location_id,
    	        in_address1,
    	        in_address2,
    	        in_address3,
    	        in_city,
    	        in_state,
    	        in_zipcode,
    	        in_country
    	        );
    	    END IF;
    	    return location_id;
    	ELSE
    	    RAISE NOTICE 'Overwriting location id %', in_location_id;
    	    -- Test it.
    	    SELECT * INTO location_row FROM location WHERE id = in_location_id;
    	    IF NOT FOUND THEN
    	        -- Tricky users are lying to us.
    	        RAISE EXCEPTION 'location_save called with nonexistant location ID %', in_location_id;
    	    ELSE
    	        -- Okay, we're good.

    	        UPDATE location SET
    	            line_one = in_address1,
    	            line_two = in_address2,
    	            line_three = in_address3,
    	            city = in_city,
    	            state = in_state,
    	            mail_code = in_zipcode,
    	            country_id = in_country
    	        WHERE id = in_location_id;
    	        return in_location_id;
    	    END IF;
    	END IF;
    END;

## Function: location_search(in_country character varying, in_zipcode character varying, in_state character varying, in_city character varying, in_address2 character varying, in_address1 character varying)

### Returns: SET OF location

### Language: PLPGSQL

Returns matching locations. All matches may be partial.

    DECLARE
    	out_location location%ROWTYPE;
    BEGIN
    	FOR out_location IN
    		SELECT * FROM location
    		WHERE address1 ilike '%' || in_address1 || '%'
    			AND address2 ilike '%' || in_address2 || '%'
    			AND in_city ilike '%' || in_city || '%'
    			AND in_state ilike '%' || in_state || '%'
    			AND in_zipcode ilike '%' || in_zipcode || '%'
    			AND in_country ilike '%' || in_country || '%'
    	LOOP
    		RETURN NEXT out_location;
    	END LOOP;
    END;

## Function: lock_record(in_session_id integer, in_id integer)

### Returns: boolean

### Language: PLPGSQL

This function seeks to lock a record with an id of in_id to a session with an id of in_session_id. If possible, it returns true. If it is already locked, false. These are not hard locks and the application is free to disregard or not even ask. They time out when the session is destroyed.

    declare
       locked int;
    begin
       SELECT locked_by into locked from transactions where id = $1;
       IF NOT FOUND THEN
    	RETURN FALSE;
       ELSEIF locked is not null AND locked <> $2 THEN
            RETURN FALSE;
       END IF;
       UPDATE transactions set locked_by = $2 where id = $1;
       RETURN TRUE;
    end;

## Function: menu_children(in_parent_id integer)

### Returns: SET OF menu_item

### Language: PLPGSQL

This function returns all menu items which are children of in_parent_id (the only input parameter). It is thus similar to menu_generate() but it only returns the menu items associated with nodes directly descendant from the parent. It is used for menues for frameless browsers.

    declare
    	item menu_item;
    	arg menu_attribute%ROWTYPE;
    begin
            FOR item IN
    		SELECT n.position, n.id, c.level, n.label, c.path,
                           to_args(array[ma.attribute, ma.value])
    		FROM connectby('menu_node', 'id', 'parent', 'position',
    				in_parent_id, 1, ',')
    			c(id integer, parent integer, "level" integer,
    				path text, list_order integer)
    		JOIN menu_node n USING(id)
                    JOIN menu_attribute ma ON (n.id = ma.node_id)
                   WHERE n.id IN (select node_id FROM menu_acl
                                   WHERE pg_has_role(CASE WHEN role_name
                                                               ilike 'public'
                                                          THEN current_user
                                                          ELSE role_name
                                                       END, 'USAGE')
                                GROUP BY node_id
                                  HAVING bool_and(CASE WHEN acl_type ilike 'DENY'
                                                       THEN FALSE
                                                       WHEN acl_type ilike 'ALLOW'
                                                       THEN TRUE
                                                    END))
                        or exists (select cn.id, cc.path
                                     FROM connectby('menu_node', 'id', 'parent',
                                                    'position', '0', 0, ',')
                                          cc(id integer, parent integer,
                                             "level" integer, path text,
                                             list_order integer)
                                     JOIN menu_node cn USING(id)
                                    WHERE cn.id IN
                                          (select node_id FROM menu_acl
                                            WHERE pg_has_role(CASE WHEN role_name
                                                               ilike 'public'
                                                          THEN current_user
                                                          ELSE role_name
                                                       END, 'USAGE')
                                         GROUP BY node_id
                                           HAVING bool_and(CASE WHEN acl_type
                                                                     ilike 'DENY'
                                                                THEN false
                                                                WHEN acl_type
                                                                     ilike 'ALLOW'
                                                                THEN TRUE
                                                             END))
                                           and cc.path like c.path || ',%')
                GROUP BY n.position, n.id, c.level, n.label, c.path, c.list_order
                ORDER BY c.list_order
            LOOP
                    return next item;
            end loop;
    end;

## Function: menu_generate()

### Returns: SET OF menu_item

### Language: PLPGSQL

This function returns the complete menu tree. It is used to generate nested menus for the web interface.

    DECLARE
    	item menu_item;
    	arg menu_attribute%ROWTYPE;
    BEGIN
    	FOR item IN
    		SELECT n.position, n.id, c.level, n.label, c.path,
                           to_args(array[ma.attribute, ma.value])
    		FROM connectby('menu_node', 'id', 'parent', 'position', '0',
    				0, ',')
    			c(id integer, parent integer, "level" integer,
    				path text, list_order integer)
    		JOIN menu_node n USING(id)
                    JOIN menu_attribute ma ON (n.id = ma.node_id)
                   WHERE n.id IN (select node_id FROM menu_acl
                                   WHERE pg_has_role(CASE WHEN role_name
                                                               ilike 'public'
                                                          THEN current_user
                                                          ELSE role_name
                                                       END, 'USAGE')
                                GROUP BY node_id
                                  HAVING bool_and(CASE WHEN acl_type ilike 'DENY'
                                                       THEN FALSE
                                                       WHEN acl_type ilike 'ALLOW'
                                                       THEN TRUE
                                                    END))
                        or exists (select cn.id, cc.path
                                     FROM connectby('menu_node', 'id', 'parent',
                                                    'position', '0', 0, ',')
                                          cc(id integer, parent integer,
                                             "level" integer, path text,
                                             list_order integer)
                                     JOIN menu_node cn USING(id)
                                    WHERE cn.id IN
                                          (select node_id FROM menu_acl
                                            WHERE pg_has_role(CASE WHEN role_name
                                                               ilike 'public'
                                                          THEN current_user
                                                          ELSE role_name
                                                       END, 'USAGE')
                                         GROUP BY node_id
                                           HAVING bool_and(CASE WHEN acl_type
                                                                     ilike 'DENY'
                                                                THEN false
                                                                WHEN acl_type
                                                                     ilike 'ALLOW'
                                                                THEN TRUE
                                                             END))
                                           and cc.path like c.path || ',%')
                GROUP BY n.position, n.id, c.level, n.label, c.path, c.list_order
                ORDER BY c.list_order

    	LOOP
    		RETURN NEXT item;
    	END LOOP;
    END;

## Function: menu_insert(in_label integer, in_position integer, in_parent_id text)

### Returns: integer

### Language: PLPGSQL

This function inserts menu items at arbitrary positions. The arguments are, in order: parent, position, label. The return value is the id number of the menu item created.

    DECLARE
    	new_id int;
    BEGIN
    	UPDATE menu_node
    	SET position = position * -1
    	WHERE parent = in_parent_id
    		AND position >= in_position;

    	INSERT INTO menu_node (parent, position, label)
    	VALUES (in_parent_id, in_position, in_label);

    	SELECT INTO new_id currval('menu_node_id_seq');

    	UPDATE menu_node
    	SET position = (position * -1) + 1
    	WHERE parent = in_parent_id
    		AND position < 0;

    	RETURN new_id;
    END;

## Function: months_passed(in_end timestamp without time zone, in_start timestamp without time zone)

### Returns: integer

### Language: SQL

Returns the number of months between in_start and in_end.

    -- The addition of one day is so that it will return '1' when run on the end
    -- day of consecutive months.

    select (extract (months from age($2 + '1 day', $1 + '1 day'))
           + extract (years from age($2, $1)) * 12)::int;

## Function: next_leap_year_calc(is_end date, in_date boolean)

### Returns: integer

### Language: SQL

Next relevant leap year calculation for a daily depreciation calculation

    SELECT
              (CASE WHEN extract('doy' FROM $1) < 59
              THEN extract('year' FROM $1)
              ELSE extract('year' FROM $1) + 1
              END)::int
              -
              CASE WHEN $2 THEN 1 ELSE 0 END;

## Function: normal_rand(integer, double precision, double precision)

### Returns: SET OF double precision

### Language: C

    normal_rand

## Function: numnode(tsquery)

### Returns: integer

### Language: INTERNAL

    tsquery_numnode

## Function: parse(oid, text)

### Returns: SET OF tokenout

### Language: INTERNAL

    ts_parse_byid

## Function: parse(text)

### Returns: SET OF tokenout

### Language: C

    tsa_parse_current

## Function: parse(text, text)

### Returns: SET OF tokenout

### Language: INTERNAL

    ts_parse_byname

## Function: payment__reverse(in_voucher_id text, in_batch_id date, in_account_class integer, in_date_reversed text, in_cash_accno date, in_credit_id integer, in_date_paid integer, in_source integer)

### Returns: integer

### Language: PLPGSQL

Reverses a payment. All fields are mandatory except batch_id and voucher_id because they determine the identity of the payment to be reversed.

    DECLARE
    	pay_row record;
            t_voucher_id int;
            t_voucher_inserted bool;
    BEGIN
            IF in_batch_id IS NOT NULL THEN
    		t_voucher_id := nextval('voucher_id_seq');
    		t_voucher_inserted := FALSE;
    	END IF;
    	FOR pay_row IN
    		SELECT a.*, c.ar_ap_account_id
    		FROM acc_trans a
    		JOIN (select id, entity_credit_account
    			FROM ar WHERE in_account_class = 2
    			UNION
    			SELECT id, entity_credit_account
    			FROM ap WHERE in_account_class = 1
    		) arap ON (a.trans_id = arap.id)
    		JOIN entity_credit_account c
    			ON (arap.entity_credit_account = c.id)
    		JOIN chart ch ON (a.chart_id = ch.id)
    		WHERE coalesce(source, '') = coalesce(in_source, '')
    			AND transdate = in_date_paid
    			AND in_credit_id = c.id
    			AND in_cash_accno = ch.accno
                            and coalesce (in_voucher_id, 0)
                                 = coalesce(voucher_id, 0)
    	LOOP
    		IF in_batch_id IS NOT NULL
    			AND t_voucher_inserted IS NOT TRUE
    		THEN
    			INSERT INTO voucher
    			(id, trans_id, batch_id, batch_class)
    			VALUES
    			(t_voucher_id, pay_row.trans_id, in_batch_id,
    				CASE WHEN in_account_class = 1 THEN 4
    				     WHEN in_account_class = 2 THEN 7
    				END);

    			t_voucher_inserted := TRUE;
    		END IF;

    		INSERT INTO acc_trans
    		(trans_id, chart_id, amount, transdate, source, memo, approved,
    			voucher_id)
    		VALUES
    		(pay_row.trans_id, pay_row.chart_id, pay_row.amount * -1,
    			in_date_reversed, in_source, 'Reversing ' ||
    			COALESCE(in_source, ''),
    			case when in_batch_id is not null then false
    			else true end, t_voucher_id);
    		INSERT INTO acc_trans
    		(trans_id, chart_id, amount, transdate, source, memo, approved,
    			voucher_id)
    		VALUES
    		(pay_row.trans_id, pay_row.ar_ap_account_id, pay_row.amount,
    			in_date_reversed, in_source, 'Reversing ' ||
    			COALESCE(in_source, ''),
    			case when in_batch_id is not null then false
    			else true end, t_voucher_id);
    		IF in_account_class = 1 THEN
    			UPDATE ap SET paid = amount -
    				(SELECT sum(a.amount)
    				FROM acc_trans a
    				JOIN chart c ON (a.chart_id = c.id)
    				WHERE c.link = 'AP'
    					AND trans_id = pay_row.trans_id
    				),
                                    force_closed = false
    			WHERE id = pay_row.trans_id;
    		ELSIF in_account_class = 2 THEN
    			update ar SET paid = amount -
    				(SELECT sum(a.amount)
    				FROM acc_trans a
    				JOIN chart c ON (a.chart_id = c.id)
    				WHERE c.link = 'AR'
    					AND trans_id = pay_row.trans_id
    				) * -1,
                                    force_closed = false
    			WHERE id = pay_row.trans_id;
    		ELSE
    			RAISE EXCEPTION 'Unknown account class for payments %',
    				in_account_class;
    		END IF;
    	END LOOP;
    	RETURN 1;
    END;

## Function: payment__search(in_account_class text, in_cash_accno date, in_credit_id date, in_date_to integer, in_date_from text, in_source integer)

### Returns: SET OF payment_record

### Language: PLPGSQL

This searches for payments. in_date_to and _date_from specify the acceptable date range. All other matches are exact except that null matches all values. Currently (and to support earlier data) we define a payment as a collection of acc_trans records against the same credit account and cash account, on the same day with the same source number, and optionally the same voucher id.

    DECLARE
    	out_row payment_record;
    BEGIN
    	FOR out_row IN
    		select sum(CASE WHEN c.entity_class = 1 then a.amount
    				ELSE a.amount * -1 END), c.meta_number,
    			c.id, co.legal_name,
    			compound_array(ARRAY[ARRAY[ch.id::text, ch.accno,
    				ch.description]]), a.source,
    			b.control_code, b.description, a.voucher_id, a.transdate
    		FROM entity_credit_account c
    		JOIN ( select entity_credit_account, id
    			FROM ar WHERE in_account_class = 2
    			UNION
    			SELECT entity_credit_account, id
    			FROM ap WHERE in_account_class = 1
    			) arap ON (arap.entity_credit_account = c.id)
    		JOIN acc_trans a ON (arap.id = a.trans_id)
    		JOIN chart ch ON (ch.id = a.chart_id)
    		JOIN company co ON (c.entity_id = co.entity_id)
    		LEFT JOIN voucher v ON (v.id = a.voucher_id)
    		LEFT JOIN batch b ON (b.id = v.batch_id)
    		WHERE (ch.accno = in_cash_accno)
    			AND (c.id = in_credit_id OR in_credit_id IS NULL)
    			AND (a.transdate >= in_date_from
    				OR in_date_from IS NULL)
    			AND (a.transdate <= in_date_to OR in_date_to IS NULL)
    			AND (source = in_source OR in_source IS NULL)
    		GROUP BY c.meta_number, c.id, co.legal_name, a.transdate,
    			a.source, a.memo, b.id, b.control_code, b.description,
                            voucher_id
    		ORDER BY a.transdate, c.meta_number, a.source
    	LOOP
    		RETURN NEXT out_row;
    	END LOOP;
    END;

## Function: payment_bulk_post(in_curr numeric[], in_exchangerate integer, in_account_class text, in_payment_date numeric, in_cash_accno text, in_ar_ap_accno text, in_total date, in_source integer, in_batch_id numeric, in_transactions text)

### Returns: integer

### Language: PLPGSQL

    DECLARE
    	out_count int;
    	t_voucher_id int;
    	t_trans_id int;
    	t_amount numeric;
            t_ar_ap_id int;
    	t_cash_id int;
            t_currs text[];
            t_exchangerate numeric;
    BEGIN
    	IF in_batch_id IS NULL THEN
    		-- t_voucher_id := NULL;
    		RAISE EXCEPTION 'Bulk Post Must be from Batch!';
    	ELSE
    		INSERT INTO voucher (batch_id, batch_class, trans_id)
    		values (in_batch_id,
                    (SELECT batch_class_id FROM batch WHERE id = in_batch_id),
                    in_transactions[1][1]);

    		t_voucher_id := currval('voucher_id_seq');
    	END IF;

    	SELECT string_to_array(value, ':') into t_currs
              from defaults
             where setting_key = 'curr';

            IF (in_curr IS NULL OR in_curr = t_currs[0]) THEN
                    t_exchangerate := 1;
            ELSE
                    t_exchangerate := in_exchangerate;
            END IF;

    	CREATE TEMPORARY TABLE bulk_payments_in (id int, amount numeric);

    	select id into t_ar_ap_id from chart where accno = in_ar_ap_accno;
    	select id into t_cash_id from chart where accno = in_cash_accno;

    	FOR out_count IN
    			array_lower(in_transactions, 1) ..
    			array_upper(in_transactions, 1)
    	LOOP
    		EXECUTE $E$
    			INSERT INTO bulk_payments_in(id, amount)
    			VALUES ($E$ || quote_literal(in_transactions[out_count][1])
    				|| $E$, $E$ ||
    				quote_literal(in_transactions[out_count][2])
    				|| $E$)$E$;
    	END LOOP;
    	EXECUTE $E$
    		INSERT INTO acc_trans
    			(trans_id, chart_id, amount, approved, voucher_id, transdate,
    			source)
    		SELECT id,
    		case when $E$ || quote_literal(in_account_class) || $E$ = 1
    			THEN $E$ || t_cash_id || $E$
    			WHEN $E$ || quote_literal(in_account_class) || $E$ = 2
    			THEN $E$ || t_ar_ap_id || $E$
    			ELSE -1 END,
    		amount * $E$|| quote_literal(t_exchangerate) || $E$,
    		CASE
    			WHEN $E$|| t_voucher_id || $E$ IS NULL THEN true
    			ELSE false END,
    		$E$ || t_voucher_id || $E$, $E$|| quote_literal(in_payment_date)
    		||$E$ , $E$ ||COALESCE(quote_literal(in_source), 'NULL') ||
    		$E$
    		FROM bulk_payments_in  where amount <> 0 $E$;

    	EXECUTE $E$
    		INSERT INTO acc_trans
    			(trans_id, chart_id, amount, approved, voucher_id, transdate,
    			source)
    		SELECT id,
    		case when $E$ || quote_literal(in_account_class) || $E$ = 1
    			THEN $E$ || t_ar_ap_id || $E$
    			WHEN $E$ || quote_literal(in_account_class) || $E$ = 2
    			THEN $E$ || t_cash_id || $E$
    			ELSE -1 END,
    		amount * -1 * $E$|| quote_literal(t_exchangerate) || $E$,
    		CASE
    			WHEN $E$|| t_voucher_id || $E$ IS NULL THEN true
    			ELSE false END,
    		$E$ || t_voucher_id || $E$, $E$|| quote_literal(in_payment_date)
    		||$E$ , $E$ ||COALESCE(quote_literal(in_source), 'null')
    		||$E$
    		FROM bulk_payments_in where amount <> 0 $E$;

            IF in_account_class = 1 THEN
            	EXECUTE $E$
    	        	UPDATE ap
    		        set paid = paid + (select amount from bulk_payments_in b
    		         	where b.id = ap.id),
                                datepaid = $E$ || quote_literal(in_payment_date) || $E$
    		         where id in (select id from bulk_payments_in) $E$;
            ELSE
            	EXECUTE $E$
    	        	UPDATE ar
    		        set paid = paid + (select amount from bulk_payments_in b
    		         	where b.id = ar.id),
                                datepaid = $E$ || quote_literal(in_payment_date) || $E$
    		         where id in (select id from bulk_payments_in) $E$;
            END IF;
    	EXECUTE $E$ DROP TABLE bulk_payments_in $E$;
    	perform unlock_all();
    	return out_count;
    END;

## Function: payment_gather_header_info(in_payment_id integer, in_account_class integer)

### Returns: SET OF payment_header_item

### Language: PLPGSQL

This function finds a payment based on the id and retrieves the record, it is usefull for printing payments :)

     DECLARE out_payment payment_header_item;
     BEGIN
     FOR out_payment IN
       SELECT p.id as payment_id, p.reference as payment_reference, p.payment_date,
              c.legal_name as legal_name, am.amount as amount, em.first_name, em.last_name, p.currency, p.notes
       FROM payment p
       JOIN entity_employee ent_em ON (ent_em.entity_id = p.employee_id)
       JOIN person em ON (ent_em.entity_id = em.entity_id)
       JOIN entity_credit_account eca ON (eca.id = p.entity_credit_id)
       JOIN company c ON   (c.entity_id  = eca.entity_id)
       JOIN payment_links pl ON (p.id = pl.payment_id)
       LEFT JOIN (  SELECT sum(a.amount) as amount
     		FROM acc_trans a
     		JOIN account acc ON (a.chart_id = acc.id)
                    JOIN account_link al ON (acc.id =al.account_id)
     		JOIN payment_links pl ON (pl.entry_id=a.entry_id)
     		WHERE al.description in
                           ('AP_paid', 'AP_discount', 'AR_paid', 'AR_discount')
                           and ((in_account_class = 1 AND al.description like 'AP%')
                           or (in_account_class = 2 AND al.description like 'AR%'))
                 ) am ON (true)
       WHERE p.id = in_payment_id
     LOOP
         RETURN NEXT out_payment;
     END LOOP;

     END;

## Function: payment_gather_line_info(in_payment_id integer, in_account_class integer)

### Returns: SET OF payment_line_item

### Language: PLPGSQL

This function finds a payment based on the id and retrieves all the line records, it is usefull for printing payments and build reports :)

     DECLARE out_payment_line payment_line_item;
     BEGIN
       FOR out_payment_line IN
         SELECT pl.payment_id, ac.entry_id, pl.type as link_type, ac.trans_id, a.invnumber as invoice_number,
         ac.chart_id, ch.accno as chart_accno, ch.description as chart_description, ch.link as chart_link,
         ac.amount,  ac.transdate as trans_date, ac.source, ac.cleared_on, ac.fx_transaction, ac.project_id,
         ac.memo, ac.invoice_id, ac.approved, ac.cleared_on, ac.reconciled_on
         FROM acc_trans ac
         JOIN payment_links pl ON (pl.entry_id = ac.entry_id )
         JOIN chart         ch ON (ch.id = ac.chart_id)
         LEFT JOIN (SELECT id,invnumber
                     FROM ar WHERE in_account_class = 2
                     UNION
                     SELECT id,invnumber
                     FROM ap WHERE in_account_class = 1
                    ) a ON (ac.trans_id = a.id)
         WHERE pl.payment_id = in_payment_id
       LOOP
          RETURN NEXT out_payment_line;
       END LOOP;
     END;

## Function: payment_get_all_accounts(in_account_class integer)

### Returns: SET OF entity

### Language: PLPGSQL

This function takes a single argument (1 for vendor, 2 for customer as always) and returns all entities with accounts of the appropriate type.

    DECLARE out_entity entity%ROWTYPE;
    BEGIN
    	FOR out_entity IN
    		SELECT  ec.id,
    			e.name, e.entity_class, e.created
    		FROM entity e
    		JOIN entity_credit_account ec ON (ec.entity_id = e.id)
    				WHERE e.entity_class = in_account_class
    	LOOP
    		RETURN NEXT out_entity;
    	END LOOP;
    END;

## Function: payment_get_all_contact_invoices(in_meta_number integer, in_ar_ap_accno integer, in_batch_id bpchar, in_date_to date, in_date_from date, in_currency integer, in_business_id text, in_account_class text)

### Returns: SET OF payment_contact_invoice

### Language: PLPGSQL

This function takes the following arguments (all prefaced with in_ in the db): account_class: 1 for vendor, 2 for customer business_type: integer of business.id. currency: char(3) of currency (for example 'USD') date_from, date_to: These dates are inclusive. batch_id: For payment batches, where fees are concerned. ar_ap_accno: The AR/AP account number. This then returns a set of contact information with a 2 dimensional array cnsisting of outstanding invoices. Note that the payment selection logic is that this returns all invoices which are either approved or in the batch_id specified. It also locks the invoices using the LedgerSMB discretionary locking framework, and if not possible, returns the username of the individual who has the lock.

    DECLARE payment_item payment_contact_invoice;
    BEGIN
    	FOR payment_item IN
    		  SELECT c.id AS contact_id, e.control_code as econtrol_code,
    			c.description as eca_description,
    			e.name AS contact_name,
    		         c.meta_number AS account_number,
    			 sum( case when u.username IS NULL or
    				       u.username = SESSION_USER
    			     THEN
    		              coalesce(p.due::numeric, 0) -
    		              CASE WHEN c.discount_terms
    		                        > extract('days' FROM age(a.transdate))
    		                   THEN 0
    		                   ELSE (coalesce(p.due::numeric, 0)) *
    					coalesce(c.discount::numeric, 0) / 100
    		              END
    			     ELSE 0::numeric
    			     END) AS total_due,
    		         compound_array(ARRAY[[
    		              a.id::text, a.invnumber, a.transdate::text,
    		              a.amount::text, (a.amount - p.due)::text,
    		              (CASE WHEN c.discount_terms
    		                        > extract('days' FROM age(a.transdate))
    		                   THEN 0
    		                   ELSE (a.amount - coalesce((a.amount - p.due), 0)) * coalesce(c.discount, 0) / 100
    		              END)::text,
    		              (coalesce(p.due, 0) -
    		              (CASE WHEN c.discount_terms
    		                        > extract('days' FROM age(a.transdate))
    		                   THEN 0
    		                   ELSE (coalesce(p.due, 0)) * coalesce(c.discount, 0) / 100
    		              END))::text,
    			 	case when u.username IS NOT NULL
    				          and u.username <> SESSION_USER
    				     THEN 0::text
    				     ELSE 1::text
    				END,
    				COALESCE(u.username, 0::text)
    				]]),
                                  sum(case when a.batch_id = in_batch_id then 1
    		                  else 0 END),
    		              bool_and(lock_record(a.id, (select max(session_id) 				FROM "session" where users_id = (
    					select id from users WHERE username =
    					SESSION_USER))))

    		    FROM entity e
    		    JOIN entity_credit_account c ON (e.id = c.entity_id)
    		    JOIN (SELECT ap.id, invnumber, transdate, amount, entity_id,
    				 paid, curr, 1 as invoice_class,
    		                 entity_credit_account, on_hold, v.batch_id,
    				 approved
    		            FROM ap
    		       LEFT JOIN (select * from voucher where batch_class = 1) v
    			         ON (ap.id = v.trans_id)
    			   WHERE in_account_class = 1
    			         AND (v.batch_class = 1 or v.batch_id IS NULL)
    		           UNION
    		          SELECT ar.id, invnumber, transdate, amount, entity_id,
    		                 paid, curr, 2 as invoice_class,
    		                 entity_credit_account, on_hold, v.batch_id,
    				 approved
    		            FROM ar
    		       LEFT JOIN (select * from voucher where batch_class = 2) v
    			         ON (ar.id = v.trans_id)
    			   WHERE in_account_class = 2
    			         AND (v.batch_class = 2 or v.batch_id IS NULL)
    			ORDER BY transdate
    		         ) a ON (a.entity_credit_account = c.id)
    		    JOIN transactions t ON (a.id = t.id)
    		    JOIN (SELECT acc_trans.trans_id,
    		                 sum(CASE WHEN in_account_class = 1 THEN amount
    		                          WHEN in_account_class = 2
    		                          THEN amount * -1
    		                     END) AS due
    		            FROM acc_trans
    		            JOIN account coa ON (coa.id = acc_trans.chart_id)
                                JOIN account_link al ON (al.account_id = coa.id)
    		       LEFT JOIN voucher v ON (acc_trans.voucher_id = v.id)
    		           WHERE ((al.description = 'AP' AND in_account_class = 1)
    		                 OR (al.description = 'AR' AND in_account_class = 2))
    			   AND (approved IS TRUE or v.batch_class IN (3, 6))
    		        GROUP BY acc_trans.trans_id) p ON (a.id = p.trans_id)
    		LEFT JOIN "session" s ON (s."session_id" = t.locked_by)
    		LEFT JOIN users u ON (u.id = s.users_id)
    		   WHERE (a.batch_id = in_batch_id
    		          OR (a.invoice_class = in_account_class
    		             AND a.approved
    			 AND (c.business_id =
    				coalesce(in_business_id, c.business_id)
    				OR in_business_id is null)
    		         AND ((a.transdate >= COALESCE(in_date_from, a.transdate)
    		               AND a.transdate <= COALESCE(in_date_to, a.transdate)))
    		         AND c.entity_class = in_account_class
    		         AND a.curr = in_currency
    		         AND a.entity_credit_account = c.id
    			 AND p.due <> 0
    		         AND a.amount <> a.paid
    			 AND NOT a.on_hold
    		         AND EXISTS (select trans_id FROM acc_trans
    		                      WHERE trans_id = a.id AND
    		                            chart_id = (SELECT id from account
    		                                         WHERE accno
    		                                               = in_ar_ap_accno)
    		                    )))
    		         AND (in_meta_number IS NULL OR
                                 in_meta_number = c.meta_number)
    		GROUP BY c.id, e.name, c.meta_number, c.threshold,
    			e.control_code, c.description
    		  HAVING  (sum(p.due) >= c.threshold
    			OR sum(case when a.batch_id = in_batch_id then 1
                                      else 0 END) > 0)
            ORDER BY c.meta_number ASC
    	LOOP
    		RETURN NEXT payment_item;
    	END LOOP;
    END;

## Function: payment_get_available_overpayment_amount(in_entity_credit_id integer, in_account_class integer)

### Returns: SET OF payment_overpayments_available_amount

### Language: PLPGSQL

    DECLARE out_overpayment payment_overpayments_available_amount;
    BEGIN
          FOR out_overpayment IN
                  SELECT chart_id, accno,   chart_description, abs(sum(available))
                  FROM overpayments
                  WHERE payment_class  = in_account_class
                  AND entity_credit_id = in_entity_credit_id
                  AND available <> 0
                  GROUP BY chart_id, accno, chart_description
          LOOP
               RETURN NEXT out_overpayment;
          END LOOP;
    END;

## Function: payment_get_entity_accounts(in_vc_idn integer, in_vc_name text, in_account_class text)

### Returns: SET OF payment_vc_info

### Language: PLPGSQL

Returns a minimal set of information about customer or vendor accounts as needed for discount calculations and the like.

     DECLARE out_entity payment_vc_info;

     BEGIN
     	FOR out_entity IN
                  SELECT ec.id, cp.legal_name ||
                         coalesce(':' || ec.description,'') as name,
                         e.entity_class, ec.discount_account_id, ec.meta_number
     		FROM entity_credit_account ec
     		JOIN entity e ON (ec.entity_id = e.id)
     		JOIN company cp ON (cp.entity_id = e.id)
    		WHERE ec.entity_class = in_account_class
    		AND (cp.legal_name ilike coalesce('%'||in_vc_name||'%','%%') OR cp.tax_id = in_vc_idn)
    	LOOP
    		RETURN NEXT out_entity;
    	END LOOP;
     END;

## Function: payment_get_open_accounts(in_account_class integer)

### Returns: SET OF entity

### Language: PLPGSQL

This function takes a single argument (1 for vendor, 2 for customer as always) and returns all entities with open accounts of the appropriate type.

    DECLARE out_entity entity%ROWTYPE;
    BEGIN
    	FOR out_entity IN
    		SELECT ec.id, cp.legal_name as name, e.entity_class, e.created
    		FROM entity e
    		JOIN entity_credit_account ec ON (ec.entity_id = e.id)
    		JOIN company cp ON (cp.entity_id = e.id)
    			WHERE ec.entity_class = in_account_class
                            AND CASE WHEN in_account_class = 1 THEN
    	           		ec.id IN (SELECT entity_credit_account FROM ap
    	           			WHERE amount <> paid
    		   			GROUP BY entity_credit_account)
    		    	       WHEN in_account_class = 2 THEN
    		   		ec.id IN (SELECT entity_credit_account FROM ar
    		   			WHERE amount <> paid
    		   			GROUP BY entity_credit_account)
    		   	  END
    	LOOP
    		RETURN NEXT out_entity;
    	END LOOP;
    END;

## Function: payment_get_open_invoice(in_invnumber integer, in_department_id integer, in_amountto bpchar, in_amountfrom date, in_dateto date, in_datefrom numeric, in_curr numeric, in_entity_credit_id integer, in_account_class text)

### Returns: SET OF payment_invoice

### Language: PLPGSQL

This function is based on payment_get_open_invoices and returns only one invoice if the in_invnumber is set. if no in_invnumber is passed this function behaves the same as payment_get_open_invoices

    DECLARE payment_inv payment_invoice;
    BEGIN
    	FOR payment_inv IN
    		SELECT * from payment_get_open_invoices(in_account_class, in_entity_credit_id, in_curr, in_datefrom, in_dateto, in_amountfrom,
    		in_amountto, in_department_id)
    		WHERE (invnumber like in_invnumber OR in_invnumber IS NULL)
    	LOOP
    		RETURN NEXT payment_inv;
    	END LOOP;
    END;

## Function: payment_get_open_invoices(in_department_id integer, in_amountto integer, in_amountfrom bpchar, in_dateto date, in_datefrom date, in_curr numeric, in_entity_credit_id numeric, in_account_class integer)

### Returns: SET OF payment_invoice

### Language: PLPGSQL

This function is the base for get_open_invoice and returns all open invoices for the entity_credit_id it has a lot of options to enable filtering and use the same logic for entity_class_id and currency.

    DECLARE payment_inv payment_invoice;
    BEGIN
    	FOR payment_inv IN
    		SELECT a.id AS invoice_id, a.invnumber AS invnumber,
    		       a.transdate AS invoice_date, a.amount AS amount,
    		       a.amount/
    		       (CASE WHEN a.curr = (SELECT * from defaults_get_defaultcurrency())
                             THEN 1
    		        ELSE
    		        (CASE WHEN in_account_class =1
    		              THEN ex.buy
    		              ELSE ex.sell END)
    		        END) as amount_fx,
    		       (CASE WHEN c.discount_terms < extract('days' FROM age(a.transdate))
    		        THEN 0
    		        ELSE (coalesce(ac.due, a.amount)) * coalesce(c.discount, 0) / 100
    		        END) AS discount,
    		        (CASE WHEN c.discount_terms < extract('days' FROM age(a.transdate))
    		        THEN 0
    		        ELSE (coalesce(ac.due, a.amount)) * coalesce(c.discount, 0) / 100
    		        END)/
    		        (CASE WHEN a.curr = (SELECT * from defaults_get_defaultcurrency())
                             THEN 1
    		        ELSE
    		        (CASE WHEN in_account_class =1
    		              THEN ex.buy
    		              ELSE ex.sell END)
    		        END) as discount_fx,
    		        ac.due - (CASE WHEN c.discount_terms < extract('days' FROM age(a.transdate))
    		        THEN 0
    		        ELSE (coalesce(ac.due, a.amount)) * coalesce(c.discount, 0) / 100
    		        END) AS due,
    		        (ac.due - (CASE WHEN c.discount_terms < extract('days' FROM age(a.transdate))
    		        THEN 0
    		        ELSE (coalesce(ac.due, a.amount)) * coalesce(c.discount, 0) / 100
    		        END))/
    		        (CASE WHEN a.curr = (SELECT * from defaults_get_defaultcurrency())
                             THEN 1
    		         ELSE
    		         (CASE WHEN in_account_class =1
    		              THEN ex.buy
    		              ELSE ex.sell END)
    		         END) AS due_fx,
    		        (CASE WHEN a.curr = (SELECT * from defaults_get_defaultcurrency())
    		         THEN 1
    		         ELSE
    		        (CASE WHEN in_account_class =1
    		         THEN ex.buy
    		         ELSE ex.sell END)
    		         END) AS exchangerate
                     FROM  (SELECT id, invnumber, transdate, amount, entity_id,
    		               1 as invoice_class, paid, curr,
    		               entity_credit_account, department_id, approved
    		          FROM ap
                             UNION
    		         SELECT id, invnumber, transdate, amount, entity_id,
    		               2 AS invoice_class, paid, curr,
    		               entity_credit_account, department_id, approved

    		         FROM ar
    		         ) a
    		JOIN (SELECT trans_id, chart_id, sum(CASE WHEN in_account_class = 1 THEN amount
    		                                  WHEN in_account_class = 2
    		                             THEN amount * -1
    		                             END) as due
    		        FROM acc_trans
    		        GROUP BY trans_id, chart_id) ac ON (ac.trans_id = a.id)
    		        JOIN chart ON (chart.id = ac.chart_id)
    		        LEFT JOIN exchangerate ex ON ( ex.transdate = a.transdate AND ex.curr = a.curr )
    		        JOIN entity_credit_account c ON (c.id = a.entity_credit_account
                            OR (a.entity_credit_account IS NULL and a.entity_id = c.entity_id))
    	 	        WHERE ((chart.link = 'AP' AND in_account_class = 1)
    		              OR (chart.link = 'AR' AND in_account_class = 2))
                  	        AND a.invoice_class = in_account_class
    		        AND c.entity_class = in_account_class
    		        AND c.id = in_entity_credit_id
    		        AND a.amount - a.paid <> 0
    		        AND a.curr = in_curr
    		        AND (a.transdate >= in_datefrom
    		             OR in_datefrom IS NULL)
    		        AND (a.transdate <= in_dateto
    		             OR in_dateto IS NULL)
    		        AND (a.amount >= in_amountfrom
    		             OR in_amountfrom IS NULL)
    		        AND (a.amount <= in_amountto
    		             OR in_amountto IS NULL)
    		        AND (a.department_id = in_department_id
    		             OR in_department_id IS NULL)
    		        AND due <> 0
    		        AND a.approved = true
    		        GROUP BY a.invnumber, a.transdate, a.amount, amount_fx, discount, discount_fx, ac.due, a.id, c.discount_terms, ex.buy, ex.sell, a.curr
    	LOOP
    		RETURN NEXT payment_inv;
    	END LOOP;
    END;

## Function: payment_get_open_overpayment_entities(in_account_class integer)

### Returns: SET OF payment_vc_info

### Language: PLPGSQL

     DECLARE out_entity payment_vc_info;
     BEGIN
    	FOR out_entity IN
        		SELECT DISTINCT entity_credit_id, legal_name, e.entity_class, discount, o.meta_number
        		FROM overpayments o
        		JOIN entity e ON (e.id=o.entity_id)
        		WHERE available <> 0 AND in_account_class = payment_class
            LOOP
                    RETURN NEXT out_entity;
            END LOOP;
     END;

## Function: payment_get_unused_overpayment(in_chart_id integer, in_entity_credit_id integer, in_account_class integer)

### Returns: SET OF overpayments

### Language: PLPGSQL

Returns a list of available overpayments

    DECLARE out_overpayment overpayments%ROWTYPE;
    BEGIN
          FOR out_overpayment IN
                  SELECT DISTINCT *
                  FROM overpayments
                  WHERE payment_class  = in_account_class
                  AND entity_credit_id = in_entity_credit_id
                  AND available <> 0
                  AND (in_chart_id IS NULL OR chart_id = in_chart_id )
                  ORDER BY payment_date

          LOOP
               RETURN NEXT out_overpayment;
          END LOOP;
    END;

## Function: payment_get_vc_info(in_location_class_id integer, in_entity_credit_id integer)

### Returns: SET OF payment_location_result

### Language: PLPGSQL

This function returns vendor or customer info

    DECLARE out_row payment_location_result;
    	BEGIN
    		FOR out_row IN
                    SELECT l.id, l.line_one, l.line_two, l.line_three, l.city,
                           l.state, l.mail_code, c.name, lc.class
                    FROM location l
                    JOIN company_to_location ctl ON (ctl.location_id = l.id)
                    JOIN company cp ON (ctl.company_id = cp.id)
                    JOIN location_class lc ON (ctl.location_class = lc.id)
                    JOIN country c ON (c.id = l.country_id)
                    JOIN entity_credit_account ec ON (ec.entity_id = cp.entity_id)
                    WHERE ec.id = in_entity_credit_id AND
                          lc.id = in_location_class_id
                    ORDER BY lc.id, l.id, c.name
                    LOOP
                    	RETURN NEXT out_row;
    		END LOOP;
    	END;

## Function: payment_post(in_approved date, in_ovp_payment_id integer, in_op_account_id integer, in_op_memo bpchar, in_op_source text, in_op_cash_account_id integer, in_op_amount text, in_transaction_id integer[], in_memo numeric[], in_source boolean[], in_cash_approved text[], in_amount text[], in_cash_account_id integer[], in_gl_description numeric[], in_department_id integer[], in_notes text[], in_curr text[], in_entity_credit_id integer[], in_account_class integer[], in_datepaid boolean)

### Returns: integer

### Language: PLPGSQL

Posts a payment. in_op_* arrays are cross-indexed with eachother. Other arrays are cross-indexed with eachother. This API will probably change in 1.4 as we start looking at using more custom complex types and arrays of those (requires Pg 8.4 or higher).

    DECLARE var_payment_id int;
    DECLARE var_gl_id int;
    DECLARE var_entry record;
    DECLARE var_entry_id int[];
    DECLARE out_count int;
    DECLARE coa_id record;
    DECLARE var_employee int;
    DECLARE var_account_id int;
    DECLARE default_currency char(3);
    DECLARE current_exchangerate numeric;
    DECLARE old_exchangerate numeric;
    DECLARE tmp_amount numeric;
    BEGIN

            SELECT * INTO default_currency  FROM defaults_get_defaultcurrency();
            SELECT * INTO current_exchangerate FROM currency_get_exchangerate(in_curr, in_datepaid, in_account_class);

            SELECT INTO var_employee p.id
            FROM users u
            JOIN person p ON (u.entity_id=p.entity_id)
            WHERE username = SESSION_USER LIMIT 1;
            --
            -- WE HAVE TO INSERT THE PAYMENT, USING THE GL INFORMATION
            -- THE ID IS GENERATED BY payment_id_seq
            --
       	INSERT INTO payment (reference, payment_class, payment_date,
    	                      employee_id, currency, notes, department_id, entity_credit_id)
    	VALUES ((CASE WHEN in_account_class = 1 THEN
    	                                setting_increment('rcptnumber') -- I FOUND THIS ON sql/modules/Settings.sql
    			             ELSE 						-- and it is very usefull
    			                setting_increment('paynumber')
    			             END),
    	         in_account_class, in_datepaid, var_employee,
                     in_curr, in_notes, in_department_id, in_entity_credit_id);
            SELECT currval('payment_id_seq') INTO var_payment_id; -- WE'LL NEED THIS VALUE TO USE payment_link table
            -- WE'LL NEED THIS VALUE TO JOIN WITH PAYMENT
            -- NOW COMES THE HEAVY PART, STORING ALL THE POSSIBLE TRANSACTIONS...
            --
            -- FIRST WE SHOULD INSERT THE CASH ACCOUNTS
            --
            -- WE SHOULD HAVE THE DATA STORED AS (ACCNO, AMOUNT), SO
         IF (array_upper(in_cash_account_id, 1) > 0) THEN
    	FOR out_count IN
    			array_lower(in_cash_account_id, 1) ..
    			array_upper(in_cash_account_id, 1)
    	LOOP
    	        INSERT INTO acc_trans (chart_id, amount,
    		                       trans_id, transdate, approved, source, memo)
    		VALUES (in_cash_account_id[out_count],
    		        CASE WHEN in_account_class = 1 THEN in_amount[out_count]*current_exchangerate
    		        ELSE (in_amount[out_count]*current_exchangerate)* - 1
    		        END,
    		        in_transaction_id[out_count], in_datepaid, coalesce(in_approved, true),
    		        in_source[out_count], in_memo[out_count]);
                    INSERT INTO payment_links
    		VALUES (var_payment_id, currval('acc_trans_entry_id_seq'), 1);
    		IF (in_ovp_payment_id IS NOT NULL AND in_ovp_payment_id[out_count] IS NOT NULL) THEN
                    	INSERT INTO payment_links
                    	VALUES (in_ovp_payment_id[out_count], currval('acc_trans_entry_id_seq'), 0);
    		END IF;

    	END LOOP;
    	-- NOW LETS HANDLE THE AR/AP ACCOUNTS
    	-- WE RECEIVED THE TRANSACTIONS_ID AND WE CAN OBTAIN THE ACCOUNT FROM THERE
    	FOR out_count IN
    		     array_lower(in_transaction_id, 1) ..
    		     array_upper(in_transaction_id, 1)
           LOOP
                   SELECT INTO var_account_id chart_id FROM acc_trans as ac
    	        JOIN chart as c ON (c.id = ac.chart_id)
           	        WHERE
           	        trans_id = in_transaction_id[out_count] AND
           	        ( c.link = 'AP' OR c.link = 'AR' );
            -- We need to know the exchangerate of this transaction
            IF (current_exchangerate = 1 ) THEN
               old_exchangerate := 1;
            ELSIF (in_account_class = 1) THEN
               SELECT buy INTO old_exchangerate
               FROM exchangerate e
               JOIN ap a on (a.transdate = e.transdate )
               WHERE a.id = in_transaction_id[out_count];
            ELSE
               SELECT sell INTO old_exchangerate
               FROM exchangerate e
               JOIN ar a on (a.transdate = e.transdate )
               WHERE a.id = in_transaction_id[out_count];
            END IF;
            -- Now we post the AP/AR transaction
            INSERT INTO acc_trans (chart_id, amount,
                                    trans_id, transdate, approved, source, memo)
    		VALUES (var_account_id,
    		        CASE WHEN in_account_class = 1 THEN

    		        (in_amount[out_count]*old_exchangerate) * -1
    		        ELSE in_amount[out_count]*old_exchangerate
    		        END,
    		        in_transaction_id[out_count], in_datepaid,  coalesce(in_approved, true),
    		        in_source[out_count], in_memo[out_count]);
            -- Lets set the gain/loss, if tmp_amount equals zero then we dont need to post
            -- any transaction
            tmp_amount := in_amount[out_count]*current_exchangerate - in_amount[out_count]*old_exchangerate;
           IF (tmp_amount < 0) THEN
              IF (in_account_class  = 1) THEN
               INSERT INTO acc_trans (chart_id, amount, trans_id, transdate, approved, source)
                VALUES (CAST((select value from defaults where setting_key like 'fxloss_accno_id') AS INT),
                        tmp_amount, in_transaction_id[out_count], in_datepaid, coalesce(in_approved, true),
                        in_source[out_count]);
               ELSE
                INSERT INTO acc_trans (chart_id, amount, trans_id, transdate, approved, source)
                VALUES (CAST((select value from defaults where setting_key like 'fxgain_accno_id') AS INT),
                        tmp_amount, in_transaction_id[out_count], in_datepaid, coalesce(in_approved, true),
                        in_source[out_count]);
              END IF;
            ELSIF (tmp_amount > 0) THEN
              IF (in_account_class  = 1) THEN
                INSERT INTO acc_trans (chart_id, amount, trans_id, transdate, approved, source)
                VALUES (CAST((select value from defaults where setting_key like 'fxgain_accno_id') AS INT),
                        tmp_amount, in_transaction_id[out_count], in_datepaid, coalesce(in_approved, true),
                        in_source[out_count]);
               ELSE
                INSERT INTO acc_trans (chart_id, amount, trans_id, transdate, approved, source)
                VALUES (CAST((select value from defaults where setting_key like 'fxloss_accno_id') AS INT),
                        tmp_amount, in_transaction_id[out_count], in_datepaid, coalesce(in_approved, true),
                        in_source[out_count]);
              END IF;
            END IF;
            -- Now we set the links
             INSERT INTO payment_links
    		VALUES (var_payment_id, currval('acc_trans_entry_id_seq'), 1);
          END LOOP;
         END IF; -- END IF
    --
    -- WE NEED TO HANDLE THE OVERPAYMENTS NOW
    --
           --
           -- FIRST WE HAVE TO MAKE THE GL TO HOLD THE OVERPAYMENT TRANSACTIONS
           -- THE ID IS GENERATED BY gl_id_seq
           --

      IF (array_upper(in_op_cash_account_id, 1) > 0) THEN
           INSERT INTO gl (reference, description, transdate,
                           person_id, notes, approved, department_id)
                  VALUES (setting_increment('glnumber'),
    	              in_gl_description, in_datepaid, var_employee,
    	              in_notes, in_approved, in_department_id);
           SELECT currval('id') INTO var_gl_id;
    --
    -- WE NEED TO SET THE GL_ID FIELD ON PAYMENT'S TABLE
    --
           UPDATE payment SET gl_id = var_gl_id
           WHERE id = var_payment_id;
           -- NOW COMES THE HEAVY PART, STORING ALL THE POSSIBLE TRANSACTIONS...
           --
           -- FIRST WE SHOULD INSERT THE OVERPAYMENT CASH ACCOUNTS
           --
    	FOR out_count IN
    			array_lower(in_op_cash_account_id, 1) ..
    			array_upper(in_op_cash_account_id, 1)
    	LOOP
    	        INSERT INTO acc_trans (chart_id, amount,
    		                       trans_id, transdate, approved, source, memo)
    		VALUES (in_op_cash_account_id[out_count],
    		        CASE WHEN in_account_class = 1 THEN in_op_amount[out_count]
    		        ELSE in_op_amount[out_count] * - 1
    		        END,
    		        var_gl_id, in_datepaid, coalesce(in_approved, true),
    		        in_op_source[out_count], in_op_memo[out_count]);
    	        INSERT INTO payment_links
    		VALUES (var_payment_id, currval('acc_trans_entry_id_seq'), 2);

    	END LOOP;
    	-- NOW LETS HANDLE THE OVERPAYMENT ACCOUNTS
    	FOR out_count IN
    		     array_lower(in_op_account_id, 1) ..
    		     array_upper(in_op_account_id, 1)
    	LOOP
             INSERT INTO acc_trans (chart_id, amount,
                                    trans_id, transdate, approved, source, memo)
    		VALUES (in_op_account_id[out_count],
    		        CASE WHEN in_account_class = 1 THEN in_op_amount[out_count] * -1
    		        ELSE in_op_amount[out_count]
    		        END,
    		        var_gl_id, in_datepaid,  coalesce(in_approved, true),
    		        in_op_source[out_count], in_op_memo[out_count]);
    		INSERT INTO payment_links
    		VALUES (var_payment_id, currval('acc_trans_entry_id_seq'), 2);
    	END LOOP;
     END IF;
     return var_payment_id;
    END;

## Function: payment_type__get_label(in_payment_type_id integer)

### Returns: SET OF payment_type

### Language: PLPGSQL

Returns all information on a payment type by the id. This should be renamed to account for its behavior in future versions.

    DECLARE out_row payment_type%ROWTYPE;
    BEGIN
    	FOR out_row IN SELECT * FROM payment_type where id=in_payment_type_id LOOP
    		RETURN NEXT out_row;
    	END LOOP;
    END;

## Function: payment_type__list()

### Returns: SET OF payment_type

### Language: PLPGSQL

    DECLARE out_row payment_type%ROWTYPE;
    BEGIN
    	FOR out_row IN SELECT * FROM payment_type LOOP
    		RETURN NEXT out_row;
    	END LOOP;
    END;

## Function: payments_get_open_currencies(in_account_class integer)

### Returns: SET OF bpchar

### Language: PLPGSQL

This does a sparse scan to find currencies attached to open invoices. It should scale per the number of currencies used rather than the size of the ar or ap tables.

    DECLARE result char(3);
    BEGIN
    select min(curr) into result from ar WHERE in_account_class = 2
    union
    select min(curr) from ap WHERE in_account_class = 1;

    LOOP
       EXIT WHEN result IS NULL;
       return next result;

       SELECT min(curr) INTO result from ar
        where in_account_class = 2 and curr > result
                union
       select min(curr) from ap
        WHERE in_account_class = 1 and curr > result
        LIMIT 1;

    END LOOP;
    END;

## Function: payments_set_exchangerate(in_datepaid integer, in_curr numeric, in_exchangerate bpchar, in_account_class date)

### Returns: integer

### Language: PLPGSQL

1.3 only. This will be replaced by a more generic function in 1.4. This sets the exchange rate for a class of transactions (payable, receivable) to a certain rate for a specific date.

    DECLARE current_exrate  exchangerate%ROWTYPE;
    BEGIN
    select  * INTO current_exrate
            FROM  exchangerate
            WHERE transdate = in_date;
    IF current_exrate.transdate = in_date THEN
       IF in_account_class = 1 THEN
          UPDATE exchangerate set buy = in_exchangerate  where transdate = in_date;
       ELSE
          UPDATE exchangerate set sell = in_exchangerate where transdate = in_date;
       END IF;
       RETURN 0;
    ELSE
        IF in_account_class = 1 THEN
         INSERT INTO exchangerate (curr, transdate, buy) values (in_currency, in_date, in_exchangerate);
      ELSE
         INSERT INTO exchangerate (curr, transdate, sell) values (in_currency, in_date, in_exchangerate);
      END IF;
    RETURN 0;
    END IF;
    END;

## Function: periods_get()

### Returns: SET OF periods

### Language: SQL

Returns dates for year to date, and last year.

    SELECT * FROM periods ORDER BY id

## Function: person__delete_contact(in_contact integer, in_contact_class_id integer, in_person_id text)

### Returns: boolean

### Language: PLPGSQL

Deletes a contact record specified for the person. Returns true if a record was found and deleted, false if not.

    BEGIN

    DELETE FROM person_to_contact
     WHERE person_id = in_person_id and contact_class_id = in_contact_class_id
           and contact= in_contact;
    RETURN FOUND;

    END;

## Function: person__delete_location(in_location_class integer, in_location_id integer, in_person_id integer)

### Returns: boolean

### Language: PLPGSQL

Deletes a location mapping to a person. Returns true if found, false if no data deleted.

    BEGIN

    DELETE FROM person_to_location
     WHERE person_id = in_person_id AND location_id = in_location_id
           AND location_class = in_location_class;

    RETURN FOUND;

    END;

## Function: person__get_my_entity_id()

### Returns: integer

### Language: SQL

Returns the entity_id of the current, logged in user.

    	SELECT entity_id from users where username = SESSION_USER;

## Function: person__list_bank_account(in_entity_id integer)

### Returns: SET OF entity_bank_account

### Language: PLPGSQL

Lists bank accounts for a person

    DECLARE out_row entity_bank_account%ROWTYPE;
    BEGIN
    	FOR out_row IN
    		SELECT * from entity_bank_account where entity_id = in_entity_id
    	LOOP
    		RETURN NEXT out_row;
    	END LOOP;
    END;

## Function: person__list_contacts(in_entity_id integer)

### Returns: SET OF contact_list

### Language: PLPGSQL

Returns a list of contacts attached to the function.

    DECLARE out_row RECORD;
    BEGIN
    	FOR out_row IN
    		SELECT cc.class, cc.id, c.description, c.contact
    		FROM person_to_contact c
    		JOIN contact_class cc ON (c.contact_class_id = cc.id)
    		JOIN person p ON (c.person_id = p.id)
    		WHERE p.entity_id = in_entity_id
    	LOOP
    		RETURN NEXT out_row;
    	END LOOP;
    END;

## Function: person__list_locations(in_entity_id integer)

### Returns: SET OF location_result

### Language: PLPGSQL

Returns a list of locations specified attached to the person.

    DECLARE out_row RECORD;
    BEGIN
    	FOR out_row IN
    		SELECT l.id, l.line_one, l.line_two, l.line_three, l.city,
    			l.state, l.mail_code, c.id, c.name, lc.id, lc.class
    		FROM location l
    		JOIN person_to_location ctl ON (ctl.location_id = l.id)
    		JOIN person p ON (ctl.person_id = p.id)
    		JOIN location_class lc ON (ctl.location_class = lc.id)
    		JOIN country c ON (c.id = l.country_id)
    		WHERE p.entity_id = in_entity_id
    		ORDER BY lc.id, l.id, c.name
    	LOOP
    		RETURN NEXT out_row;
    	END LOOP;
    END;

## Function: person__list_notes(in_entity_id integer)

### Returns: SET OF entity_note

### Language: PLPGSQL

Returns a list of notes attached to a person.

    DECLARE out_row record;
    BEGIN
    	FOR out_row IN
    		SELECT *
    		FROM entity_note
    		WHERE ref_key = in_entity_id
    		ORDER BY created
    	LOOP
    		RETURN NEXT out_row;
    	END LOOP;
    END;

## Function: person__list_salutations()

### Returns: SET OF salutation

### Language: SQL

Returns a list of salutations ordered by id.

     SELECT * FROM salutation ORDER BY id ASC

## Function: person__save(in_country_id integer, in_last_name integer, in_middle_name text, in_first_name text, in_salutation_id text, in_entity_id integer)

### Returns: integer

### Language: PLPGSQL

Saves the person with the information specified. Returns the entity_id of the record saved.

        DECLARE
            e_id int;
            e entity;
            loc location;
            l_id int;
            p_id int;
        BEGIN

        select * into e from entity where id = in_entity_id and entity_class = 3;
        e_id := in_entity_id;

        IF NOT FOUND THEN
            INSERT INTO entity (name, entity_class, country_id)
    	values (in_first_name || ' ' || in_last_name, 3, in_country_id);
    	e_id := currval('entity_id_seq');

        END IF;

        UPDATE person SET
                salutation_id = in_salutation_id,
                first_name = in_first_name,
                last_name = in_last_name,
                middle_name = in_middle_name
        WHERE
                entity_id = in_entity_id;
        IF FOUND THEN
    	RETURN in_entity_id;
        ELSE
            -- Do an insert

            INSERT INTO person (salutation_id, first_name, last_name, entity_id)
    	VALUES (in_salutation_id, in_first_name, in_last_name, e_id);

            RETURN e_id;

        END IF;
    END;

## Function: person__save_contact(in_contact_new integer, in_contact_orig integer, in_contact_class text, in_entity_id text)

### Returns: integer

### Language: PLPGSQL

Saves saves contact info. Returns 1 if a row was inserted, 0 if it was updated.

    DECLARE
        out_id int;
        v_orig person_to_contact;
    BEGIN

        SELECT cc.* into v_orig
        FROM person_to_contact cc, person p
        WHERE p.entity_id = in_entity_id
        and cc.contact_class_id = in_contact_class
        AND cc.contact = in_contact_orig
        AND cc.person_id = p.id;

        IF NOT FOUND THEN

            -- create
            INSERT INTO person_to_contact(person_id, contact_class_id, contact)
            VALUES (
                (SELECT id FROM person WHERE entity_id = in_entity_id),
                in_contact_class,
                in_contact_new
            );
            return 1;
        ELSE
            -- edit.
            UPDATE person_to_contact
            SET contact = in_contact_new
            WHERE
            contact = in_contact_orig
            AND person_id = v_orig.person_id
            AND contact_class = in_contact_class;
            return 0;
        END IF;

    END;

## Function: person__save_location(in_old_location_class integer, in_country_code integer, in_mail_code integer, in_state text, in_city text, in_line_three text, in_line_two text, in_line_one text, in_location_class text, in_location_id integer, in_entity_id integer)

### Returns: integer

### Language: PLPGSQL

Saves a location mapped to the person with the specified information. Returns the location id saved.

        DECLARE
            l_row location;
            l_id INT;
    	    t_person_id int;
        BEGIN
    	SELECT id INTO t_person_id
    	FROM person WHERE entity_id = in_entity_id;

        UPDATE person_to_location
           SET location_class = in_location_class
         WHERE person_id = t_person_id
               AND location_class = in_old_location_class
               AND location_id = in_location_id;

        IF NOT FOUND THEN
            -- Create a new one.
            l_id := location_save(
                in_location_id,
        	    in_line_one,
        	    in_line_two,
        	    in_line_three,
        	    in_city,
        		in_state,
        		in_mail_code,
        		in_country_code);

            INSERT INTO person_to_location
        		(person_id, location_id, location_class)
        	VALUES  (t_person_id, l_id, in_location_class);
        ELSE
            l_id := location_save(
                in_location_id,
        	    in_line_one,
        	    in_line_two,
        	    in_line_three,
        	    in_city,
        		in_state,
        		in_mail_code,
        		in_country_code);
            -- Update the old one.
        END IF;
        return l_id;
        END;

## Function: plainto_tsquery(oid, text)

### Returns: tsquery

### Language: INTERNAL

    plainto_tsquery_byid

## Function: plainto_tsquery(text)

### Returns: tsquery

### Language: INTERNAL

    plainto_tsquery

## Function: plainto_tsquery(text, text)

### Returns: tsquery

### Language: C

    tsa_plainto_tsquery_name

## Function: project_list_open(in_date date)

### Returns: SET OF project

### Language: PLPGSQL

This function returns all projects that were open as on the date provided as the argument.

    DECLARE out_project project%ROWTYPE;
    BEGIN
    	FOR out_project IN
    		SELECT * from project
    		WHERE startdate <= in_date AND enddate >= in_date
    		      AND completed = 0
    	LOOP
    		return next out_project;
    	END LOOP;
    END;

## Function: prsd_end(internal)

### Returns: void

### Language: C

    tsa_prsd_end

## Function: prsd_getlexeme(internal, internal, internal)

### Returns: integer

### Language: C

    tsa_prsd_getlexeme

## Function: prsd_headline(internal, internal, internal)

### Returns: internal

### Language: C

    tsa_prsd_headline

## Function: prsd_lextype(internal)

### Returns: internal

### Language: C

    tsa_prsd_lextype

## Function: prsd_start(internal, integer)

### Returns: internal

### Language: C

    tsa_prsd_start

## Function: querytree(tsquery)

### Returns: text

### Language: INTERNAL

    tsquerytree

## Function: rank(real[], tsvector, tsquery)

### Returns: real

### Language: INTERNAL

    ts_rank_wtt

## Function: rank(real[], tsvector, tsquery, integer)

### Returns: real

### Language: INTERNAL

    ts_rank_wttf

## Function: rank(tsvector, tsquery)

### Returns: real

### Language: INTERNAL

    ts_rank_tt

## Function: rank(tsvector, tsquery, integer)

### Returns: real

### Language: INTERNAL

    ts_rank_ttf

## Function: rank_cd(real[], tsvector, tsquery)

### Returns: real

### Language: INTERNAL

    ts_rankcd_wtt

## Function: rank_cd(real[], tsvector, tsquery, integer)

### Returns: real

### Language: INTERNAL

    ts_rankcd_wttf

## Function: rank_cd(tsvector, tsquery)

### Returns: real

### Language: INTERNAL

    ts_rankcd_tt

## Function: rank_cd(tsvector, tsquery, integer)

### Returns: real

### Language: INTERNAL

    ts_rankcd_ttf

## Function: reconciliation__account_list()

### Returns: SET OF recon_accounts

### Language: SQL

returns set of accounts set up for reconciliation. Currently we pull the account number and description from the account table.

        SELECT
            coa.accno || ' ' || coa.description as name,
            coa.accno, coa.id as id
        FROM account coa
             JOIN cr_coa_to_account cta ON cta.chart_id = coa.id
        ORDER BY coa.accno;

## Function: reconciliation__add_entry(in_amount integer, in_date text, in_type text, in_scn timestamp without time zone, in_report_id numeric)

### Returns: integer

### Language: PLPGSQL

This function is used for automatically matching entries from an external source like a bank-produced csv file. This function is very sensitive to ordering of inputs. NULL or empty in_scn values MUST be submitted after meaningful scns. It is also highly recommended that within each category, one submits in order of amount. We should therefore wrap it in another function which can operate on a set, perhaps in 1.4....

        DECLARE
    	in_account int;
            la RECORD;
            t_errorcode INT;
            our_value NUMERIC;
            lid INT;
    	in_count int;
    	t_scn TEXT;
    	t_uid int;
    	t_prefix text;
        BEGIN

    	SELECT value into t_prefix FROM defaults WHERE setting_key = 'check_prefix';

    	t_uid := person__get_my_entity_id();
    	IF in_scn = '' THEN
    		t_scn := NULL;
    	ELSE
    		t_scn := t_prefix || in_scn;
    	END IF;
    	IF t_scn IS NOT NULL THEN
    		SELECT count(*) INTO in_count FROM cr_report_line
    		WHERE scn ilike t_scn AND report_id = in_report_id
    			AND their_balance = 0;

    		IF in_count = 0 THEN
    			INSERT INTO cr_report_line
    			(report_id, scn, their_balance, our_balance, clear_time,
    				"user", trans_type)
    			VALUES
    			(in_report_id, t_scn, in_amount, 0, in_date, t_uid,
    				in_type);
    		ELSIF in_count = 1 THEN
    			UPDATE cr_report_line
    			SET their_balance = in_amount, clear_time = in_date,
    				cleared = true
    			WHERE t_scn = scn AND report_id = in_report_id
    				AND their_balance = 0;
    		ELSE
    			SELECT count(*) INTO in_count FROM cr_report_line
    			WHERE t_scn ilike scn AND report_id = in_report_id
    				AND our_value = in_amount and their_balance = 0;

    			IF in_count = 0 THEN -- no match among many of values
    				SELECT id INTO lid FROM cr_report_line
                            	WHERE t_scn ilike scn AND report_id = in_report_id
    				ORDER BY our_balance ASC limit 1;

    				UPDATE cr_report_line
                                    SET their_balance = in_amount,
    					clear_time = in_date,
    					trans_type = in_type,
    					cleared = true
                                    WHERE id = lid;

    			ELSIF in_count = 1 THEN -- EXECT MATCH
    				UPDATE cr_report_line
    				SET their_balance = in_amount,
    					trans_type = in_type,
    					clear_time = in_date,
    					cleared = true
    				WHERE t_scn = scn AND report_id = in_report_id
                                    	AND our_value = in_amount
    					AND their_balance = 0;
    			ELSE -- More than one match
    				SELECT id INTO lid FROM cr_report_line
                            	WHERE t_scn ilike scn AND report_id = in_report_id
                                    	AND our_value = in_amount
    				ORDER BY id ASC limit 1;

    				UPDATE cr_report_line
                                    SET their_balance = in_amount,
    					trans_type = in_type,
    					cleared = true,
    					clear_time = in_date
                                    WHERE id = lid;

    			END IF;
    		END IF;
    	ELSE -- scn IS NULL, check on amount instead
    		SELECT count(*) INTO in_count FROM cr_report_line
    		WHERE report_id = in_report_id AND our_balance = in_amount
    			AND their_balance = 0 and post_date = in_date
    			and scn NOT LIKE t_prefix || '%';

    		IF in_count = 0 THEN -- no match
    			INSERT INTO cr_report_line
    			(report_id, scn, their_balance, our_balance, clear_time,
    			"user", trans_type)
    			VALUES
    			(in_report_id, t_scn, in_amount, 0, in_date, t_uid,
    			in_type);
    		ELSIF in_count = 1 THEN -- perfect match
    			UPDATE cr_report_line SET their_balance = in_amount,
    					trans_type = in_type,
    					clear_time = in_date,
    					cleared = true
    			WHERE report_id = in_report_id AND our_balance = in_amount
                            	AND their_balance = 0 and
    				in_scn NOT LIKE t_prefix || '%';
    		ELSE -- more than one match
    			SELECT min(id) INTO lid FROM cr_report_line
    			WHERE report_id = in_report_id AND our_balance = in_amount
                            	AND their_balance = 0 and post_date = in_date
    				AND scn NOT LIKE t_prefix || '%'
    			LIMIT 1;

    			UPDATE cr_report_line SET their_balance = in_amount,
    					trans_type = in_type,
    					clear_time = in_date,
    					cleared = true
    			WHERE id = lid;

    		END IF;
    	END IF;
            return 1;

        END;

## Function: reconciliation__delete_my_report(in_report_id integer)

### Returns: boolean

### Language: PLPGSQL

This function allows a user to delete his or her own unsubmitted, unapproved reconciliation reports only. This is designed to allow a user to back out of the reconciliation process without cluttering up the search results for others.

    BEGIN
        DELETE FROM cr_report_line
         WHERE report_id = in_report_id
               AND report_id IN (SELECT id FROM cr_report
                                  WHERE entered_username = SESSION_USER
                                        AND submitted IS NOT TRUE
                                        and approved IS NOT TRUE);
        DELETE FROM cr_report
         WHERE id = in_report_id AND entered_username = SESSION_USER
               AND submitted IS NOT TRUE AND approved IS NOT TRUE;
        RETURN FOUND;
    END;

## Function: reconciliation__delete_unapproved(in_report_id integer)

### Returns: boolean

### Language: PLPGSQL

This function deletes any specified unapproved transaction.

    BEGIN
        DELETE FROM cr_report_line
         WHERE report_id = in_report_id
               AND report_id IN (SELECT id FROM cr_report
                                  WHERE approved IS NOT TRUE);
        DELETE FROM cr_report
         WHERE id = in_report_id AND approved IS NOT TRUE;
        RETURN FOUND;
    END;

## Function: reconciliation__get_cleared_balance(in_chart_id integer)

### Returns: numeric

### Language: SQL

Gets the cleared balance of the account specified by chart_id. This is specified in normal format (i.e. positive numbers for debits for asset and espense accounts, and positive numbers for credits in other accounts Note that currently contra accounts will show negative balances.

    	select CASE WHEN c.category in('A', 'E') THEN sum(ac.amount) * -1 ELSE
    		sum(ac.amount) END
    	FROM account c
    	JOIN acc_trans ac ON (ac.chart_id = c.id)
    	JOIN (select id from ar where approved
    		union
    		select id from ap where approved
    		union
    		select id from gl where approved) g on (g.id = ac.trans_id)
    	WHERE c.id = $1 AND ac.cleared is true and ac.approved is true
    		GROUP BY c.id, c.category;

## Function: reconciliation__get_current_balance(in_date integer, in_account_id date)

### Returns: numeric

### Language: PLPGSQL

Gets the current balance of all approved transactions against a specific account. For asset and expense accounts this is the debit balance, for others this is the credit balance.

    DECLARE outval NUMERIC;
    BEGIN
    	SELECT CASE WHEN (select category FROM account WHERE id = in_account_id)
    			IN ('A', 'E') THEN sum(a.amount) * -1
    		ELSE sum(a.amount) END
    	FROM acc_trans a
    	JOIN (
    		SELECT id FROM ar
    		WHERE approved is true
    		UNION
    		SELECT id FROM ap
    		WHERE approved is true
    		UNION
    		SELECT id FROM gl
    		WHERE approved is true
    	) gl ON a.trans_id = gl.id
    	WHERE a.approved IS TRUE
    		AND a.chart_id = in_account_id
    		AND a.transdate <= in_date;

    	RETURN outval;
    END;

## Function: reconciliation__get_total(in_report_id integer)

### Returns: SET OF cr_report

### Language: PLPGSQL

Retrieves all header info from the reconciliation report.

        DECLARE
            row cr_report;
        BEGIN

            SELECT * INTO row FROM cr_report
            where id = in_report_id
            AND scn = -1;

            IF NOT FOUND THEN -- I think this is a fairly major error condition
                RAISE EXCEPTION 'Bad report id.';
            ELSE
                return next row;
            END IF;
        END;

## Function: reconciliation__new_report_id(in_end_date integer, in_total numeric, in_chart_id date)

### Returns: integer

### Language: SQL

Inserts creates a new report and returns the id.

        INSERT INTO cr_report(chart_id, their_total, end_date) values ($1, $2, $3);
        SELECT currval('cr_report_id_seq')::int;

## Function: reconciliation__pending_transactions(in_their_total date, in_report_id integer, in_chart_id integer, in_end_date numeric)

### Returns: integer

### Language: PLPGSQL

Ensures that the list of pending transactions in the report is up to date.

        DECLARE
            gl_row RECORD;
        BEGIN
    		INSERT INTO cr_report_line (report_id, scn, their_balance,
    			our_balance, "user", voucher_id, ledger_id, post_date)
    		SELECT in_report_id,
    		       COALESCE(ac.source, gl.ref),
    		       0,
    		       sum(amount) AS amount,
    				(select entity_id from users
    				where username = CURRENT_USER),
    			ac.voucher_id, min(ac.entry_id), ac.transdate
    		FROM acc_trans ac
    		JOIN transactions t on (ac.trans_id = t.id)
    		JOIN (select id, entity_credit_account::text as ref, 'ar' as table FROM ar where approved
    			UNION
    		      select id, entity_credit_account::text, 'ap' as table FROM ap WHERE approved
    			UNION
    		      select id, reference, 'gl' as table FROM gl WHERE approved) gl
    			ON (gl.table = t.table_name AND gl.id = t.id)
    		LEFT JOIN cr_report_line rl ON (rl.report_id = in_report_id
    			AND ((rl.ledger_id = ac.entry_id
    				AND ac.voucher_id IS NULL)
    				OR (rl.voucher_id = ac.voucher_id)))
    		WHERE ac.cleared IS FALSE
    			AND ac.approved IS TRUE
    			AND ac.chart_id = in_chart_id
    			AND ac.transdate <= in_end_date
    		GROUP BY gl.ref, ac.source, ac.transdate,
    			ac.memo, ac.voucher_id, gl.table
    		HAVING count(rl.id) = 0;

    		UPDATE cr_report set updated = now(),
    			their_total = coalesce(in_their_total, their_total)
    		where id = in_report_id;
        RETURN in_report_id;
        END;

## Function: reconciliation__report_approve(in_report_id integer)

### Returns: integer

### Language: PLPGSQL

Marks the report approved and marks all cleared transactions in it cleared.

        -- Does some basic checks before allowing the approval to go through;
        -- moves the approval to "cr_report_line", I guess, or some other "final" table.
        --
        -- Pending may just be a single flag in the database to mark that it is
        -- not finalized. Will need to discuss with Chris.

        DECLARE
            current_row RECORD;
            completed cr_report_line;
            total_errors INT;
            in_user TEXT;
    	ac_entries int[];
        BEGIN
            in_user := current_user;

            -- so far, so good. Different user, and no errors remain. Therefore,
            -- we can move it to completed reports.
            --
            -- User may not be necessary - I would think it better to use the
            -- in_user, to note who approved the report, than the user who
            -- filed it. This may require clunkier syntax..

            --
    	ac_entries := '{}';
            update cr_report set approved = 't',
    		approved_by = person__get_my_entity_id(),
    		approved_username = SESSION_USER
    	where id = in_report_id;

    	FOR current_row IN
    		SELECT compound_array(entries) AS entries FROM (
    			select as_array(ac.entry_id) as entries
    		FROM acc_trans ac
    		JOIN transactions t on (ac.trans_id = t.id)
    		JOIN (select id, entity_credit_account::text as ref, 'ar' as table FROM ar
    			UNION
    		      select id, entity_credit_account::text, 'ap' as table FROM ap
    			UNION
    		      select id, reference, 'gl' as table FROM gl) gl
    			ON (gl.table = t.table_name AND gl.id = t.id)
    		LEFT JOIN cr_report_line rl ON (rl.report_id = in_report_id
    			AND ((rl.ledger_id = ac.entry_id
    				AND ac.voucher_id IS NULL)
    				OR (rl.voucher_id = ac.voucher_id)) and rl.cleared is true)
    		WHERE ac.cleared IS FALSE
    			AND ac.chart_id = (select chart_id from cr_report where id = in_report_id)
    		GROUP BY gl.ref, ac.source, ac.transdate,
    			ac.memo, ac.voucher_id, gl.table
    		HAVING count(rl.report_id) > 0) a
    	LOOP
    		ac_entries := ac_entries || current_row.entries;
    	END LOOP;

    	UPDATE acc_trans SET cleared = TRUE
    	where entry_id = any(ac_entries);

            return 1;
        END;

## Function: reconciliation__report_details(in_report_id integer)

### Returns: SET OF cr_report_line

### Language: PLPGSQL

Returns the details of the report.

        DECLARE
            row cr_report_line;
        BEGIN
            FOR row IN
    		select * from cr_report_line where report_id = in_report_id
    		order by scn, post_date
    	LOOP

                RETURN NEXT row;

            END LOOP;
        END;

## Function: reconciliation__report_details_payee(in_report_id integer)

### Returns: SET OF recon_payee

### Language: PLPGSQL

Pulls the payee information for the reconciliation report.

       DECLARE
            row recon_payee;
        BEGIN
            FOR row IN
            	select * from recon_payee where report_id = in_report_id
            	order by scn, post_date
            LOOP
              RETURN NEXT row;
            END LOOP;
        END;

## Function: reconciliation__report_summary(in_report_id integer)

### Returns: cr_report

### Language: PLPGSQL

        DECLARE
            row cr_report;
        BEGIN
            select * into row from cr_report where id = in_report_id;

            RETURN row;

        END;

## Function: reconciliation__save_set(in_line_ids integer, in_report_id integer[])

### Returns: boolean

### Language: PLPGSQL

Sets which lines of the report are cleared.

    BEGIN
    	UPDATE cr_report_line SET cleared = false
    	WHERE report_id = in_report_id;

    	UPDATE cr_report_line SET cleared = true
    	WHERE report_id = in_report_id AND id = ANY(in_line_ids);
    	RETURN found;
    END;

## Function: reconciliation__search(in_approved date, in_submitted date, in_chart_id numeric, in_balance_to numeric, in_balance_from integer, in_date_to boolean, in_date_from boolean)

### Returns: SET OF cr_report

### Language: PLPGSQL

Searches for reconciliation reports. NULLs match all values. in_date_to and in_date_from give a range of reports. All other inputs are exact matches.

    DECLARE report cr_report;
    BEGIN
    	FOR report IN
    		SELECT r.* FROM cr_report r
    		JOIN account c ON (r.chart_id = c.id)
    		WHERE
    			(in_date_from IS NULL OR in_date_from <= end_date) and
    			(in_date_to IS NULL OR in_date_to >= end_date) AND
    			(in_balance_from IS NULL
    				or in_balance_from <= their_total ) AND
    			(in_balance_to IS NULL
    				OR in_balance_to >= their_total) AND
    			(in_chart_id IS NULL OR in_chart_id = chart_id) AND
    			(in_submitted IS NULL or in_submitted = submitted) AND
    			(in_approved IS NULL OR in_approved = approved) AND
    			(r.deleted IS FALSE)
    		ORDER BY c.accno, end_date, their_total
    	LOOP
    		RETURN NEXT report;
    	END LOOP;
    END;

## Function: reconciliation__submit_set(in_line_ids integer, in_report_id integer[])

### Returns: boolean

### Language: PLPGSQL

Submits a reconciliation report for approval. in_line_ids is used to specify which report lines are cleared, finalizing the report.

    BEGIN
    	UPDATE cr_report set submitted = true where id = in_report_id;
    	PERFORM reconciliation__save_set(in_report_id, in_line_ids);

    	RETURN FOUND;
    END;

## Function: report_invoice_aging(in_entity_class integer, in_entity_id integer)

### Returns: SET OF report_aging_item

### Language: PLPGSQL

    DECLARE
    	item report_aging_item;
    BEGIN
    	IF in_entity_class = 1 THEN
    		FOR item IN
    			SELECT c.entity_id,
    			       c.meta_number, e.name,
    			       l.line_one as address1, l.line_two as address2,
    			       l.line_three as address3,
    			       l.city_province, l.mail_code,
    			       country.name as country,
    			       '' as contact_name, '' as email,
    		               '' as phone, '' as fax,
    		               a.invnumber, a.transdate, a.till, a.ordnumber,
    			       a.ponumber, a.notes,
    			       CASE WHEN
    			                 EXTRACT(days FROM age(a.transdate)/30)
    			                 = 0
    			                 THEN (a.amount - a.paid) ELSE 0 END
    			            as c0,
    			       CASE WHEN EXTRACT(days FROM age(a.transdate)/30)
    			                 = 1
    			                 THEN (a.amount - a.paid) ELSE 0 END
    			            as c30,
    			       CASE WHEN EXTRACT(days FROM age(a.transdate)/30)
    			                 = 2
    			                 THEN (a.amount - a.paid) ELSE 0 END
    			            as c60,
    			       CASE WHEN EXTRACT(days FROM age(a.transdate)/30)
    			                 > 2
    			                 THEN (a.amount - a.paid) ELSE 0 END
    			            as c90,
    			       a.duedate, a.id, a.curr,
    			       COALESCE((SELECT sell FROM exchangerate ex
    			         WHERE a.curr = ex.curr
    			              AND ex.transdate = a.transdate), 1)
    			       AS exchangerate,
    				(SELECT compound_array(ARRAY[[p.partnumber,
    						i.description, i.qty::text]])
    					FROM parts p
    					JOIN invoice i ON (i.parts_id = p.id)
    					WHERE i.trans_id = a.id) AS line_items
    			  FROM ap a
    			  JOIN entity_credit_account c USING (entity_id)
    			  JOIN entity e ON (e.id = c.entity_id)
    			 CROSS JOIN location l
    			  JOIN country ON (country.id = l.country_id)
    			 WHERE a.entity_id like coalesce(in_entity_id::text, '%')
    				AND l.id = (SELECT min(location_id)
    					FROM company_to_location
    					WHERE company_id = (select min(id)
    						FROM company
    						WHERE entity_id = c.entity_id))
    			ORDER BY entity_id, curr, transdate, invnumber
    		LOOP
    			return next item;
    		END LOOP;
    	ELSIF in_entity_class = 2 THEN
    		FOR item IN
    			SELECT c.entity_id,
    			       c.meta_number, e.name,
    			       l.line_one as address1, l.line_two as address2,
    			       l.line_three as address3,
    			       l.city_province, l.mail_code,
    			       country.name as country,
    			       '' as contact_name, '' as email,
    		               '' as phone, '' as fax,
    		               a.invnumber, a.transdate, a.till, a.ordnumber,
    			       a.ponumber, a.notes,
    			       CASE WHEN
    			                 EXTRACT(days FROM age(a.transdate)/30)
    			                 = 0
    			                 THEN (a.amount - a.paid) ELSE 0 END
    			            as c0,
    			       CASE WHEN EXTRACT(days FROM age(a.transdate)/30)
    			                 = 1
    			                 THEN (a.amount - a.paid) ELSE 0 END
    			            as c30,
    			       CASE WHEN EXTRACT(days FROM age(a.transdate)/30)
    			                 = 2
    			                 THEN (a.amount - a.paid) ELSE 0 END
    			            as c60,
    			       CASE WHEN EXTRACT(days FROM age(a.transdate)/30)
    			                 > 2
    			                 THEN (a.amount - a.paid) ELSE 0 END
    			            as c90,
    			       a.duedate, a.id, a.curr,
    			       (SELECT buy FROM exchangerate ex
    			         WHERE a.curr = ex.curr
    			              AND ex.transdate = a.transdate)
    			       AS exchangerate,
    				(SELECT compound_array(ARRAY[[p.partnumber,
    						i.description, i.qty::text]])
    					FROM parts p
    					JOIN invoice i ON (i.parts_id = p.id)
    					WHERE i.trans_id = a.id) AS line_items
    			  FROM ar a
    			  JOIN entity_credit_account c USING (entity_id)
    			  JOIN entity e ON (e.id = c.entity_id)
    			 CROSS JOIN location l
    			  JOIN country ON (country.id = l.country_id)
    			 WHERE a.entity_id like coalesce(in_entity_id::text, '%')
    				AND l.id = (SELECT min(location_id)
    					FROM company_to_location
    					WHERE company_id = (select min(id)
    						FROM company
    						WHERE entity_id = c.entity_id))
    			ORDER BY entity_id, curr, transdate, invnumber
    		LOOP
    			return next item;
    		END LOOP;
    	ELSE
    		RAISE EXCEPTION 'Entity Class % unsupported in aging report',
    			in_entity_class;
    	END IF;
    END;

## Function: report_trial_balance(in_gifi date, in_project_id date, in_department_id integer, in_dateto integer, in_datefrom boolean)

### Returns: SET OF trial_balance_line

### Language: PLPGSQL

This is a simple routine to generate trial balances for the full company, for a project, or for a department.

    DECLARE out_row trial_balance_line;
    BEGIN
    	IF in_department_id IS NULL THEN
    		FOR out_row IN
    			SELECT c.id, c.accno, c.description,
    				SUM(CASE WHEN ac.transdate < in_datefrom
    				              AND c.category IN ('I', 'L', 'Q')
    				    THEN ac.amount
    				    ELSE ac.amount * -1
    				    END),
    			        SUM(CASE WHEN ac.transdate >= in_date_from
    				              AND ac.amount > 0
    			            THEN ac.amount
    			            ELSE 0 END),
    			        SUM(CASE WHEN ac.transdate >= in_date_from
    				              AND ac.amount < 0
    			            THEN ac.amount
    			            ELSE 0 END) * -1,
    				SUM(CASE WHEN ac.transdate >= in_date_from
    					AND c.charttype IN ('I')
    				    THEN ac.amount
    				    WHEN ac.transdate >= in_date_from
    				              AND c.category IN ('I', 'L', 'Q')
    				    THEN ac.amount
    				    ELSE ac.amount * -1
    				    END)
    				FROM acc_trans ac
    				JOIN (select id, approved FROM ap
    					UNION ALL
    					select id, approved FROM gl
    					UNION ALL
    					select id, approved FROM ar) g
    					ON (g.id = ac.trans_id)
    				JOIN chart c ON (c.id = ac.chart_id)
    				WHERE ac.transdate <= in_date_to
    					AND ac.approved AND g.approved
    					AND (in_project_id IS NULL
    						OR in_project_id = ac.project_id)
    				GROUP BY c.id, c.accno, c.description
    				ORDER BY c.accno

    		LOOP
    			RETURN NEXT out_row;
    		END LOOP;
    	ELSE
    		FOR out_row IN
    			SELECT 1
    		LOOP
    			RETURN NEXT out_row;
    		END LOOP;
    	END IF;
    END;

## Function: reset_tsearch()

### Returns: void

### Language: C

    tsa_reset_tsearch

## Function: rewrite(tsquery, text)

### Returns: tsquery

### Language: INTERNAL

    tsquery_rewrite_query

## Function: rewrite(tsquery, tsquery, tsquery)

### Returns: tsquery

### Language: INTERNAL

    tsquery_rewrite

## Function: rewrite(tsquery[])

### Returns: tsquery

### Language: INTERNAL

    aggregate_dummy

## Function: rewrite_accum(tsquery, tsquery[])

### Returns: tsquery

### Language: C

    tsa_rewrite_accum

## Function: rewrite_finish(tsquery)

### Returns: tsquery

### Language: C

    tsa_rewrite_finish

## Function: save_taxform(in_taxform_name integer, in_country_code text)

### Returns: boolean

### Language: PLPGSQL

Saves tax form information. Returns true or raises exception.

    BEGIN
    	INSERT INTO country_tax_form(country_id, form_name)
    	values (in_country_code, in_taxform_name);

    	RETURN true;
    END;

## Function: session_check(in_token integer, in_session_id text)

### Returns: session

### Language: PLPGSQL

Returns a session row. If no session exists, creates one. The row returned is the current, active session.

    DECLARE out_row session%ROWTYPE;
    BEGIN
    	DELETE FROM session
    	 WHERE last_used < now() - coalesce((SELECT value FROM defaults
                                        WHERE setting_key = 'timeout')::interval,
    	                            '90 minutes'::interval);
            UPDATE session
               SET last_used = now()
             WHERE session_id = in_session_id
                   AND token = in_token
                   AND last_used > now() - (SELECT value FROM defaults
    				WHERE setting_key = 'timeout')::interval
    	       AND users_id = (select id from users
    			where username = SESSION_USER);
    	IF FOUND THEN
    		SELECT * INTO out_row FROM session WHERE session_id = in_session_id;
    	ELSE
    		DELETE FROM SESSION
    		WHERE users_id IN (select id from users
                            where username = SESSION_USER);
    		-- the above query also releases all discretionary locks by the
                    -- session

    		IF NOT FOUND THEN
    			PERFORM id FROM users WHERE username = SESSION_USER;
    			IF NOT FOUND THEN
    				RAISE EXCEPTION 'User Not Known';
    			END IF;

    		END IF;
    		INSERT INTO session(users_id, token, last_used)
    		SELECT id, md5(random()::text), now()
    		  FROM users WHERE username = SESSION_USER;

    		SELECT * INTO out_row FROM session
    		 WHERE session_id = currval('session_session_id_seq');
    	END IF;
    	RETURN out_row;
    END;

## Function: set_curcfg(integer)

### Returns: void

### Language: C

    tsa_set_curcfg

## Function: set_curcfg(text)

### Returns: void

### Language: C

    tsa_set_curcfg_byname

## Function: set_curdict(integer)

### Returns: void

### Language: C

    tsa_set_curdict

## Function: set_curdict(text)

### Returns: void

### Language: C

    tsa_set_curdict_byname

## Function: set_curprs(integer)

### Returns: void

### Language: C

    tsa_set_curprs

## Function: set_curprs(text)

### Returns: void

### Language: C

    tsa_set_curprs_byname

## Function: set_limit(real)

### Returns: real

### Language: C

    set_limit

## Function: setting__get_currencies()

### Returns: text[]

### Language: SQL

Returns an array of currencies from the defaults table.

    SELECT string_to_array(value, ':') from defaults where setting_key = 'curr';

## Function: setting_get(in_key character varying)

### Returns: defaults

### Language: SQL

Returns the value of the setting in the defaults table.

    SELECT * FROM defaults WHERE setting_key = $1;

## Function: setting_get_default_accounts()

### Returns: SET OF defaults

### Language: PLPGSQL

Returns a set of settings for default accounts.

    DECLARE
    	account defaults%ROWTYPE;
    BEGIN
    	FOR account IN
    		SELECT * FROM defaults
    		WHERE setting_key like '%accno_id'
                    ORDER BY setting_key
    	LOOP
    		RETURN NEXT account;
    	END LOOP;
    END;

## Function: setting_increment(in_key character varying)

### Returns: character varying

### Language: PLPGSQL

This function takes a value for a sequence in the defaults table and increments it. Leading zeroes and spaces are preserved as placeholders. Currently <?lsmb parsing is not supported in this routine though it may be added at a later date.

    DECLARE
    	base_value VARCHAR;
    	raw_value VARCHAR;
    	increment INTEGER;
    	inc_length INTEGER;
    	new_value VARCHAR;
    BEGIN
    	SELECT value INTO raw_value FROM defaults
    	WHERE setting_key = in_key
    	FOR UPDATE;

    	SELECT substring(raw_value from  '(' || E'\\' || 'd*)(' || E'\\' || 'D*|<' || E'\\' || '?lsmb [^<>] ' || E'\\' || '?>)*$')
    	INTO base_value;

    	IF base_value like '0%' THEN
    		increment := base_value::integer + 1;
    		SELECT char_length(increment::text) INTO inc_length;

    		SELECT overlay(base_value placing increment::varchar
    			from (select char_length(base_value)
    				- inc_length + 1) for inc_length)
    		INTO new_value;
    	ELSE
    		new_value := base_value::integer + 1;
    	END IF;
    	SELECT regexp_replace(raw_value, base_value, new_value) INTO new_value;
    	UPDATE defaults SET value = new_value WHERE setting_key = in_key;

    	return new_value;
    END;

## Function: setting_set(in_value character varying, in_key character varying)

### Returns: boolean

### Language: PLPGSQL

sets a value in the defaults thable and returns true if successful.

    BEGIN
    	UPDATE defaults SET value = in_value WHERE setting_key = in_key;
            IF NOT FOUND THEN
                 INSERT INTO defaults (setting_key, value)
                      VALUES (in_setting_key, in_value);
            END IF;
    	RETURN TRUE;
    END;

## Function: setweight(tsvector, "char")

### Returns: tsvector

### Language: INTERNAL

    tsvector_setweight

## Function: show_curcfg()

### Returns: oid

### Language: INTERNAL

    get_current_ts_config

## Function: show_limit()

### Returns: real

### Language: C

    show_limit

## Function: show_trgm(text)

### Returns: text[]

### Language: C

    show_trgm

## Function: similarity(text, text)

### Returns: real

### Language: C

    similarity

## Function: similarity_op(text, text)

### Returns: boolean

### Language: C

    similarity_op

## Function: snb_en_init(internal)

### Returns: internal

### Language: C

    tsa_snb_en_init

## Function: snb_lexize(internal, internal, integer)

### Returns: internal

### Language: C

    tsa_snb_lexize

## Function: snb_ru_init(internal)

### Returns: internal

### Language: C

    tsa_snb_ru_init

## Function: snb_ru_init_koi8(internal)

### Returns: internal

### Language: C

    tsa_snb_ru_init_koi8

## Function: snb_ru_init_utf8(internal)

### Returns: internal

### Language: C

    tsa_snb_ru_init_utf8

## Function: spell_init(internal)

### Returns: internal

### Language: C

    tsa_spell_init

## Function: spell_lexize(internal, internal, integer)

### Returns: internal

### Language: C

    tsa_spell_lexize

## Function: stat(text)

### Returns: SET OF statinfo

### Language: INTERNAL

    ts_stat1

## Function: stat(text, text)

### Returns: SET OF statinfo

### Language: INTERNAL

    ts_stat2

## Function: strip(tsvector)

### Returns: tsvector

### Language: INTERNAL

    tsvector_strip

## Function: syn_init(internal)

### Returns: internal

### Language: C

    tsa_syn_init

## Function: syn_lexize(internal, internal, integer)

### Returns: internal

### Language: C

    tsa_syn_lexize

## Function: tax_form__get(in_form_id integer)

### Returns: country_tax_form

### Language: SQL

Retrieves specified tax form information from the database.

    SELECT * FROM country_tax_form where id = $1;

## Function: tax_form__list_all()

### Returns: SET OF country_tax_form

### Language: SQL

Returns a set of all tax forms, ordered by country_id and id

    SELECT * FROM country_tax_form ORDER BY country_id, id;

## Function: tax_form__list_ext()

### Returns: SET OF taxform_list

### Language: SQL

Returns a list of tax forms with an added field, country_name, to specify the name of the country.

    SELECT t.id, t.form_name, t.country_id, c.name, t.default_reportable
      FROM country_tax_form t
      JOIN country c ON c.id = t.country_id
     ORDER BY c.name, t.form_name;

## Function: tax_form__save(in_default_reportable integer, in_form_name integer, in_country_id text, in_id boolean)

### Returns: integer

### Language: PLPGSQL

Saves tax form information to the database.

    BEGIN
            UPDATE country_tax_form
               SET country_id = in_country_id,
                   form_name =in_form_name,
                   default_reportable = coalesce(in_default_reportable,false)
             WHERE id = in_id;

            IF FOUND THEN
               RETURN in_id;
            END IF;

    	insert into country_tax_form(country_id,form_name, default_reportable)
    	values (in_country_id, in_form_name,
                    coalesce(in_default_reportable, false));

    	RETURN currval('country_tax_form_id_seq');
    END;

## Function: tax_form_details_report(in_meta_number integer, in_end date, in_begin date, in_tax_form_id text)

### Returns: SET OF tax_form_report_detail_item

### Language: PLPGSQL

This provides a list of invoices and transactions that a report hits. This is intended to allow an organization to adjust what is reported on the 1099 before printing them.

    DECLARE
    	out_row tax_form_report_detail_item;
    BEGIN
    	FOR out_row IN
                  SELECT company.legal_name, company.entity_id,
                         entity_credit_account.entity_class, entity.control_code,
                         entity_credit_account.meta_number,
                         sum(CASE WHEN gl.amount = 0 then 0
                                  when relation = 'acc_trans'
                              THEN ac.reportable_amount * pmt.amount
                                    / gl.amount
                              ELSE 0
                          END * CASE WHEN gl.class = 'ap' THEN -1 else 1 end),
                         sum(CASE WHEN gl.amount = 0 then 0
                                  WHEN relation = 'invoice'
                              THEN ac.reportable_amount * pmt.amount
                                   / gl.amount
                              ELSE 0
                          END * CASE WHEN gl.class = 'ar' THEN -1 else 1 end),
                         SUM(CASE WHEN gl.amount = 0 THEN 0
                                  ELSE ac.reportable_amount * pmt.amount
                                   / gl.amount
                                  END
                             * CASE WHEN gl.class = 'ap' THEN -1 else 1 end
                             * CASE WHEN relation = 'invoice' THEN -1 ELSE 1 END),
                         gl.invnumber, gl.duedate::text, gl.id
                    FROM (select id, entity_credit_account, invnumber, duedate,
                                 amount, transdate, 'ar' as class
                            FROM ar
                           UNION
                          select id, entity_credit_account, invnumber, duedate,
                                 amount, transdate, 'ap' as class
                            FROM ap
                         ) gl
                    JOIN (select trans_id, 'acc_trans' as relation,
                                 sum(amount) as amount,
                                 sum(case when atf.reportable then amount else 0
                                     end) as reportable_amount
                            FROM  acc_trans
                       LEFT JOIN ac_tax_form atf
                              ON (acc_trans.entry_id = atf.entry_id)
                           GROUP BY trans_id
                           UNION
                          select trans_id, 'invoice' as relation,
                                 sum(sellprice * qty) as amount,
                                 sum(case when itf.reportable
                                          then sellprice * qty
                                          else 0
                                     end) as reportable_amount
                            FROM invoice
                       LEFT JOIN invoice_tax_form itf
                              ON (invoice.id = itf.invoice_id)
                           GROUP BY trans_id
                         ) ac ON (ac.trans_id = gl.id)
    		JOIN entity_credit_account ON (gl.entity_credit_account = entity_credit_account.id)
    		JOIN entity ON (entity.id = entity_credit_account.entity_id)
    		JOIN company ON (entity.id = company.entity_id)
    		JOIN country_tax_form ON (entity_credit_account.taxform_id = country_tax_form.id)
                    JOIN (SELECT ac.trans_id, sum(ac.amount) as amount,
                                 as_array(entry_id) as entry_ids,
                                 as_array(chart_id) as chart_ids,
                                 count(*) as num
                            FROM acc_trans ac
                           where chart_id in (select account_id
                                                from account_link
                                               where description like '%paid')
                              AND transdate BETWEEN in_begin AND in_end
                         group by ac.trans_id
                         ) pmt ON  (pmt.trans_id = gl.id)
    		WHERE country_tax_form.id = in_tax_form_id AND meta_number = in_meta_number
    		GROUP BY legal_name, meta_number, company.entity_id, entity_credit_account.entity_class, entity.control_code, gl.invnumber, gl.duedate, gl.id
    	LOOP
    		RETURN NEXT out_row;
    	END LOOP;
    END;

## Function: tax_form_summary_report(in_end integer, in_begin date, in_tax_form_id date)

### Returns: SET OF tax_form_report_item

### Language: PLPGSQL

This provides the total reportable value per vendor. As per 1099 forms, these are cash-basis documents and show amounts paid.

    DECLARE
    	out_row tax_form_report_item;
    BEGIN
    	FOR out_row IN
                  SELECT company.legal_name, company.entity_id,
                         entity_credit_account.entity_class, entity.control_code,
                         entity_credit_account.meta_number,
                         sum(CASE WHEN gl.amount = 0 THEN 0
                                  WHEN relation = 'acc_trans'
                              THEN ac.reportable_amount * pmt.amount
                                    / gl.amount
                              ELSE 0
                          END * CASE WHEN gl.class = 'ap' THEN -1 else 1 end),
                         sum(CASE WHEN gl.amount = 0 THEN 0
                                  WHEN relation = 'invoice'
                              THEN ac.reportable_amount * pmt.amount
                                   / gl.amount
                              ELSE 0
                          END * CASE WHEN gl.class = 'ar' THEN -1 else 1 end),
                         sum(CASE WHEN gl.amount = 0 THEN 0
                              ELSE ac.reportable_amount * pmt.amount
                                    / gl.amount
                          END * CASE WHEN gl.class = 'ap' THEN -1 else 1 end
                          * CASE WHEN ac.relation = 'invoice' then -1 else 1 end)

    		FROM (select id, transdate, entity_credit_account, invoice,
                                 amount, 'ar' as class FROM ar
                           UNION
                          select id, transdate, entity_credit_account, invoice,
                                  amount, 'ap' as class from ap
                         ) gl
                   JOIN (select trans_id, 'acc_trans' as relation,
                                 sum(amount) as amount,
                                 sum(case when atf.reportable then amount else 0
                                     end) as reportable_amount
                            FROM  acc_trans
                        LEFT JOIN ac_tax_form atf
                              ON (acc_trans.entry_id = atf.entry_id)
                           GROUP BY trans_id
                           UNION
                          select trans_id, 'invoice' as relation,
                                 sum(sellprice * qty) as amount,
                                 sum(case when itf.reportable
                                          then sellprice * qty
                                          else 0
                                     end) as reportable_amount
                            FROM invoice
                        LEFT JOIN invoice_tax_form itf
                              ON (invoice.id = itf.invoice_id)
                           GROUP BY trans_id
                         ) ac ON (ac.trans_id = gl.id
                                 AND ((gl.invoice is true and ac.relation='invoice')
                                      OR (gl.invoice is false
                                         and ac.relation='acc_trans')))
                    JOIN (SELECT ac.trans_id, sum(ac.amount) as amount,
                                 as_array(entry_id) as entry_ids,
                                 as_array(chart_id) as chart_ids,
                                 count(*) as num
                            FROM acc_trans ac
                           where chart_id in (select account_id
                                                from account_link
                                               where description like '%paid')
                              AND transdate BETWEEN in_begin AND in_end
                         group by ac.trans_id
                         ) pmt ON  (pmt.trans_id = gl.id)
    		JOIN entity_credit_account
                      ON (gl.entity_credit_account = entity_credit_account.id)
    		JOIN entity ON (entity.id = entity_credit_account.entity_id)
    		JOIN company ON (entity.id = company.entity_id)
    		JOIN country_tax_form ON (entity_credit_account.taxform_id = country_tax_form.id)
                   WHERE country_tax_form.id = in_tax_form_id
                 GROUP BY legal_name, meta_number, company.entity_id, entity_credit_account.entity_class, entity.control_code
        LOOP
    		RETURN NEXT out_row;
    	END LOOP;
    END;

## Function: thesaurus_init(internal)

### Returns: internal

### Language: C

    tsa_thesaurus_init

## Function: thesaurus_lexize(internal, internal, integer, internal)

### Returns: internal

### Language: C

    tsa_thesaurus_lexize

## Function: to_args(in_args text[], in_base text[])

### Returns: text[]

### Language: SQL

This function takes two arguments. The first is a one-dimensional array representing the base state of the argument array. The second is a two element array of {key, value}. If either of the args is null, it returns the first argument. Otherwise it returns the first initial array, concatenated with key || '=' || value. It primarily exists for the to_args aggregate.

    SELECT CASE WHEN $2[1] IS NULL OR $2[2] IS NULL THEN $1
                ELSE $1 || ($2[1]::text || '=' || $2[2]::text)
           END;

## Function: to_args(text[])

### Returns: text[]

### Language: INTERNAL

Turns a setof ARRAY[key,value] into an ARRAY[key||'='||value, key||'='||value,...]

    aggregate_dummy

## Function: to_tsquery(oid, text)

### Returns: tsquery

### Language: INTERNAL

    to_tsquery_byid

## Function: to_tsquery(text)

### Returns: tsquery

### Language: INTERNAL

    to_tsquery

## Function: to_tsquery(text, text)

### Returns: tsquery

### Language: C

    tsa_to_tsquery_name

## Function: to_tsvector(oid, text)

### Returns: tsvector

### Language: INTERNAL

    to_tsvector_byid

## Function: to_tsvector(text)

### Returns: tsvector

### Language: INTERNAL

    to_tsvector

## Function: to_tsvector(text, text)

### Returns: tsvector

### Language: C

    tsa_to_tsvector_name

## Function: token_type()

### Returns: SET OF tokentype

### Language: C

    tsa_token_type_current

## Function: token_type(integer)

### Returns: SET OF tokentype

### Language: INTERNAL

    ts_token_type_byid

## Function: token_type(text)

### Returns: SET OF tokentype

### Language: INTERNAL

    ts_token_type_byname

## Function: track_global_sequence()

### Returns: trigger

### Language: PLPGSQL

This trigger is used to track the id sequence entries across the transactions table, and with the ar, ap, and gl tables. This is necessary because these have not been properly refactored yet.

    BEGIN
    	IF tg_op = 'INSERT' THEN
    		INSERT INTO transactions (id, table_name)
    		VALUES (new.id, TG_RELNAME);
    	ELSEIF tg_op = 'UPDATE' THEN
    		IF new.id = old.id THEN
    			return new;
    		ELSE
    			UPDATE transactions SET id = new.id WHERE id = old.id;
    		END IF;
    	ELSE
    		DELETE FROM transactions WHERE id = old.id;
    	END IF;
    	RETURN new;
    END;

## Function: trigger_parts_short()

### Returns: trigger

### Language: PLPGSQL

    BEGIN
      IF NEW.onhand >= NEW.rop THEN
        NOTIFY parts_short;
      END IF;
      RETURN NEW;
    END;

## Function: trigger_pending_job()

### Returns: trigger

### Language: PLPGSQL

    BEGIN
      IF NEW.success IS NULL THEN
        NOTIFY job_entered;
      END IF;
      RETURN NEW;
    END;

## Function: ts_debug(text)

### Returns: SET OF tsdebug

### Language: SQL

    select
            (select c.cfgname::text from pg_catalog.pg_ts_config as c
             where c.oid = show_curcfg()),
            t.alias as tok_type,
            t.descr as description,
            p.token,
            ARRAY ( SELECT m.mapdict::pg_catalog.regdictionary::pg_catalog.text
                    FROM pg_catalog.pg_ts_config_map AS m
                    WHERE m.mapcfg = show_curcfg() AND m.maptokentype = p.tokid
                    ORDER BY m.mapseqno )
            AS dict_name,
            strip(to_tsvector(p.token)) as tsvector
    from
            parse( _get_parser_from_curcfg(), $1 ) as p,
            token_type() as t
    where
            t.tokid = p.tokid

## Function: tsearch2()

### Returns: trigger

### Language: C

    tsa_tsearch2

## Function: tsq_mcontained(tsquery, tsquery)

### Returns: boolean

### Language: INTERNAL

    tsq_mcontained

## Function: tsq_mcontains(tsquery, tsquery)

### Returns: boolean

### Language: INTERNAL

    tsq_mcontains

## Function: tsquery_and(tsquery, tsquery)

### Returns: tsquery

### Language: INTERNAL

    tsquery_and

## Function: tsquery_not(tsquery)

### Returns: tsquery

### Language: INTERNAL

    tsquery_not

## Function: tsquery_or(tsquery, tsquery)

### Returns: tsquery

### Language: INTERNAL

    tsquery_or

## Function: unlock(in_id integer)

### Returns: boolean

### Language: PLPGSQL

Releases a pessimistic locks against a transaction, if that transaciton, as identified by in_id exists, and if it is locked by the current session. These locks are again only advisory, and the application may choose to handle them or not. Returns true if the transaction was unlocked by this routine, false otherwise.

    BEGIN
        UPDATE transactions SET locked_by = NULL WHERE id = in_id
               AND locked_by IN (SELECT session_id FROM session WHERE users_id =
    		(SELECT id FROM users WHERE username = SESSION_USER));
        RETURN FOUND;
    END;

## Function: unlock_all()

### Returns: boolean

### Language: PLPGSQL

Releases all pessimistic locks against transactions. These locks are again only advisory, and the application may choose to handle them or not. Returns true if any transactions were unlocked, false otherwise.

    BEGIN
        UPDATE transactions SET locked_by = NULL
        where locked_by IN
              (select session_id from session WHERE users_id =
                      (SELECT id FROM users WHERE username = SESSION_USER));

        RETURN FOUND;
    END;

## Function: user__change_password(in_new_password text)

### Returns: integer

### Language: PLPGSQL

Alloes a user to change his or her own password. The password is set to expire setting_get('password_duration') days after the password change.

    DECLARE
    	t_expires timestamp;
            t_password_duration text;
    BEGIN
        SELECT value INTO t_password_duration FROM defaults
         WHERE setting_key = 'password_duration';
        IF t_password_duration IS NULL or t_password_duration='' THEN
            t_expires := 'infinity';
        ELSE
            t_expires := now()
                         + (t_password_duration::numeric::text || ' days')::interval;
        END IF;

        UPDATE users SET notify_password = DEFAULT where username = SESSION_USER;

        EXECUTE 'ALTER USER ' || quote_ident(SESSION_USER) ||
                ' with ENCRYPTED password ' || quote_literal(in_new_password) ||
                     ' VALID UNTIL '|| quote_literal(t_expires);
        return 1;
    END;

## Function: user__check_my_expiration()

### Returns: interval

### Language: PLPGSQL

Returns the time when password of the current logged in user is set to expire.

    DECLARE
        outval interval;
    BEGIN
        SELECT CASE WHEN isfinite(rolvaliduntil) is not true THEN '1 year'::interval
                    ELSE rolvaliduntil - now() END AS expiration INTO outval
        FROM pg_roles WHERE rolname = SESSION_USER;
        RETURN outval;
    end;

## Function: user__expires_soon()

### Returns: boolean

### Language: SQL

Returns true if the password of the current logged in user is set to expire within on week.

       SELECT user__check_my_expiration() < '1 week';

## Function: user__get_all_users()

### Returns: SET OF user_listable

### Language: SQL

        select * from user_listable;

## Function: user__get_preferences(in_user_id integer)

### Returns: SET OF user_preference

### Language: PLPGSQL

Returns the preferences row for the user.

    declare
        v_row user_preference;
    BEGIN
        select * into v_row from user_preference where id = in_user_id;

        IF NOT FOUND THEN

            RAISE EXCEPTION 'Could not find user preferences for id %', in_user_id;
        ELSE
            return next v_row;
        END IF;
    END;

## Function: user__save_preferences(in_printer text, in_stylesheet text, in_language text, in_numberformat text, in_dateformat text)

### Returns: boolean

### Language: PLPGSQL

Saves user preferences. Returns true if successful, false if no preferences were found to update.

    BEGIN
        UPDATE user_preference
        SET dateformat = in_dateformat,
            numberformat = in_numberformat,
            language = in_language,
            stylesheet = in_stylesheet,
            printer = in_printer
        WHERE id = (select id from users where username = SESSION_USER);
        RETURN FOUND;
    END;

## Function: voucher__delete(in_voucher_id integer)

### Returns: integer

### Language: PLPGSQL

Deletes the specified voucher from the batch.

    DECLARE
    	voucher_row RECORD;
    BEGIN
    	SELECT * INTO voucher_row FROM voucher WHERE id = in_voucher_id;
    	IF voucher_row.batch_class IN (1, 2, 5) THEN
            DELETE FROM ac_tax_form WHERE entry_id IN (
                   SELECT entry_id
                     FROM acc_trans
                   WHERE trans_id = voucher_row.trans_id);

    		DELETE FROM acc_trans WHERE trans_id = voucher_row.trans_id;
    		DELETE FROM ar WHERE id = voucher_row.trans_id;
    		DELETE FROM ap WHERE id = voucher_row.trans_id;
    		DELETE FROM gl WHERE id = voucher_row.trans_id;
    		DELETE FROM voucher WHERE id = voucher_row.id;
    		-- DELETE FROM transactions WHERE id = voucher_row.trans_id;
    	ELSE
    		update ar set paid = amount +
    			(select sum(amount) from acc_trans
    			join chart ON (acc_trans.chart_id = chart.id)
    			where link = 'AR' AND trans_id = ar.id
    				AND (voucher_id IS NULL
    				OR voucher_id <> voucher_row.id))
    		where id in (select trans_id from acc_trans
    				where voucher_id = voucher_row.id);

    		update ap set paid = amount - (select sum(amount) from acc_trans
    			join chart ON (acc_trans.chart_id = chart.id)
    			where link = 'AP' AND trans_id = ap.id
    				AND (voucher_id IS NULL
    				OR voucher_id <> voucher_row.id))
    		where id in (select trans_id from acc_trans
    				where voucher_id = voucher_row.id);
                    DELETE FROM ac_tax_form WHERE entry_id IN
                           (select entry_id from acc_trans
                             where voucher_id = voucher_row.id);

    		DELETE FROM acc_trans where voucher_id = voucher_row.id;
    	END IF;
    	RETURN 1;
    END;

## Function: voucher_get_batch(in_batch_id integer)

### Returns: batch

### Language: PLPGSQL

Retrieves basic batch information based on batch_id.

    DECLARE
    	batch_out batch%ROWTYPE;
    BEGIN
    	SELECT * INTO batch_out FROM batch b WHERE b.id = in_batch_id;
    	RETURN batch_out;
    END;

## Function: voucher_list(in_batch_id integer)

### Returns: SET OF voucher_list

### Language: PLPGSQL

Retrieves a list of vouchers and amounts attached to the batch.

    declare voucher_item record;
    BEGIN
        	FOR voucher_item IN
    		SELECT v.id, a.invnumber, e.name,
    			v.batch_id, v.trans_id,
    			a.amount, a.transdate, 'Payable'
    		FROM voucher v
    		JOIN ap a ON (v.trans_id = a.id)
    		JOIN entity_credit_account eca
    			ON (eca.id = a.entity_credit_account)
    		JOIN entity e ON (eca.entity_id = e.id)
    		WHERE v.batch_id = in_batch_id
    			AND v.batch_class = (select id from batch_class
    					WHERE class = 'ap')
    		UNION
    		SELECT v.id, a.invnumber, e.name,
    			v.batch_id, v.trans_id,
    			a.amount, a.transdate, 'Receivable'
    		FROM voucher v
    		JOIN ar a ON (v.trans_id = a.id)
    		JOIN entity_credit_account eca
    			ON (eca.id = a.entity_credit_account)
    		JOIN entity e ON (eca.entity_id = e.id)
    		WHERE v.batch_id = in_batch_id
    			AND v.batch_class = (select id from batch_class
    					WHERE class = 'ar')
    		UNION ALL
    		-- TODO:  Add the class labels to the class table.
    		SELECT v.id, a.source,
    			cr.meta_number || '--'  || co.legal_name ,
    			v.batch_id, v.trans_id,
    			sum(CASE WHEN bc.class LIKE 'payment%' THEN a.amount * -1
    			     ELSE a.amount  END), a.transdate,
    			CASE WHEN bc.class = 'payment' THEN 'Payment'
    			     WHEN bc.class = 'payment_reversal'
    			     THEN 'Payment Reversal'
    			END
    		FROM voucher v
    		JOIN acc_trans a ON (v.id = a.voucher_id)
                    JOIN batch_class bc ON (bc.id = v.batch_class)
    		JOIN chart c ON (a.chart_id = c.id)
    		JOIN ap ON (ap.id = a.trans_id)
    		JOIN entity_credit_account cr
    			ON (ap.entity_credit_account = cr.id)
    		JOIN company co ON (cr.entity_id = co.entity_id)
    		WHERE v.batch_id = in_batch_id
    			AND a.voucher_id = v.id
    			AND (bc.class like 'payment%' AND c.link = 'AP')
    		GROUP BY v.id, a.source, cr.meta_number, co.legal_name ,
                            v.batch_id, v.trans_id, a.transdate, bc.class

    		UNION ALL
    		SELECT v.id, a.source, a.memo,
    			v.batch_id, v.trans_id,
    			CASE WHEN bc.class LIKE 'receipt%' THEN sum(a.amount) * -1
    			     ELSE sum(a.amount)  END, a.transdate,
    			CASE WHEN bc.class = 'receipt' THEN 'Receipt'
    			     WHEN bc.class = 'receipt_reversal'
    			     THEN 'Receipt Reversal'
    			END
    		FROM voucher v
    		JOIN acc_trans a ON (v.id = a.voucher_id)
                    JOIN batch_class bc ON (bc.id = v.batch_class)
    		JOIN chart c ON (a.chart_id = c.id)
    		JOIN ar ON (ar.id = a.trans_id)
    		JOIN entity_credit_account cr
    			ON (ar.entity_credit_account = cr.id)
    		JOIN company co ON (cr.entity_id = co.entity_id)
    		WHERE v.batch_id = in_batch_id
    			AND a.voucher_id = v.id
    			AND (bc.class like 'receipt%' AND c.link = 'AR')
    		GROUP BY v.id, a.source, cr.meta_number, co.legal_name ,
                            a.memo, v.batch_id, v.trans_id, a.transdate, bc.class
    		UNION ALL
    		SELECT v.id, g.reference, g.description,
    			v.batch_id, v.trans_id,
    			sum(a.amount), g.transdate, 'GL'
    		FROM voucher v
    		JOIN gl g ON (g.id = v.trans_id)
    		JOIN acc_trans a ON (v.trans_id = a.trans_id)
    		WHERE a.amount > 0
    			AND v.batch_id = in_batch_id
    			AND v.batch_class IN (select id from batch_class
    					where class = 'gl')
    		GROUP BY v.id, g.reference, g.description, v.batch_id,
    			v.trans_id, g.transdate
    		ORDER BY 7, 1
    	LOOP
    		RETURN NEXT voucher_item;
    	END LOOP;
    END;

## Function: warehouse__list_all()

### Returns: SET OF warehouse

### Language: SQL

    SELECT * FROM warehouse order by description;

Generated by PostgreSQL Autodoc

W3C HTML 4.01 Strict