Dumped on 2012-12-27

Index of database - ledgersmb


Schema lsmb13

standard public schema


Table: lsmb13.ac_tax_form

Mapping acc_trans to country_tax_form for reporting purposes.

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

Index - Schema lsmb13


Table: lsmb13.acc_trans

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

lsmb13.acc_trans Structure
F-Key Name Type Description
lsmb13.transactions.id trans_id integer NOT NULL
lsmb13.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
lsmb13.voucher.id voucher_id integer
entry_id serial PRIMARY KEY

Tables referencing this one via Foreign Key Constraints:

ac_transdate_year_idx date_part('YEAR'::text, transdate) 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 lsmb13


Table: lsmb13.account

This table stores the main account info.

lsmb13.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
lsmb13.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:

Index - Schema lsmb13


Table: lsmb13.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.

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

Index - Schema lsmb13


Table: lsmb13.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.

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

Tables referencing this one via Foreign Key Constraints:

Index - Schema lsmb13


Table: lsmb13.account_link

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

Index - Schema lsmb13


Table: lsmb13.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.

lsmb13.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:

Index - Schema lsmb13


Table: lsmb13.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.

lsmb13.ap Structure
F-Key Name Type Description
lsmb13.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
lsmb13.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
lsmb13.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.
lsmb13.entity_credit_account.id entity_credit_account integer NOT NULL

reference for the vendor account used.

 

lsmb13.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:

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 lsmb13


Table: lsmb13.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.

lsmb13.ar Structure
F-Key Name Type Description
lsmb13.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
lsmb13.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
lsmb13.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.
lsmb13.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_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 lsmb13


Table: lsmb13.assembly

Holds mapping for parts that are members of assemblies.

lsmb13.assembly Structure
F-Key Name Type Description
lsmb13.parts.id id integer PRIMARY KEY

This is the id of the assembly the part is being mapped to.
lsmb13.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 lsmb13


Table: lsmb13.asset_class

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

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

Tables referencing this one via Foreign Key Constraints:

Index - Schema lsmb13


Table: lsmb13.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.

lsmb13.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
lsmb13.asset_unit_class.id unit_class integer NOT NULL

Tables referencing this one via Foreign Key Constraints:

Index - Schema lsmb13


Table: lsmb13.asset_disposal_method

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

 

lsmb13.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:

Index - Schema lsmb13


Table: lsmb13.asset_item

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

lsmb13.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
lsmb13.warehouse.id location_id integer
lsmb13.department.id department_id integer
lsmb13.ap.id invoice_id integer
lsmb13.account.id asset_account_id integer
lsmb13.account.id dep_account_id integer
lsmb13.account.id exp_account_id integer
lsmb13.asset_item.id obsolete_by integer UNIQUE#1
lsmb13.asset_class.id asset_class_id integer

Tables referencing this one via Foreign Key Constraints:

Index - Schema lsmb13


Table: lsmb13.asset_note

lsmb13.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 ''::pg_catalog.tsvector
created timestamp without time zone NOT NULL DEFAULT now()
created_by text DEFAULT "session_user"()
lsmb13.asset_item.id ref_key integer NOT NULL
subject text

Table lsmb13.asset_note Inherits note,

 

lsmb13.asset_note Constraints
Name Constraint
asset_note_note_class_check CHECK ((note_class = 4))

Index - Schema lsmb13


Table: lsmb13.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.

lsmb13.asset_report Structure
F-Key Name Type Description
id serial PRIMARY KEY
report_date date
lsmb13.gl.id gl_id bigint UNIQUE
lsmb13.asset_class.id asset_class bigint
lsmb13.asset_report_class.id report_class integer
lsmb13.entity.id entered_by bigint NOT NULL DEFAULT person__get_my_entity_id()
lsmb13.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:

Index - Schema lsmb13


Table: lsmb13.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.

lsmb13.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:

Index - Schema lsmb13


Table: lsmb13.asset_report_line

lsmb13.asset_report_line Structure
F-Key Name Type Description
lsmb13.asset_item.id asset_id bigint PRIMARY KEY
lsmb13.asset_report.id report_id bigint PRIMARY KEY
amount numeric
lsmb13.department.id department_id integer

In case assets are moved between departments, we have to store this here.
lsmb13.warehouse.id warehouse_id integer

Index - Schema lsmb13


Table: lsmb13.asset_rl_to_disposal_method

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

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

Index - Schema lsmb13


Table: lsmb13.asset_unit_class

lsmb13.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:

Index - Schema lsmb13


Table: lsmb13.audittrail

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

lsmb13.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()
lsmb13.person.entity_id person_id integer NOT NULL
entry_id bigserial PRIMARY KEY
audittrail_trans_id_key trans_id

Index - Schema lsmb13


Table: lsmb13.batch

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

lsmb13.batch Structure
F-Key Name Type Description
id serial PRIMARY KEY
lsmb13.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
lsmb13.entity_employee.entity_id approved_by integer
lsmb13.entity_employee.entity_id created_by integer
lsmb13.session.session_id locked_by integer
created_on date DEFAULT now()

 

lsmb13.batch Constraints
Name Constraint
batch_control_code_check CHECK ((length(control_code) > 0))

Tables referencing this one via Foreign Key Constraints:

Index - Schema lsmb13


Table: lsmb13.batch_class

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

lsmb13.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:

Index - Schema lsmb13


Table: lsmb13.budget_info

lsmb13.budget_info Structure
F-Key Name Type Description
id serial UNIQUE NOT NULL
start_date date NOT NULL
end_date date NOT NULL
reference text PRIMARY KEY
description text NOT NULL
lsmb13.entity.id entered_by integer NOT NULL DEFAULT person__get_my_entity_id()
lsmb13.entity.id approved_by integer
lsmb13.entity.id obsolete_by integer
entered_at timestamp without time zone NOT NULL DEFAULT now()
approved_at timestamp without time zone
obsolete_at timestamp without time zone

 

lsmb13.budget_info Constraints
Name Constraint
budget_info_check CHECK ((start_date < end_date))

Tables referencing this one via Foreign Key Constraints:

Index - Schema lsmb13


Table: lsmb13.budget_line

lsmb13.budget_line Structure
F-Key Name Type Description
lsmb13.budget_info.id budget_id integer PRIMARY KEY
lsmb13.account.id account_id integer PRIMARY KEY
description text
amount numeric NOT NULL

Index - Schema lsmb13


Table: lsmb13.budget_note

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

Table lsmb13.budget_note Inherits note,

 

lsmb13.budget_note Constraints
Name Constraint
budget_note_note_class_check CHECK ((note_class = 6))

Index - Schema lsmb13


Table: lsmb13.budget_to_department

lsmb13.budget_to_department Structure
F-Key Name Type Description
lsmb13.budget_info.id budget_id integer PRIMARY KEY
lsmb13.department.id department_id integer NOT NULL

Index - Schema lsmb13


Table: lsmb13.budget_to_project

lsmb13.budget_to_project Structure
F-Key Name Type Description
lsmb13.budget_info.id budget_id integer PRIMARY KEY
lsmb13.project.id project_id integer NOT NULL

Index - Schema lsmb13


Table: lsmb13.business

Groups of Customers assigned joint discounts.

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

Index - Schema lsmb13


View: lsmb13.chart

Compatibility chart for 1.2 and earlier.

lsmb13.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'::text AS charttype
, NULL::bpchar AS category
, NULL::text AS link
, NULL::integer AS account_heading
, NULL::text AS gifi_accno
, false AS contra
, false AS tax 
FROM lsmb13.account_heading 
UNIONSELECT c.id
, c.accno
, c.description
,'A'::text AS charttype
, c.category
, lsmb13.concat_colon
(l.description) AS link
, c.heading AS account_heading
, c.gifi_accno
, c.contra
, c.tax 
FROM (lsmb13.account c 
LEFT JOIN lsmb13.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 lsmb13


Table: lsmb13.company

lsmb13.company Structure
F-Key Name Type Description
id serial UNIQUE NOT NULL
lsmb13.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

 

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

Tables referencing this one via Foreign Key Constraints:

company_name_gist__idx legal_name lsmb13.gist_trgm_ops

Index - Schema lsmb13


Table: lsmb13.company_to_contact

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

 

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

Index - Schema lsmb13


Table: lsmb13.company_to_entity

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

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

 

lsmb13.company_to_entity Constraints
Name Constraint
company_to_entity_check CHECK ((company_id <> entity_id))

Index - Schema lsmb13


Table: lsmb13.company_to_location

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

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

Index - Schema lsmb13


Table: lsmb13.contact_class

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

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

 

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

Tables referencing this one via Foreign Key Constraints:

Index - Schema lsmb13


Table: lsmb13.country

lsmb13.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

 

lsmb13.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:

Index - Schema lsmb13


Table: lsmb13.country_tax_form

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

lsmb13.country_tax_form Structure
F-Key Name Type Description
lsmb13.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:

Index - Schema lsmb13


Table: lsmb13.cr_coa_to_account

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

Index - Schema lsmb13


Table: lsmb13.cr_report

lsmb13.cr_report Structure
F-Key Name Type Description
id bigserial PRIMARY KEY
lsmb13.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()
lsmb13.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
lsmb13.entity.id deleted_by integer
lsmb13.entity.id approved_by integer
approved_username text
recon_fx boolean DEFAULT false

 

lsmb13.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:

Index - Schema lsmb13


Table: lsmb13.cr_report_line

lsmb13.cr_report_line Structure
F-Key Name Type Description
id bigserial PRIMARY KEY
lsmb13.cr_report.id report_id integer NOT NULL
scn text
their_balance numeric
our_balance numeric
errorcode integer
lsmb13.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
lsmb13.acc_trans.entry_id lsmb13.acc_trans.entry_id ledger_id integer
lsmb13.voucher.id voucher_id integer
overlook boolean NOT NULL DEFAULT false
cleared boolean NOT NULL DEFAULT false

Index - Schema lsmb13


Table: lsmb13.custom_field_catalog

Deprecated, use only with old code.

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

Index - Schema lsmb13


Table: lsmb13.custom_table_catalog

Deprecated, use only with old code.

lsmb13.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:

Index - Schema lsmb13


Table: lsmb13.customertax

Mapping customer to taxes.

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

Index - Schema lsmb13


Table: lsmb13.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.

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

Index - Schema lsmb13


Table: lsmb13.department

lsmb13.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:

department_id_key id

Index - Schema lsmb13


Table: lsmb13.dpt_trans

Department to Transaction Map

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

Index - Schema lsmb13


Table: lsmb13.eca_note

Notes for entity_credit_account entries.

lsmb13.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 ''::pg_catalog.tsvector
created timestamp without time zone NOT NULL DEFAULT now()
created_by text DEFAULT "session_user"()
lsmb13.entity_credit_account.id ref_key integer NOT NULL

references entity_credit_account.id
subject text

Table lsmb13.eca_note Inherits note,

 

lsmb13.eca_note Constraints
Name Constraint
eca_note_note_class_check CHECK ((note_class = 3))

Index - Schema lsmb13


Table: lsmb13.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.

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

 

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

Index - Schema lsmb13


Table: lsmb13.eca_to_location

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

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

Index - Schema lsmb13


View: lsmb13.employee_search

lsmb13.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 (
     (
           (
                 (lsmb13.entity_employee e 
               LEFT JOIN lsmb13.entity en 
                      ON (
                             (e.entity_id = en.id)
                       )
                 )
         LEFT JOIN lsmb13.entity_employee m 
                ON (
                       (e.manager_id = m.entity_id)
                 )
           )
   LEFT JOIN lsmb13.entity em 
          ON (
                 (em.id = m.entity_id)
           )
     )
LEFT JOIN lsmb13.entity_note emn 
    ON (
           (emn.ref_key = em.id)
     )
);

Index - Schema lsmb13


View: lsmb13.employees

lsmb13.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 (
     (lsmb13.person p 
        JOIN lsmb13.entity_employee ee 
       USING (entity_id)
     )
LEFT JOIN lsmb13.salutation s 
    ON (
           (p.salutation_id = s.id)
     )
);

Index - Schema lsmb13


Table: lsmb13.entity

The primary entity table to map to all contacts

lsmb13.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
lsmb13.entity_class.id lsmb13.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)
lsmb13.country.id country_id integer NOT NULL

 

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

Tables referencing this one via Foreign Key Constraints:

entity_name_gist_idx name lsmb13.gist_trgm_ops

Index - Schema lsmb13


Table: lsmb13.entity_bank_account

This stores bank account information for both companies and persons.

lsmb13.entity_bank_account Structure
F-Key Name Type Description
id serial UNIQUE NOT NULL
lsmb13.entity.id entity_id integer NOT NULL
bic character varying UNIQUE#1

Banking Institution Code, such as routing number of SWIFT code.
iban character varying UNIQUE#1 NOT NULL

International Bank Account Number. used to store the actual account number for the banking institution.

Tables referencing this one via Foreign Key Constraints:

Index - Schema lsmb13


Table: lsmb13.entity_class

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

lsmb13.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
lsmb13.country.id country_id integer
active boolean NOT NULL DEFAULT true

 

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

Tables referencing this one via Foreign Key Constraints:

entity_class_idx lower(class)

Index - Schema lsmb13


Table: lsmb13.entity_class_to_entity

Relation builder for classes to entity

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

Index - Schema lsmb13


Table: lsmb13.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.

lsmb13.entity_credit_account Structure
F-Key Name Type Description
id serial UNIQUE NOT NULL
lsmb13.entity.id entity_id integer PRIMARY KEY
lsmb13.entity_class.id entity_class integer PRIMARY KEY
pay_to_name text
discount numeric
description text
discount_terms integer
lsmb13.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
lsmb13.language.code language_code character varying(6)
lsmb13.pricegroup.id pricegroup_id integer
curr character(3)
startdate date DEFAULT ('now'::text)::date
enddate date
threshold numeric
lsmb13.entity_employee.entity_id employee_id integer
lsmb13.person.id primary_contact integer
lsmb13.account.id ar_ap_account_id integer
lsmb13.account.id cash_account_id integer
lsmb13.entity_bank_account.id bank_account integer
lsmb13.country_tax_form.id taxform_id integer

 

lsmb13.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:

Index - Schema lsmb13


Table: lsmb13.entity_employee

This contains employee-specific extensions to person/entity.

lsmb13.entity_employee Structure
F-Key Name Type Description
lsmb13.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
lsmb13.entity.id manager_id integer
employeenumber character varying(32)
dob date

Tables referencing this one via Foreign Key Constraints:

Index - Schema lsmb13


Table: lsmb13.entity_note

lsmb13.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 ''::pg_catalog.tsvector
created timestamp without time zone NOT NULL DEFAULT now()
created_by text DEFAULT "session_user"()
lsmb13.entity.id ref_key integer NOT NULL
subject text
lsmb13.entity.id entity_id integer

Table lsmb13.entity_note Inherits note,

 

lsmb13.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 lsmb13.gist_trgm_ops

Index - Schema lsmb13


Table: lsmb13.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.

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

 

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

Index - Schema lsmb13


Table: lsmb13.exchangerate

lsmb13.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 lsmb13


Table: lsmb13.file_base

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

lsmb13.file_base Structure
F-Key Name Type Description
content bytea NOT NULL
lsmb13.mime_type.id mime_type_id integer NOT NULL
file_name text PRIMARY KEY
description text
lsmb13.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.
lsmb13.file_class.id file_class integer PRIMARY KEY

Index - Schema lsmb13


Table: lsmb13.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.

lsmb13.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:

Index - Schema lsmb13


View: lsmb13.file_links

lsmb13.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 lsmb13.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 lsmb13.file_order_links;

Index - Schema lsmb13


Table: lsmb13.file_order

lsmb13.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)
lsmb13.oe.id ref_key integer PRIMARY KEY
file_class integer PRIMARY KEY

Table lsmb13.file_order Inherits file_base,

 

lsmb13.file_order Constraints
Name Constraint
file_order_file_class_check CHECK ((file_class = 2))

Tables referencing this one via Foreign Key Constraints:

Index - Schema lsmb13


View: lsmb13.file_order_links

lsmb13.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 (
     (lsmb13.file_secondary_attachment sl 
        JOIN lsmb13.oe 
          ON (
                 (sl.ref_key = oe.id)
           )
     )
  JOIN lsmb13.oe_class oc 
    ON (
           (oe.oe_class_id = oc.id)
     )
)
WHERE (sl.source_class = 2);

Index - Schema lsmb13


Table: lsmb13.file_order_to_order

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

lsmb13.file_order_to_order Structure
F-Key Name Type Description
lsmb13.file_order.id file_id integer PRIMARY KEY
source_class integer PRIMARY KEY
lsmb13.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 lsmb13.file_order_to_order Inherits file_secondary_attachment,

 

lsmb13.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 lsmb13


Table: lsmb13.file_order_to_tx

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

lsmb13.file_order_to_tx Structure
F-Key Name Type Description
lsmb13.file_order.id file_id integer PRIMARY KEY
source_class integer PRIMARY KEY
lsmb13.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 lsmb13.file_order_to_tx Inherits file_secondary_attachment,

 

lsmb13.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 lsmb13


Table: lsmb13.file_part

File attachments primarily attached to orders and quotations.

lsmb13.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)
lsmb13.parts.id ref_key integer PRIMARY KEY
file_class integer PRIMARY KEY

Table lsmb13.file_part Inherits file_base,

 

lsmb13.file_part Constraints
Name Constraint
file_part_file_class_check CHECK ((file_class = 3))

Index - Schema lsmb13


Table: lsmb13.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.

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

Index - Schema lsmb13


Table: lsmb13.file_transaction

File attachments primarily attached to orders and quotatoins.

lsmb13.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)
lsmb13.transactions.id ref_key integer PRIMARY KEY
file_class integer PRIMARY KEY

Table lsmb13.file_transaction Inherits file_base,

 

lsmb13.file_transaction Constraints
Name Constraint
file_transaction_file_class_check CHECK ((file_class = 1))

Tables referencing this one via Foreign Key Constraints:

Index - Schema lsmb13


View: lsmb13.file_tx_links

lsmb13.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 (lsmb13.file_secondary_attachment sl 
  JOIN (
           (
            SELECT gl.id
                 , gl.reference
                 ,'gl'::text AS type 
              FROM lsmb13.gl 
             UNIONSELECT ar.id
                 , ar.invnumber
                 , CASE WHEN ar.invoice THEN 'is'::text ELSE 'ar'::text END AS type 
              FROM lsmb13.ar
           )
       UNIONSELECT ap.id
           , ap.invnumber
           , CASE WHEN ap.invoice THEN 'ir'::text ELSE 'ap'::text END AS type 
        FROM lsmb13.ap
     ) gl 
    ON (
           (
                 (sl.ref_key = gl.id)
               AND (sl.source_class = 1)
           )
     )
);

Index - Schema lsmb13


Table: lsmb13.file_tx_to_order

Secondary links from transactions to orders.

lsmb13.file_tx_to_order Structure
F-Key Name Type Description
lsmb13.file_transaction.id file_id integer PRIMARY KEY
source_class integer PRIMARY KEY
lsmb13.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 lsmb13.file_tx_to_order Inherits file_secondary_attachment,

 

lsmb13.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 lsmb13


Table: lsmb13.file_view_catalog

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

Index - Schema lsmb13


Table: lsmb13.gifi

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

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

Index - Schema lsmb13


Table: lsmb13.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.

lsmb13.gl Structure
F-Key Name Type Description
lsmb13.transactions.id id integer PRIMARY KEY DEFAULT nextval('id'::regclass)
reference text
description text
transdate date DEFAULT ('now'::text)::date
lsmb13.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:

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

Index - Schema lsmb13


Table: lsmb13.inventory

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

lsmb13.inventory Structure
F-Key Name Type Description
lsmb13.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 lsmb13


Table: lsmb13.invoice

Line items of invoices with goods/services attached.

lsmb13.invoice Structure
F-Key Name Type Description
id serial PRIMARY KEY
lsmb13.transactions.id trans_id integer
lsmb13.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_id_key id invoice_trans_id_key trans_id

Index - Schema lsmb13


Table: lsmb13.invoice_note

lsmb13.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 ''::pg_catalog.tsvector
created timestamp without time zone NOT NULL DEFAULT now()
created_by text DEFAULT "session_user"()
lsmb13.invoice.id ref_key integer NOT NULL
subject text

Table lsmb13.invoice_note Inherits note,

invoice_note_id_idx id invoice_note_vectors_idx vector

Index - Schema lsmb13


Table: lsmb13.invoice_tax_form

Maping invoice to country_tax_form.

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

Index - Schema lsmb13


Table: lsmb13.jcitems

Time and materials cards. Materials cards not implemented.

lsmb13.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
lsmb13.person.id person_id integer NOT NULL
notes text
total numeric NOT NULL
non_billable numeric NOT NULL
jcitems_id_key id

Index - Schema lsmb13


Table: lsmb13.language

Languages for manual translations and so forth.

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

Tables referencing this one via Foreign Key Constraints:

Index - Schema lsmb13


Table: lsmb13.location

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

lsmb13.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
lsmb13.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

 

lsmb13.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:

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

Index - Schema lsmb13


Table: lsmb13.location_class

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

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

 

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

Tables referencing this one via Foreign Key Constraints:

Index - Schema lsmb13


Table: lsmb13.lsmb_group

lsmb13.lsmb_group Structure
F-Key Name Type Description
role_name text PRIMARY KEY

Tables referencing this one via Foreign Key Constraints:

Index - Schema lsmb13


Table: lsmb13.lsmb_group_grants

lsmb13.lsmb_group_grants Structure
F-Key Name Type Description
lsmb13.lsmb_group.role_name group_name text PRIMARY KEY
granted_role text PRIMARY KEY

Index - Schema lsmb13


Table: lsmb13.lsmb_roles

A beginning of a group tracking system. Not exposed through the front end yet.

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

Index - Schema lsmb13


Table: lsmb13.makemodel

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

lsmb13.makemodel Structure
F-Key Name Type Description
parts_id integer PRIMARY KEY
make text PRIMARY KEY
model text PRIMARY KEY
makemodel_make_key lower(make) makemodel_model_key lower(model) makemodel_parts_id_key parts_id

Index - Schema lsmb13


Table: lsmb13.menu_acl

Provides access control list entries for menu nodes.

lsmb13.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'.
lsmb13.menu_node.id node_id integer PRIMARY KEY

 

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

Index - Schema lsmb13


Table: lsmb13.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.

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

Index - Schema lsmb13


View: lsmb13.menu_friendly

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

lsmb13.menu_friendly Structure
F-Key Name Type Description
level integer
path text
label text
id integer
position integer
WITH RECURSIVE tree
(path
     , id
     , parent
     , level
     , positions
) AS 
(
SELECT (menu_node.id)::text AS path
     , menu_node.id
     , menu_node.parent
     , 0 AS level
     , (menu_node."position")::text AS "position"
  FROM lsmb13.menu_node 
 WHERE (menu_node.parent IS NULL)
 UNIONSELECT (
           (t.path || 
               ','::text
           ) || 
           (n.id)::text
     )
     , n.id
     , n.parent
     , (t.level + 1)
     , (
           (t.positions || 
               ','::text
           ) || n."position"
     )
  FROM (lsmb13.menu_node n 
        JOIN tree t 
          ON (
                 (t.id = n.parent)
           )
     )
)
SELECT t.level
, t.path
, (repeat
     (' '::text
           , (2 * t.level)
     ) || 
     (n.label)::text
) AS label
, n.id
, n."position"
FROM (tree t 
  JOIN lsmb13.menu_node n 
 USING (id)
)
ORDER BY (string_to_array
     (t.positions
           ,','::text
     )
)::integer[];

Index - Schema lsmb13


Table: lsmb13.menu_node

This table stores the tree structure of the menu.

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

Tables referencing this one via Foreign Key Constraints:

Index - Schema lsmb13


Table: lsmb13.mime_type

This is a lookup table for storing MIME types.

lsmb13.mime_type Structure
F-Key Name Type Description
id serial UNIQUE NOT NULL
mime_type text PRIMARY KEY
invoice_include boolean DEFAULT false

Tables referencing this one via Foreign Key Constraints:

Index - Schema lsmb13


Table: lsmb13.new_shipto

Tracks ship_to information for orders and invoices.

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

Index - Schema lsmb13


Table: lsmb13.note

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

lsmb13.note Structure
F-Key Name Type Description
id serial PRIMARY KEY
lsmb13.note_class.id note_class integer NOT NULL
note text NOT NULL

Body of note.
vector tsvector NOT NULL DEFAULT ''::pg_catalog.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 lsmb13


Table: lsmb13.note_class

Coordinate with others before adding entries.

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

 

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

Tables referencing this one via Foreign Key Constraints:

Index - Schema lsmb13


Table: lsmb13.oe

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

lsmb13.oe Structure
F-Key Name Type Description
id serial PRIMARY KEY
ordnumber text
transdate date DEFAULT ('now'::text)::date
lsmb13.entity.id entity_id integer
amount numeric
netamount numeric
reqdate date
taxincluded boolean
shippingpoint text
notes text
curr character(3)
lsmb13.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
lsmb13.entity_credit_account.id entity_credit_account integer NOT NULL
lsmb13.oe_class.id oe_class_id integer NOT NULL

Tables referencing this one via Foreign Key Constraints:

oe_id_key id oe_ordnumber_key ordnumber oe_transdate_key transdate

Index - Schema lsmb13


Table: lsmb13.oe_class

Hardwired classifications for orders and quotations. Coordinate before adding.

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

 

lsmb13.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:

Index - Schema lsmb13


Table: lsmb13.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.

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

Index - Schema lsmb13


Table: lsmb13.orderitems

Line items for sales/purchase orders and quotations.

lsmb13.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 lsmb13


View: lsmb13.overpayments

lsmb13.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
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
, abs
(sum
     (ac.amount)
) AS available
, cmp.legal_name
, eca.id AS entity_credit_id
, eca.entity_id
, eca.discount
, eca.meta_number 
FROM (
     (
           (
                 (
                       (lsmb13.payment p 
                          JOIN lsmb13.payment_links pl 
                            ON (
                                   (pl.payment_id = p.id)
                             )
                       )
                    JOIN lsmb13.acc_trans ac 
                      ON (
                             (ac.entry_id = pl.entry_id)
                       )
                 )
              JOIN lsmb13.chart c 
                ON (
                       (c.id = ac.chart_id)
                 )
           )
        JOIN lsmb13.entity_credit_account eca 
          ON (
                 (eca.id = p.entity_credit_id)
           )
     )
  JOIN lsmb13.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
, cmp.legal_name
, eca.id
, eca.entity_id
, eca.discount
, eca.meta_number;

Index - Schema lsmb13


Table: lsmb13.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.

lsmb13.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:

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

Index - Schema lsmb13


Table: lsmb13.parts_translation

Translation information for parts.

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

Table lsmb13.parts_translation Inherits translation,

Index - Schema lsmb13


Table: lsmb13.partscustomer

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

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

Index - Schema lsmb13


Table: lsmb13.partsgroup

Groups of parts for Point of Sale screen.

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

Tables referencing this one via Foreign Key Constraints:

partsgroup_id_key id

Index - Schema lsmb13


Table: lsmb13.partsgroup_translation

Translation information for partsgroups.

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

Table lsmb13.partsgroup_translation Inherits translation,

Index - Schema lsmb13


Table: lsmb13.partstax

Mapping of parts to taxes.

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

Index - Schema lsmb13


Table: lsmb13.partsvendor

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

lsmb13.partsvendor Structure
F-Key Name Type Description
lsmb13.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 lsmb13


Table: lsmb13.payment

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

lsmb13.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
lsmb13.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
lsmb13.entity_credit_account.id entity_credit_id integer
lsmb13.person.id employee_id integer
currency character(3)
notes text
department_id integer

Tables referencing this one via Foreign Key Constraints:

payment_id_idx id

Index - Schema lsmb13


Table: lsmb13.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.

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

Index - Schema lsmb13


Table: lsmb13.payment_type

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

Index - Schema lsmb13


Table: lsmb13.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.

lsmb13.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
lsmb13.pending_job.id job_id integer DEFAULT currval('pending_job_id_seq'::regclass)
payments_queue_job_id job_id

Index - Schema lsmb13


Table: lsmb13.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.

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

 

lsmb13.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:

pending_job_batch_id_pending batch_id) WHERE (success IS NULL pending_job_entered_by entered_by

Index - Schema lsmb13


View: lsmb13.periods

lsmb13.periods Structure
F-Key Name Type Description
id text
label text
date_to date
date_from date
SELECT'ytd'::text AS id
,'Year to Date'::text AS label
, (now
     ()
)::date AS date_to
, (
     (
           (date_part
                 ('year'::text
                       , now
                       ()
                 )
           )::text || '-01-01'::text
     )
)::date AS date_from 
UNIONSELECT'last_year'::text AS id
,'Last Year'::text 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 lsmb13


Table: lsmb13.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.

lsmb13.person Structure
F-Key Name Type Description
id serial PRIMARY KEY
lsmb13.entity.id entity_id integer UNIQUE NOT NULL
lsmb13.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

 

lsmb13.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:

Index - Schema lsmb13


Table: lsmb13.person_to_company

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

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

Index - Schema lsmb13


Table: lsmb13.person_to_contact

This table stores contact information for companies

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

 

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

Index - Schema lsmb13


Table: lsmb13.person_to_entity

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

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

 

lsmb13.person_to_entity Constraints
Name Constraint
person_to_entity_check CHECK ((entity_id <> person_id))

Index - Schema lsmb13


Table: lsmb13.person_to_location

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

Index - Schema lsmb13


Table: lsmb13.pricegroup

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

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

Tables referencing this one via Foreign Key Constraints:

pricegroup_id_key id pricegroup_pricegroup_key pricegroup

Index - Schema lsmb13


Table: lsmb13.project

lsmb13.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
lsmb13.entity_credit_account.id credit_id integer

Tables referencing this one via Foreign Key Constraints:

project_id_key id

Index - Schema lsmb13


Table: lsmb13.project_translation

Translation information for projects.

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

Table lsmb13.project_translation Inherits translation,

Index - Schema lsmb13


View: lsmb13.recon_payee

lsmb13.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
, ac.voucher_id
, rr.overlook
, rr.cleared 
FROM (
     (
           (lsmb13.cr_report_line rr 
         LEFT JOIN lsmb13.acc_trans ac 
                ON (
                       (rr.ledger_id = ac.entry_id)
                 )
           )
   LEFT JOIN lsmb13.gl 
          ON (
                 (ac.trans_id = gl.id)
           )
     )
LEFT JOIN (
           (
            SELECT ap.id
                 , e.name 
              FROM (
                       (lsmb13.ap 
                          JOIN lsmb13.entity_credit_account eca 
                            ON (
                                   (ap.entity_credit_account = eca.id)
                             )
                       )
                    JOIN lsmb13.entity e 
                      ON (
                             (eca.entity_id = e.id)
                       )
                 )
             UNIONSELECT ar.id
                 , e.name 
              FROM (
                       (lsmb13.ar 
                          JOIN lsmb13.entity_credit_account eca 
                            ON (
                                   (ar.entity_credit_account = eca.id)
                             )
                       )
                    JOIN lsmb13.entity e 
                      ON (
                             (eca.entity_id = e.id)
                       )
                 )
           )
       UNIONSELECT gl.id
           , gl.description 
        FROM lsmb13.gl
     ) n 
    ON (
           (n.id = ac.trans_id)
     )
);

Index - Schema lsmb13


Table: lsmb13.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.

lsmb13.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 lsmb13


Table: lsmb13.recurringemail

Email to be sent out when recurring transaction is posted.

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

Index - Schema lsmb13


Table: lsmb13.recurringprint

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

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

Index - Schema lsmb13


View: lsmb13.role_view

lsmb13.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 lsmb13


Table: lsmb13.salutation

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

Tables referencing this one via Foreign Key Constraints:

Index - Schema lsmb13


Table: lsmb13.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.

lsmb13.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
lsmb13.users.id users_id integer NOT NULL
notify_pasword interval NOT NULL DEFAULT '7 days'::interval

 

lsmb13.session Constraints
Name Constraint
session_token_check CHECK ((length((token)::text) = 32))

Tables referencing this one via Foreign Key Constraints:

Index - Schema lsmb13


Table: lsmb13.sic

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

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

Index - Schema lsmb13


Table: lsmb13.status

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

lsmb13.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 lsmb13


Table: lsmb13.tax

Information on tax rates.

lsmb13.tax Structure
F-Key Name Type Description
lsmb13.account.id lsmb13.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).
lsmb13.taxmodule.taxmodule_id taxmodule_id integer NOT NULL DEFAULT 1
minvalue numeric
maxvalue numeric

Index - Schema lsmb13


Table: lsmb13.tax_extended

This stores extended information for manual tax calculations.

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

Index - Schema lsmb13


Table: lsmb13.taxcategory

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

Tables referencing this one via Foreign Key Constraints:

Index - Schema lsmb13


Table: lsmb13.taxmodule

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

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

Tables referencing this one via Foreign Key Constraints:

Index - Schema lsmb13


Table: lsmb13.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.

lsmb13.transactions Structure
F-Key Name Type Description
id integer PRIMARY KEY
table_name text
lsmb13.session.session_id locked_by integer

This should only be used in pessimistic locking measures as required by large batch work flows.
lsmb13.entity.id approved_by integer
approved_at timestamp without time zone

Tables referencing this one via Foreign Key Constraints:

transactions_locked_by_i locked_by

Index - Schema lsmb13


Table: lsmb13.translation

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

lsmb13.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 lsmb13


Table: lsmb13.trial_balance

lsmb13.trial_balance Structure
F-Key Name Type Description
id serial PRIMARY KEY
date_from date
date_to date
description text NOT NULL
lsmb13.trial_balance__yearend_types.type yearend text NOT NULL

Tables referencing this one via Foreign Key Constraints:

Index - Schema lsmb13


Table: lsmb13.trial_balance__account_to_report

lsmb13.trial_balance__account_to_report Structure
F-Key Name Type Description
lsmb13.trial_balance.id report_id integer NOT NULL
lsmb13.account.id account_id integer NOT NULL

Index - Schema lsmb13


Table: lsmb13.trial_balance__heading_to_report

lsmb13.trial_balance__heading_to_report Structure
F-Key Name Type Description
lsmb13.trial_balance.id report_id integer NOT NULL
lsmb13.account_heading.id heading_id integer NOT NULL

Index - Schema lsmb13


Table: lsmb13.trial_balance__yearend_types

lsmb13.trial_balance__yearend_types Structure
F-Key Name Type Description
type text PRIMARY KEY

Tables referencing this one via Foreign Key Constraints:

Index - Schema lsmb13


View: lsmb13.user_listable

lsmb13.user_listable Structure
F-Key Name Type Description
id integer
username character varying(30)
created date
SELECT u.id
, u.username
, e.created 
FROM (lsmb13.entity e 
  JOIN lsmb13.users u 
    ON (
           (u.entity_id = e.id)
     )
);

Index - Schema lsmb13


Table: lsmb13.user_preference

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

lsmb13.user_preference Structure
F-Key Name Type Description
lsmb13.users.id id integer PRIMARY KEY
lsmb13.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 lsmb13


Table: lsmb13.users

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

lsmb13.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
lsmb13.entity.id entity_id integer NOT NULL

Tables referencing this one via Foreign Key Constraints:

Index - Schema lsmb13


Table: lsmb13.vendortax

Mapping vendor to taxes.

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

Index - Schema lsmb13


Table: lsmb13.voucher

Mapping transactions to batches for batch approval.

lsmb13.voucher Structure
F-Key Name Type Description
lsmb13.transactions.id trans_id integer NOT NULL
lsmb13.batch.id batch_id integer NOT NULL
id serial PRIMARY KEY

This is simply a surrogate key for easy reference.
lsmb13.batch_class.id batch_class integer NOT NULL

This is the authoritative class of the voucher.

Tables referencing this one via Foreign Key Constraints:

Index - Schema lsmb13


Table: lsmb13.warehouse

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

Tables referencing this one via Foreign Key Constraints:

Index - Schema lsmb13


Table: lsmb13.yearend

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

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

Index - Schema lsmb13


Function: lsmb13._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: lsmb13._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: lsmb13.account__delete(in_id integer)

Returns: boolean

Language: PLPGSQL

This deletes an account with the id specified. If the account has transactions associated with it, it will fail and raise a foreign key constraint.

BEGIN
DELETE FROM account_link WHERE account_id = in_id;
DELETE FROM account WHERE id = in_id;
RETURN FOUND;
END;

Function: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.account__is_recon(in_accno text)

Returns: boolean

Language: SQL

Returns true if account is set up for reconciliation, false otherwise. Note that returns false on invalid account number too

 SELECT count(*) > 0 
     FROM cr_coa_to_account c2a
     JOIN account ON account.id = c2a.chart_id 
    WHERE accno = $1; 

Function: lsmb13.account__list_by_heading()

Returns: SET OF account

Language: SQL

SELECT * FROM account ORDER BY heading;

Function: lsmb13.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: lsmb13.account__save(in_is_temp integer, in_obsolete text, in_link text, in_tax bpchar, in_contra text, in_heading integer, in_gifi_accno boolean, in_category boolean, in_description text[], in_accno boolean, in_id boolean)

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,
                obsolete = in_obsolete,
                tax = t_tax,
                is_temp = in_is_temp
	WHERE id = in_id;

	IF FOUND THEN
		t_id := in_id;
	ELSE
                -- can't obsolete on insert, but this can be changed if users
                -- request it --CT
		INSERT INTO account (accno, description, category, gifi_accno,
			heading, contra, tax, is_temp)
		VALUES (in_accno, in_description, in_category, in_gifi_accno,
			t_heading_id, in_contra, in_tax, in_is_temp);

		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: lsmb13.account__save_tax(in_old_validto integer, in_taxmodule_id date, in_pass numeric, in_taxnumber numeric, in_maxvalue numeric, in_minvalue 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,
               minvalue = in_minvalue,
               maxvalue = in_maxvalue,
               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, minvalue, maxvalue, taxnumber,
                        pass, taxmodule_id)
         VALUES (in_chart_id, in_validto, in_rate, in_minvalue, in_maxvalue, 
                in_taxnumber, in_pass, in_taxmodule_id);

         RETURN TRUE;

END;

Function: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.account_heading_list()

Returns: SET OF account_heading

Language: SQL

Lists all existing account headings.

SELECT * FROM account_heading order by accno;

Function: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.asset_dep_straight_line_yr_d(in_report_id integer[], in_report_date date, in_asset_ids integer)

Returns: boolean

Language: SQL

     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, -- years
                  ai.usable_life - 
                  get_fractional_year(coalesce(max(report_date),
                                         start_depreciation,
                                         purchase_date),
                                       coalesce(start_depreciation,
                                         purchase_date)),
                  get_fractional_year(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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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.id IS NULL OR 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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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
	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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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 batch WHERE id = in_batch_id;
	DELETE FROM transactions WHERE id = ANY(t_transaction_ids);

	RETURN 1;
END;

Function: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.budget__approve(in_id integer)

Returns: budget_info_ext

Language: SQL

UPDATE budget_info 
   set approved_at = now(), approved_by = person__get_my_entity_id()
 WHERE id = $1;

SELECT budget__get_info($1);

Function: lsmb13.budget__get_details(in_id integer)

Returns: SET OF budget_line

Language: SQL

This retrieves the budget lines associated with a budget.

  SELECT * FROM budget_line where budget_id = $1;

Function: lsmb13.budget__get_info(in_id integer)

Returns: budget_info_ext

Language: SQL

Selects the budget info.

 
select bi.id, bi.start_date, bi.end_date, bi.reference, bi.description, 
       bi.entered_by, bi.approved_by, bi.obsolete_by, bi.entered_at, 
       bi.approved_at, bi.obsolete_at, 
       ee.name, ae.name, oe.name, bd.department_id, d.description, 
       bp.project_id, p.projectnumber
  from budget_info bi
  JOIN entity ee ON bi.entered_by = ee.id
  LEFT JOIN budget_to_department bd ON bd.budget_id = bi.id
  LEFT JOIN entity ae ON bi.approved_by = ae.id
  LEFT JOIN entity oe ON bi.obsolete_by = oe.id
  LEFT JOIN budget_to_project bp ON bp.budget_id = bi.id
  LEFT JOIN department d ON d.id = bd.department_id
  LEFT JOIN project p ON bp.project_id = p.id
 where bi.id = $1; 

Function: lsmb13.budget__get_notes(in_id integer)

Returns: SET OF budget_note

Language: SQL

Returns all notes associated with a budget, by default in the order they were created.

 
SELECT * FROM budget_note WHERE ref_key = $1
 ORDER BY created;

Function: lsmb13.budget__mark_obsolete(in_id integer)

Returns: budget_info_ext

Language: SQL

Marks a budget as obsolete

UPDATE budget_info 
   set obsolete_by = person__get_my_entity_id(), obsolete_at = now()
 WHERE id = $1 and approved_by is not null;
SELECT budget__get_info($1)

Function: lsmb13.budget__reject(in_id integer)

Returns: boolean

Language: PLPGSQL

Deletes unapproved budgets only.

BEGIN

DELETE FROM budget_line 
 WHERE budget_id IN (SELECT id from budget_info 
                      WHERE id = in_id AND approved_by IS NULL);

DELETE FROM budget_to_project
 WHERE budget_id IN (SELECT id from budget_info 
                      WHERE id = in_id AND approved_by IS NULL);

DELETE FROM budget_to_department
 WHERE budget_id IN (SELECT id from budget_info 
                      WHERE id = in_id AND approved_by IS NULL);

DELETE FROM budget_info WHERE id = in_id AND approved_by IS NULL;

RETURN FOUND;
END;

Function: lsmb13.budget__save_details(in_details integer, in_id text[])

Returns: budget_info_ext

Language: PLPGSQL

This saves the line items for the budget. in_details is an array n long where each entry is {int account_id, text description, numeric amount}. The in_id parameter is the budget_id.

DECLARE
   loop_count int;
   retval budget_info_ext;
BEGIN
    FOR loop_count in   
        array_lower(in_details, 1) ..
        array_upper(in_details, 1)
    LOOP
        INSERT INTO budget_line 
                    (budget_id, 
                     account_id, 
                     description, 
                     amount)
             VALUES (in_id, 
                     in_details[loop_count][1]::int, 
                     in_details[loop_count][2], 
                     in_details[loop_count][3]::numeric);
    END LOOP;
    retval := budget__get_info(in_id);
    return retval;
END;

Function: lsmb13.budget__save_info(in_project_id integer, in_department_id date, in_description date, in_reference text, in_end_date text, in_start_date integer, in_id integer)

Returns: budget_info_ext

Language: PLPGSQL

Saves the extended budget info passed through to the function. See the comment on type budget_info_ext for more information.

DECLARE 
   retval budget_info_ext;
   t_id int;
BEGIN
  UPDATE budget_info
     SET start_date = in_start_date,
         end_date = in_end_date,
         reference = in_reference,
         description = in_description
   WHERE id = in_id and approved_by is null;
  IF FOUND THEN
      t_id := in_id;
  ELSE
       PERFORM * FROM budget_info WHERE id = in_id and approved_by is not null;
       IF FOUND THEN
           RAISE EXCEPTION 'report approved';
       END IF;
       INSERT INTO budget_info (start_date, end_date, reference, description)
            VALUES (in_start_date, in_end_date, in_reference, in_description);
       t_id = currval('budget_info_id_seq');
  END IF;
  IF in_project_id IS NOT NULL THEN
     UPDATE budget_to_project 
        SET project_id = in_project_id 
      WHERE budget_id = t_id;

     IF NOT FOUND THEN
        INSERT INTO budget_to_project(budget_id, project_id)
             VALUES (t_id, in_project_id);
     END IF;
  END IF;
  IF in_department_id IS NOT NULL THEN
     UPDATE budget_to_department 
        SET department_id = in_department_id 
      WHERE budget_id = t_id;

     IF NOT FOUND THEN
        INSERT INTO budget_to_department(budget_id, department_id)
             VALUES (t_id, in_department_id);
     END IF;
  END IF;
  retval := budget__get_info(t_id);
  return retval;
END;

Function: lsmb13.budget__save_note(in_note integer, in_subject text, in_id text)

Returns: budget_note

Language: SQL

Saves a note attached to a budget.

INSERT INTO budget_note (subject, note, ref_key) 
     values ($2, $3, $1);

SELECT * FROM budget_note WHERE id = currval('note_id_seq'::regclass);

Function: lsmb13.budget__search(in_is_obsolete date, in_is_approved date, in_project_id date, in_department_id text, in_obsolete_by text, in_approved_by integer, in_entered_by integer, in_description integer, in_reference integer, in_includes_date integer, in_end_date boolean, in_start_date boolean)

Returns: SET OF budget_info_ext

Language: SQL

This is a general search for budgets

select bi.id, bi.start_date, bi.end_date, bi.reference, bi.description, 
       bi.entered_by, bi.approved_by, bi.obsolete_by, bi.entered_at, 
       bi.approved_at, bi.obsolete_at, 
       ee.name, ae.name, oe.name, bd.department_id, d.description, 
       bp.project_id, p.projectnumber
  from budget_info bi 
  JOIN entity ee ON bi.entered_by = ee.id
  LEFT JOIN budget_to_department bd ON bd.budget_id = bi.id
  LEFT JOIN entity ae ON bi.approved_by = ae.id
  LEFT JOIN entity oe ON bi.obsolete_by = oe.id
  LEFT JOIN budget_to_project bp ON bp.budget_id = bi.id
  LEFT JOIN department d ON d.id = bd.department_id
  LEFT JOIN project p ON bp.project_id = p.id
 WHERE (start_date = $1 or $1 is null) AND ($2 = end_date or $2 is null) 
       AND ($3 BETWEEN start_date AND end_date or $2 is null)
       AND ($4 ilike reference || '%' or $4 is null) 
       AND (bi.description @@ plainto_tsquery($5) or $5 is null) 
       AND ($6 = entered_by or $6 is null) 
       AND ($7 = approved_by or $7 is null) 
       AND ($8 = obsolete_by or $8 is null) 
       AND ($9 = department_id OR $9 is null) 
       AND ($10 = project_id OR  $10 IS NULL)
       AND ($11 IS NULL OR ($11 = (approved_by IS NOT NULL)))
       AND ($12 IS NULL OR ($12 = (obsolete_by IS NOT NULL)))
 ORDER BY department_id, project_id, reference;

Function: lsmb13.budget__variance_report(in_id integer)

Returns: SET OF budget_variance_report

Language: SQL

Retrieves a variance report for budget with an id of in_id.

   WITH agg_account (amount, id, transdate)
        AS ( SELECT ac.amount *
                    CASE WHEN a.contra THEN -1 ELSE 1 END *
                    CASE WHEN a.category IN ('A', 'E') THEN -1 ELSE 1 END
                    AS amount,
                    ac.chart_id, ac.transdate
               FROM acc_trans ac
               JOIN account a ON ac.chart_id = a.id
           )
   SELECT act.accno, act.description, act.id, b.description, b.amount,
          coalesce(sum(a.amount), 0), 
          b.amount - coalesce(sum(a.amount), 0) AS variance
     FROM budget_info bi
     JOIN budget_line b ON bi.id = b.budget_id
     JOIN account act ON act.id = b.account_id
LEFT JOIN agg_account a ON a.transdate BETWEEN bi.start_date and bi.end_date
                           AND a.id = b.account_id
    WHERE bi.id = $1
 GROUP BY act.accno, act.description, act.id, b.description, b.amount
 ORDER BY act.accno;

Function: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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))
                       AND not obsolete
              ORDER BY accno
	LOOP
		RETURN next out_row;
	END LOOP;
END;

Function: lsmb13.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: lsmb13.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: lsmb13.cogs__add_for_ap(in_lastcost integer, in_qty numeric, in_parts_id numeric)

Returns: numeric

Language: PLPGSQL

DECLARE t_alloc numeric := 0;
        t_cogs numeric := 0;
        t_inv invoice;
        t_cp account_checkpoint;
        t_ar ar;
        t_avail numeric;
BEGIN


IF in_qty > 0 THEN
   return cogs__reverse_ap(in_parts_id, in_qty * -1) * in_lastcost;
END IF;

SELECT * INTO t_cp FROM account_checkpoint ORDER BY end_date DESC LIMIT 1;

FOR t_inv IN
    SELECT i.*
      FROM invoice i
      JOIN ar a ON a.id = i.trans_id
     WHERE qty + allocated > 0 and parts_id  = in_parts_id
  ORDER BY a.transdate, a.id, i.id
LOOP
   t_avail := t_inv.qty + t_inv.allocated;
   SELECT * INTO t_ar FROM ar WHERE id = t_inv.trans_id;
   IF t_alloc < in_qty THEN
       RAISE EXCEPTION 'TOO MANY ALLOCATED';
   ELSIF t_alloc = in_qty THEN
       return t_alloc;
   ELSIF (in_qty + t_alloc) * -1 <=  t_avail  THEN
       UPDATE invoice SET allocated = allocated + (in_qty + t_alloc)
        WHERE id = t_inv.id;

       INSERT INTO acc_trans 
              (chart_id, transdate, amount, invoice_id, approved, trans_id)
       SELECT expense_accno_id, 
              CASE WHEN t_ar.transdate > t_cp.end_date THEN t_ar.transdate
                   ELSE t_cp.end_date + '1 day'::interval
               END, (in_qty + t_alloc) * in_lastcost, t_inv.id, true,
              t_inv.trans_id
         FROM parts 
       WHERE  id = t_inv.parts_id AND inventory_accno_id IS NOT NULL
              AND expense_accno_id IS NOT NULL
       UNION
       SELECT income_accno_id,
              CASE WHEN t_ar.transdate > t_cp.end_date THEN t_ar.transdate
                   ELSE t_cp.end_date + '1 day'::interval
               END, -1 * (in_qty + t_alloc) * in_lastcost, t_inv.id, true,
              t_inv.trans_id
         FROM parts 
       WHERE  id = t_inv.parts_id AND inventory_accno_id IS NOT NULL
              AND expense_accno_id IS NOT NULL;
                                    
       t_cogs := t_cogs + (in_qty + t_alloc) * in_lastcost;
       return in_qty * -1;
   ELSE
       UPDATE invoice SET allocated = qty * -1
        WHERE id = t_inv.id;
       t_cogs := t_cogs + t_avail * in_lastcost;

       INSERT INTO acc_trans
              (chart_id, transdate, amount, invoice_id, approved, trans_id)
       SELECT expense_accno_id,
              CASE WHEN t_ar.transdate > t_cp.end_date THEN t_ar.transdate
                   ELSE t_cp.end_date + '1 day'::interval
               END,  -1 * t_avail * in_lastcost, 
              t_inv.id, true, t_inv.trans_id
         FROM parts
       WHERE  id = t_inv.parts_id AND inventory_accno_id IS NOT NULL
              AND expense_accno_id IS NOT NULL
       UNION
       SELECT income_accno_id,
              CASE WHEN t_ar.transdate > t_cp.end_date THEN t_ar.transdate
                   ELSE t_cp.end_date + '1 day'::interval
               END, -t_avail * in_lastcost, t_inv.id, true, t_inv.trans_id
         FROM parts
       WHERE  id = t_inv.parts_id AND inventory_accno_id IS NOT NULL
              AND expense_accno_id IS NOT NULL;
       t_alloc := t_alloc + t_avail;
       t_cogs := t_cogs + t_avail * in_lastcost;
   END IF;


END LOOP;

RETURN t_alloc;
END;

Function: lsmb13.cogs__add_for_ap_line(in_invoice_id integer)

Returns: numeric

Language: PLPGSQL

DECLARE retval numeric;
        r_cogs numeric[];
        t_inv invoice;
        t_adj numeric;
        t_ap  ap;
BEGIN

SELECT * INTO t_inv FROM invoice 
 WHERE id = in_invoice_id;

IF t_inv.qty + t_inv.allocated = 0 THEN
   return 0;
END IF;

SELECT * INTO t_ap FROM ap WHERE id = t_inv.trans_id;

IF t_inv.qty < 0 THEN -- normal COGS

    SELECT cogs__add_for_ap(i.parts_id, i.qty + i.allocated, i.sellprice) 
      INTO retval
      FROM invoice i
      JOIN parts p ON p.id = i.parts_id
     WHERE i.id = $1;

    UPDATE invoice 
       SET allocated = allocated + retval
     WHERE id = $1;
ELSE -- reversal

   r_cogs := cogs__reverse_ap(t_inv.parts_id, t_inv.qty + t_inv.allocated);

   UPDATE invoice
      SET allocated = allocated + r_cogs[1]
    WHERE id = in_invoice_id;

   t_adj := t_inv.sellprice * r_cogs[1] + r_cogs[2];

   INSERT INTO acc_trans 
          (chart_id, trans_id, approved,  amount, transdate, invoice_id)
   SELECT p.inventory_accno_id, t_inv.trans_id, true, t_adj, t_ap.transdate, 
          in_invoice_id
     FROM parts p
    WHERE id = t_inv.parts_id
    UNION
   SELECT p.expense_accno_id, t_inv.trans_id, true, t_adj * -1, t_ap.transdate,
          in_invoice_id
     FROM parts p
    WHERE id = t_inv.parts_id;
   retval := r_cogs[1];
   raise notice 'cogs reversal returned %', r_cogs;

END IF;

RETURN retval;

END;


Function: lsmb13.cogs__add_for_ar(in_qty integer, in_parts_id numeric)

Returns: numeric[]

Language: PLPGSQL

This function accepts a parts_id and a quantity, and iterates through AP records in order, calculating COGS on a FIFO basis and returning it to the application to attach to the current transaction. Return values are an array of {allocated, cogs}.

DECLARE t_alloc numeric := 0;
        t_cogs numeric := 0;
        t_inv invoice;
        t_avail numeric;
BEGIN


FOR t_inv IN
    SELECT i.*
      FROM invoice i
      JOIN (select id, approved, transdate from ap
             union
            select id, approved, transdate from gl) a ON a.id = i.trans_id
     WHERE qty + allocated < 0 AND i.parts_id = in_parts_id
  ORDER BY a.transdate asc, a.id asc, i.id asc
LOOP
   t_avail := (t_inv.qty + t_inv.allocated) * -1;
   IF t_alloc > in_qty THEN
       RAISE EXCEPTION 'TOO MANY ALLOCATED';
   ELSIF t_alloc = in_qty THEN
       return ARRAY[t_alloc, t_cogs];
   ELSIF (in_qty + t_alloc) <= t_avail THEN
       UPDATE invoice SET allocated = allocated + (in_qty - t_alloc)
        WHERE id = t_inv.id;
       t_cogs := t_cogs + (in_qty - t_alloc) * t_inv.sellprice;
       t_alloc := in_qty;
       return ARRAY[t_alloc, t_cogs];
   ELSE
       UPDATE invoice SET allocated = qty * -1
        WHERE id = t_inv.id;
       t_cogs := t_cogs + (t_avail * t_inv.sellprice);
       t_alloc := t_alloc + t_avail;
   END IF;
END LOOP;

RETURN ARRAY[t_alloc, t_cogs];

END;

Function: lsmb13.cogs__add_for_ar_line(in_invoice_id integer)

Returns: numeric

Language: PLPGSQL

DECLARE 
   t_cogs numeric[];
   t_inv invoice;
   t_part parts;
   t_ar ar;
   t_transdate date;
BEGIN

SELECT * INTO t_inv FROM invoice WHERE id = in_invoice_id;
SELECT * INTO t_part FROM parts WHERE id = t_inv.parts_id;
SELECT * INTO t_ar FROM ar WHERE id = t_inv.trans_id;

IF t_inv.qty + t_inv.allocated = 0 THEN
   return 0;
END IF;

IF t_inv.qty > 0 THEN 
   t_cogs := cogs__add_for_ar(t_inv.parts_id, t_inv.qty + t_inv.allocated);
ELSE
   t_cogs := cogs__reverse_ar(t_inv.parts_id, t_inv.qty + t_inv.allocated);
END IF;


UPDATE invoice set allocated = allocated - t_cogs[1]
 WHERE id = in_invoice_id;

SELECT CASE WHEN t_ar.transdate > max(end_date) THEN t_ar.transdate
            ELSE max(end_date) + '1 day'::interval
        END INTO t_transdate
  from account_checkpoint td; 
INSERT INTO acc_trans 
       (trans_id, chart_id, approved, amount, transdate,  invoice_id)
VALUES (t_inv.trans_id, CASE WHEN t_inv.qty < 0 AND t_ar.is_return 
                           THEN t_part.returns_accno_id
                           ELSE t_part.expense_accno_id
                      END, TRUE, t_cogs[2] * -1, t_transdate, t_inv.id),
       (t_inv.trans_id, t_part.inventory_accno_id, TRUE, t_cogs[2], 
       t_transdate, t_inv.id);

RETURN t_cogs[1];

END;


Function: lsmb13.cogs__reverse_ap(in_qty integer, in_parts_id numeric)

Returns: numeric[]

Language: PLPGSQL

This function iterates through invoice rows attached to ap transactions and allocates them on a first-in first-out basis. The sort of pseudo-"COGS" value is returned to the application for further handling.

DECLARE t_alloc numeric :=0;
        t_inv invoice;
        t_cogs numeric :=0;
        retval numeric[];
BEGIN
RAISE NOTICE 'reversing AP: parts_id %, qty %', in_parts_id, in_qty;

FOR t_inv IN
    SELECT i.*
      FROM invoice i
      JOIN ap a ON a.id = i.trans_id
     WHERE qty + allocated < 0 AND parts_id = in_parts_id
  ORDER BY a.transdate, a.id, i.id
LOOP
   RAISE NOTICE 'id %, avail %, allocated %, requesting %', t_inv.id, t_inv.qty + t_inv.allocated, t_alloc, in_qty - t_alloc;
   IF t_alloc > in_qty THEN
       RAISE EXCEPTION 'TOO MANY ALLOCATED';
   ELSIF t_alloc = in_qty THEN
       return ARRAY[t_alloc, t_cogs];
   ELSIF (in_qty - t_alloc) <= -1 * (t_inv.qty + t_inv.allocated) THEN
       raise notice 'partial reversal';
       UPDATE invoice SET allocated = allocated + (in_qty - t_alloc)
        WHERE id = t_inv.id;
       return ARRAY[in_qty * -1, t_cogs + (in_qty - t_alloc) * t_inv.sellprice];
   ELSE
       raise notice 'total reversal';
       UPDATE invoice SET allocated = qty * -1
        WHERE id = t_inv.id;
       t_alloc := t_alloc - (t_inv.qty + t_inv.allocated);
       t_cogs := t_cogs - (t_inv.qty + t_inv.allocated) * t_inv.sellprice;
   END IF;
END LOOP;

RETURN ARRAY[t_alloc, t_cogs];

RAISE EXCEPTION 'TOO FEW TO ALLOCATE';
END;

Function: lsmb13.cogs__reverse_ar(in_qty integer, in_parts_id numeric)

Returns: numeric[]

Language: PLPGSQL

This function accepts a part id and quantity to reverse. It then iterates backwards over AP related records, calculating COGS. This does not save COGS but rather returns it to the application to save. Return values are an array of {allocated, cogs}.

DECLARE t_alloc numeric := 0;
        t_cogs numeric := 0;
        t_inv invoice;
        t_avail numeric;
BEGIN

FOR t_inv IN
    SELECT i.*
      FROM invoice i
      JOIN (select id, approved, transdate from ap 
            union
            select id, approved, transdate from gl) a ON a.id = i.trans_id
     WHERE allocated > 0 and a.approved and parts_id = in_parts_id
  ORDER BY a.transdate DESC, a.id DESC, i.id DESC
LOOP
   t_avail := t_inv.allocated;
   IF t_alloc < in_qty THEN
       RAISE EXCEPTION 'TOO MANY ALLOCATED';
   ELSIF t_alloc = in_qty THEN
       RETURN ARRAY[t_alloc, t_cogs];
   ELSIF (in_qty - t_alloc) * -1 <=  t_inv.allocated THEN
       raise notice 'partial reversal';
       UPDATE invoice SET allocated = allocated + (in_qty - t_alloc)
        WHERE id = t_inv.id;
       t_cogs := t_cogs +  (in_qty - t_alloc) * t_inv.sellprice;
       return ARRAY[t_alloc + (in_qty - t_alloc), t_cogs];
   ELSE
       raise notice 'full reversal';
       UPDATE invoice SET allocated = 0
        WHERE id = t_inv.id;
       t_alloc := t_alloc + t_inv.allocated * -1;
       t_cogs := t_cogs + -1 * (t_inv.allocated) * t_inv.sellprice;
   END IF;
END LOOP;

RAISE EXCEPTION 'TOO FEW TO REVERSE';

END;

Function: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.company__next_id()

Returns: bigint

Language: SQL

    
    select nextval('company_id_seq');
    

Function: lsmb13.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: lsmb13.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 entity_credit_account ec ON (ec.entity_id = e.id)
		LEFT JOIN business b ON (ec.business_id = b.id)
		WHERE coalesce(ec.entity_class,e.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)
				OR t_contact_info IS NULL)
			
			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))
	 		AND (ec.meta_number = in_meta_number
			     OR in_meta_number IS NULL)
	LOOP
		RETURN NEXT out_row;
	END LOOP;
END;

Function: lsmb13.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: lsmb13.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: lsmb13.company_save(in_country_id 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_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: lsmb13.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: lsmb13.concat(tsvector, tsvector)

Returns: tsvector

Language: INTERNAL

tsvector_concat

Function: lsmb13.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: lsmb13.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: lsmb13.connectby(text, text, text, text, integer)

Returns: SET OF record

Language: C

connectby_text

Function: lsmb13.connectby(text, text, text, text, integer, text)

Returns: SET OF record

Language: C

connectby_text

Function: lsmb13.connectby(text, text, text, text, text, integer)

Returns: SET OF record

Language: C

connectby_text_serial

Function: lsmb13.connectby(text, text, text, text, text, integer, text)

Returns: SET OF record

Language: C

connectby_text_serial

Function: lsmb13.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: lsmb13.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: lsmb13.crosstab(text)

Returns: SET OF record

Language: C

crosstab

Function: lsmb13.crosstab(text, integer)

Returns: SET OF record

Language: C

crosstab

Function: lsmb13.crosstab(text, text)

Returns: SET OF record

Language: C

crosstab_hash

Function: lsmb13.crosstab2(text)

Returns: SET OF tablefunc_crosstab_2

Language: C

crosstab

Function: lsmb13.crosstab3(text)

Returns: SET OF tablefunc_crosstab_3

Language: C

crosstab

Function: lsmb13.crosstab4(text)

Returns: SET OF tablefunc_crosstab_4

Language: C

crosstab

Function: lsmb13.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 = 2 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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.del_department()

Returns: trigger

Language: PLPGSQL

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

Function: lsmb13.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: lsmb13.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: lsmb13.del_yearend()

Returns: trigger

Language: PLPGSQL

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

Function: lsmb13.department__list_all()

Returns: SET OF department

Language: SQL

SELECT * FROM department order by description;

Function: lsmb13.department_list()

Returns: SET OF department

Language: SQL

 SELECT * FROM department ORDER BY description 

Function: lsmb13.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: lsmb13.dex_init(internal)

Returns: internal

Language: C

tsa_dex_init

Function: lsmb13.dex_lexize(internal, internal, integer)

Returns: internal

Language: C

tsa_dex_lexize

Function: lsmb13.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.invoice, 
                       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, false as invoice,
                                approved from gl
			WHERE lower(in_type) = 'gl'
			UNION
			SELECT id, transdate, invnumber as reference, 
				(SELECT name FROM eca__get_entity(entity_credit_account)),
				invoice, approved from ap
			WHERE lower(in_type) = 'ap'
			UNION
			SELECT id, transdate, invnumber as reference,
				description, 
				invoice, 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 and charttype = 'A')
           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, trans.invoice
		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: lsmb13.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
                PERFORM cogs__add_for_ar_line(id) FROM invoice 
                  WHERE trans_id = in_id;
		UPDATE ar set approved = true where id = in_id;
	ELSIF (t_table = 'ap') THEN
                PERFORM cogs__add_for_ap_line(id) FROM invoice 
                  WHERE trans_id = in_id;
		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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.eca__delete_pricematrix(in_entry_id integer, in_credit_id integer)

Returns: boolean

Language: PLPGSQL

DECLARE retval bool;

BEGIN

retval := false;

DELETE FROM partsvendor 
 WHERE entry_id = in_entry_id 
       AND credit_id = in_credit_id;

retval := FOUND;

DELETE FROM partscustomer
 WHERE entry_id = in_entry_id
       AND credit_id = in_credit_id;

RETURN FOUND or retval;

END;

Function: lsmb13.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: lsmb13.eca__get_pricematrix(in_credit_id integer)

Returns: SET OF eca__pricematrix

Language: SQL

This returns the pricematrix for the customer or vendor (entity_credit_account identified by in_id), orderd by partnumber, validfrom


SELECT pc.parts_id, p.partnumber, p.description, pc.credit_id, pc.pricebreak,
       pc.sellprice, NULL, NULL::int, NULL, pc.validfrom, pc.validto, pc.curr,
       pc.entry_id
  FROM partscustomer pc
  JOIN parts p on pc.parts_id = p.id
  JOIN entity_credit_account eca ON pc.credit_id = eca.id
 WHERE pc.credit_id = $1 AND eca.entity_class = 2
 UNION
SELECT pv.parts_id, p.partnumber, p.description, pv.credit_id, NULL, NULL,
       pv.lastcost, pv.leadtime::int, pv.partnumber, NULL, NULL, pv.curr, 
       pv.entry_id
  FROM partsvendor pv
  JOIN parts p on pv.parts_id = p.id
  JOIN entity_credit_account eca ON pv.credit_id = eca.id
 WHERE pv.credit_id = $1 and eca.entity_class = 1
 ORDER BY partnumber, validfrom


Function: lsmb13.eca__get_pricematrix_by_pricegroup(in_credit_id integer)

Returns: SET OF eca__pricematrix

Language: SQL

SELECT pc.parts_id, p.partnumber, p.description, pc.credit_id, pc.pricebreak,
       pc.sellprice, NULL::numeric, NULL::int, NULL::text, pc.validfrom, 
       pc.validto, pc.curr, pc.entry_id
  FROM partscustomer pc
  JOIN parts p on pc.parts_id = p.id
  JOIN entity_credit_account eca ON pc.pricegroup_id = eca.pricegroup_id
 WHERE eca.id = $1 AND eca.entity_class = 2

Function: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.eca__save_pricematrix(in_entry_id integer, in_curr integer, in_validto numeric, in_validfrom numeric, in_partnumber smallint, in_lead_time text, in_price date, in_pricebreak date, in_credit_id bpchar, in_parts_id integer)

Returns: eca__pricematrix

Language: PLPGSQL

DECLARE 
   retval eca__pricematrix;
   t_insert bool;

BEGIN

t_insert := false;

PERFORM * FROM entity_credit_account 
  WHERE id = in_credit_id AND entity_class = 1;

IF FOUND THEN -- VENDOR
    UPDATE partsvendor
       SET lastcost = in_price,
           leadtime = in_lead_time,
           partnumber = in_partnumber,
           curr = in_curr
     WHERE credit_id = in_credit_id AND entry_id = in_entry_id;

    IF NOT FOUND THEN
        INSERT INTO partsvendor
               (parts_id, credit_id, lastcost, leadtime, partnumber, curr)
        VALUES (in_parts_id, in_credit_id, in_price, in_leadtime::int2, 
               in_partnumber, in_curr);
    END IF;

    SELECT pv.parts_id, p.partnumber, p.description, pv.credit_id, NULL, NULL,
           pv.lastcost, pv.leadtime::int, pv.partnumber, NULL, NULL, pv.curr, 
           pv.entry_id
      INTO retval
      FROM partsvendor pv
      JOIN parts p ON p.id = pv.parts_id
     WHERE parts_id = in_parts_id and credit_id = in_credit_id;

    RETURN retval;
END IF;

PERFORM * FROM entity_credit_account
  WHERE id = in_credit_id AND entity_class = 2;

IF FOUND THEN -- CUSTOMER
    UPDATE partscustomer
       SET pricebreak = in_pricebreak,
           sellprice  = in_price,
           validfrom  = in_validfrom,
           validto    = in_validto,
           curr       = in_curr
     WHERE entry_id = in_entry_id and credit_id = in_credit_id;

    IF NOT FOUND THEN
        INSERT INTO partscustomer
               (parts_id, credit_id, sellprice, validfrom, validto, curr)
        VALUES (in_parts_id, in_credit_id, in_price, in_validfrom, in_validto, 
                in_curr);

        t_insert := true;
    END IF;

    SELECT pc.parts_id, p.partnumber, p.description, pc.credit_id, 
           pc.pricebreak, pc.sellprice, NULL, NULL, NULL, pc.validfrom, 
           pc.validto, pc.curr, pc.entry_id
      INTO retval
      FROM partscustomer pc
      JOIN parts p on pc.parts_id = p.id
     WHERE entry_id = CASE WHEN t_insert 
                           THEN currval('partscustomer_entry_id_seq') 
                           ELSE in_entry_id 
                      END;
                           
    RETURN retval;

END IF;

RAISE EXCEPTION 'No valid entity credit account found';
   
END;

Function: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.employee__all_managers()

Returns: SET OF employee_result

Language: SQL

   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, e.country_id
     FROM person p
     JOIN entity_employee ee on (ee.entity_id = p.entity_id)
     JOIN entity e ON (p.entity_id = e.id)
LEFT JOIN salutation s on (p.salutation_id = s.id)
LEFT JOIN person mp ON ee.manager_id = p.entity_id
    WHERE ee.role = 'manager'
 ORDER BY ee.employeenumber;

Function: lsmb13.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, e.country_id
     FROM person p
     JOIN entity_employee ee on (ee.entity_id = p.entity_id)
     JOIN entity e ON (p.entity_id = e.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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, e.country_id
     FROM person p
     JOIN entity e ON p.entity_id = e.id
     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: lsmb13.employee_delete(in_id integer)

Returns: void

Language: PLPGSQL

BEGIN
	DELETE FROM employee WHERE entity_id = in_id;
	RETURN;
END;

Function: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.entity__get(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: lsmb13.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: lsmb13.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: lsmb13.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
             LEFT JOIN defaults ON setting_key = 'roll_prefix'
		WHERE active and pg_has_role(SESSION_USER, 
                                     coalesce(defaults.value, 
                                     'lsmb_' || current_database() || '__') ||
                                     'contact_class_' ||
                                     lower(regexp_replace(class, ' ', '_')), 
                                     'USAGE')
		ORDER BY id
	LOOP
		RETURN NEXT out_row;
	END LOOP;
END;

Function: lsmb13.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.discount_terms,
			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.discount_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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.entity_credit_save(in_discount_account_id integer, in_taxform_id integer, in_pay_to_name integer, in_cash_account_id text, in_ar_ap_account_id numeric, in_threshold boolean, in_enddate numeric, in_startdate integer, in_curr integer, in_pricegroup_id character varying, in_language_code integer, in_business_id character varying, in_meta_number integer, in_terms bpchar, in_discount_terms date, in_creditlimit date, in_taxincluded numeric, in_discount integer, in_description integer, in_entity_id text, in_entity_class integer, 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,
                discount_account_id = in_discount_account_id,
                meta_number = t_meta_number,
                business_id = in_business_id,
                language_code = in_language_code,
                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,
                discount_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_code,
                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,
                in_discount_account_id
            );
            RETURN currval('entity_credit_account_id_seq');
       END IF;

    END;
    

Function: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.eoy_reopen_books(in_end_date date)

Returns: boolean

Language: PLPGSQL

Removes checkpoints and reverses yearend transactions 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: lsmb13.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')
                      OR acc.category = 'Q' AND acc.is_temp)
	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: lsmb13.file__attach_to_eca(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 entity credit accounts.

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_eca
                   (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: lsmb13.file__attach_to_entity(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 contact or entity. 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 entities

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_entity
                   (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: lsmb13.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_order
                   (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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.file__get_for_template(in_file_class integer, in_ref_key integer)

Returns: SET OF file_list_item

Language: SQL

 

SELECT m.mime_type, CASE WHEN f.file_class = 3 THEN ref_key ||'-'|| f.file_name
                         ELSE f.file_name END, 
       f.description, f.uploaded_by, e.name, 
       f.uploaded_at, f.id, f.ref_key, f.file_class,  f.content
  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
       AND m.invoice_include 
       OR f.id IN (SELECT max(fb.id) 
                   FROM file_base fb
                   JOIN mime_type m ON fb.mime_type_id = m.id
                        AND m.mime_type ilike 'image%'
                   JOIN invoice i ON i.trans_id = $1
                        AND i.parts_id = fb.ref_key
                  WHERE fb.file_class = 3)

Function: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.get_covers(tsvector, tsquery)

Returns: text

Language: C

tsa_get_covers

Function: lsmb13.get_default_lang()

Returns: text

Language: SQL

 SELECT coalesce((select description FROM language 
    WHERE code = (SELECT substring(value, 1, 2) FROM defaults
                   WHERE setting_key = 'default_language')), 'english');

Function: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.gin_extract_trgm(text, internal)

Returns: internal

Language: C

gin_extract_trgm

Function: lsmb13.gin_extract_trgm(text, internal, smallint, internal, internal)

Returns: internal

Language: C

gin_extract_trgm

Function: lsmb13.gin_trgm_consistent(internal, smallint, text, integer, internal, internal)

Returns: boolean

Language: C

gin_trgm_consistent

Function: lsmb13.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: lsmb13.gtrgm_compress(internal)

Returns: internal

Language: C

gtrgm_compress

Function: lsmb13.gtrgm_consistent(internal, text, integer, oid, internal)

Returns: boolean

Language: C

gtrgm_consistent

Function: lsmb13.gtrgm_decompress(internal)

Returns: internal

Language: C

gtrgm_decompress

Function: lsmb13.gtrgm_in(cstring)

Returns: gtrgm

Language: C

gtrgm_in

Function: lsmb13.gtrgm_out(lsmb13.gtrgm)

Returns: cstring

Language: C

gtrgm_out

Function: lsmb13.gtrgm_penalty(internal, internal, internal)

Returns: internal

Language: C

gtrgm_penalty

Function: lsmb13.gtrgm_picksplit(internal, internal)

Returns: internal

Language: C

gtrgm_picksplit

Function: lsmb13.gtrgm_same(lsmb13.gtrgm, lsmb13.gtrgm, internal)

Returns: internal

Language: C

gtrgm_same

Function: lsmb13.gtrgm_union(bytea, internal)

Returns: integer[]

Language: C

gtrgm_union

Function: lsmb13.headline(oid, text, tsquery)

Returns: text

Language: INTERNAL

ts_headline_byid

Function: lsmb13.headline(oid, text, tsquery, text)

Returns: text

Language: INTERNAL

ts_headline_byid_opt

Function: lsmb13.headline(text, text, tsquery)

Returns: text

Language: C

tsa_headline_byname

Function: lsmb13.headline(text, text, tsquery, text)

Returns: text

Language: C

tsa_headline_byname

Function: lsmb13.headline(text, tsquery)

Returns: text

Language: INTERNAL

ts_headline

Function: lsmb13.headline(text, tsquery, text)

Returns: text

Language: INTERNAL

ts_headline_opt

Function: lsmb13.in_tree(in_search_array integer, in_node_id lsmb13.tree_record[])

Returns: boolean

Language: SQL

SELECT CASE WHEN count(*) > 0 THEN true ELSE false END
  FROM unnest($2) r
 WHERE t @> array[$1];

Function: lsmb13.in_tree(in_search_array integer[], in_node_id lsmb13.tree_record[])

Returns: boolean

Language: SQL

SELECT bool_and(in_tree(e, $2))
  FROM unnest($1) e;

Function: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.je_get_default_lines()

Returns: character varying

Language: SQL

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

Function: lsmb13.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: lsmb13.job__create(in_batch_id integer, in_batch_class integer)

Returns: integer

Language: PLPGSQL

BEGIN
	INSERT INTO pending_job (batch_class, batch_id)
	VALUES (coalesce(in_batch_class, 3), in_batch_id);

	RETURN currval('pending_job_id_seq');
END;

Function: lsmb13.job__status(in_job_id integer)

Returns: job__status

Language: PLPGSQL

DECLARE out_row job__status;
BEGIN
	SELECT  (completed_at IS NULL)::INT, success::int, completed_at,
		error_condition
	INTO out_row 
	FROM pending_job
	WHERE id = in_job_id;

	RETURN out_row;
END;

Function: lsmb13.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: lsmb13.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: lsmb13.length(tsvector)

Returns: integer

Language: INTERNAL

tsvector_length

Function: lsmb13.lexize(oid, text)

Returns: text[]

Language: INTERNAL

ts_lexize

Function: lsmb13.lexize(text)

Returns: text[]

Language: C

tsa_lexize_bycurrent

Function: lsmb13.lexize(text, text)

Returns: text[]

Language: C

tsa_lexize_byname

Function: lsmb13.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: lsmb13.location__deactivate(in_id integer)

Returns: location

Language: SQL


UPDATE location set active = false, inactive_date = now()
 WHERE id = $1;

SELECT * FROM location WHERE id = 1;


Function: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.menu_children(in_parent_id integer)

Returns: SET OF menu_item

Language: SQL

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.

SELECT * FROM menu_generate() where parent = $1;

Function: lsmb13.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 
               WITH RECURSIVE tree (path, id, parent, level, positions)
                               AS (select id::text as path, id, parent, 
                                           0 as level, position::text
                                      FROM menu_node where parent is null
                                     UNION 
                                    select path || ',' || n.id::text, n.id, 
                                           n.parent,
                                           t.level + 1, 
                                           t.positions || ',' || n.position
                                      FROM menu_node n
                                      JOIN tree t ON t.id = n.parent) 
		SELECT n.position, n.id, c.level, n.label, c.path, n.parent,
                       to_args(array[ma.attribute, ma.value])
		FROM tree c
		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 acl
                          LEFT JOIN pg_roles pr on pr.rolname = acl.role_name
                               WHERE CASE WHEN role_name 
                                                           ilike 'public'
                                                      THEN true
                                                      WHEN rolname IS NULL
                                                      THEN FALSE
                                                      ELSE pg_has_role(rolname,
                                                                       'USAGE')
                                      END
                            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 tree cc
                                 JOIN menu_node cn USING(id)
                                WHERE cn.id IN 
                                      (select node_id 
                                         FROM menu_acl acl
                                    LEFT JOIN pg_roles pr 
                                              on pr.rolname = acl.role_name
                                        WHERE CASE WHEN rolname 
                                                           ilike 'public'
                                                      THEN true
                                                      WHEN rolname IS NULL
                                                      THEN FALSE
                                                      ELSE pg_has_role(rolname,
                                                                       'USAGE')
                                                END
                                     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::text 
                                           like c.path::text || ',%')
            GROUP BY n.position, n.id, c.level, n.label, c.path, c.positions,
                     n.parent
            ORDER BY string_to_array(c.positions, ',')::int[]
	LOOP
		RETURN NEXT item;
	END LOOP;
END;

Function: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.normal_rand(integer, double precision, double precision)

Returns: SET OF double precision

Language: C

normal_rand

Function: lsmb13.numnode(tsquery)

Returns: integer

Language: INTERNAL

tsquery_numnode

Function: lsmb13.parse(oid, text)

Returns: SET OF tokenout

Language: INTERNAL

ts_parse_byid

Function: lsmb13.parse(text)

Returns: SET OF tokenout

Language: C

tsa_parse_current

Function: lsmb13.parse(text, text)

Returns: SET OF tokenout

Language: INTERNAL

ts_parse_byname

Function: lsmb13.parse_date(in_date date)

Returns: date

Language: SQL

Simple way to cast a Perl string to a date format of known type.

 select $1; 

Function: lsmb13.parts__get_by_id(in_id integer)

Returns: parts

Language: SQL

SELECT * FROM parts WHERE id = $1;

Function: lsmb13.parts__get_by_partnumber(in_partnumber text)

Returns: parts

Language: SQL

SELECT * FROM PARTS WHERE partnumber = $1 and obsolete is not true; 

Function: lsmb13.parts__search_lite(in_description text, in_partnumber text)

Returns: SET OF parts

Language: SQL

SELECT * 
  FROM parts 
 WHERE ($1 IS NULL OR (partnumber like $1 || '%'))
       AND ($2 IS NULL 
            OR (description
                @@
                plainto_tsquery(get_default_lang()::regconfig, $2)))
       AND not obsolete
ORDER BY partnumber;

Function: lsmb13.payment__reverse(in_currency text, in_exchangerate date, in_voucher_id integer, in_batch_id text, in_account_class date, in_date_reversed integer, in_cash_accno integer, in_credit_id integer, in_date_paid numeric, in_source bpchar)

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;
        t_currs text[];
        t_rev_fx numeric;
        t_fxgain_id int;
        t_fxloss_id int;
        t_paid_fx numeric;
BEGIN
        SELECT * INTO t_rev_fx FROM currency_get_exchangerate(
              in_currency, in_date_reversed, in_account_class);

        SELECT * INTO t_paid_fx FROM currency_get_exchangerate(
              in_currency, in_date_paid, in_account_class);

       select value::int INTO t_fxgain_id FROM setting_get('fxgain_accno_id');
       select value::int INTO t_fxloss_id FROM setting_get('fxloss_accno_id');

       SELECT string_to_array(value, ':') into t_currs
          from defaults
         where setting_key = 'curr';

        IF in_currency IS NULL OR in_currency = t_currs[1] THEN
                t_rev_fx := 1;
                t_paid_fx := 1;
        ELSIF t_rev_fx IS NULL THEN
                t_rev_fx := in_exchangerate;
                PERFORM payments_set_exchangerate(in_account_class,
                                                  in_exchangerate,
                                                  in_currency,
                                                  in_date_reversed);
        ELSIF t_rev_fx <> in_exchangerate THEN
                RAISE EXCEPTION 'Exchange rate different than on file';
        END IF;
        IF t_rev_fx IS NULL THEN
            RAISE EXCEPTION 'No exchangerate provided and not default currency';
        END IF;


        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, arap.curr, arap.fxrate
		FROM acc_trans a
		JOIN (select id, curr, entity_credit_account, 
                             CASE WHEN curr = t_currs[1] THEN 1
                                   ELSE buy END as fxrate
			FROM ar 
                   LEFT JOIN exchangerate USING (transdate, curr)
                       WHERE in_account_class = 2
			UNION
			SELECT id, curr, entity_credit_account, 
                               CASE WHEN curr = t_currs[1] THEN 1
                                    ELSE sell END as fxrate
			FROM ap
                   LEFT JOIN exchangerate USING (transdate, curr)
                       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 account ch ON (a.chart_id = ch.id)
		WHERE a.source IS NOT DISTINCT FROM in_source
			AND a.transdate = in_date_paid
			AND in_credit_id = arap.entity_credit_account
			AND in_cash_accno = ch.accno
                        and in_voucher_id IS NOT DISTINCT FROM voucher_id
	LOOP
                IF pay_row.curr = t_currs[1] THEN
                   pay_row.fxrate = 1;
                END IF;

		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 / t_paid_fx * -1 * t_rev_fx, 
			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),
                 (pay_row.trans_id, pay_row.ar_ap_account_id, 
                        pay_row.amount / t_paid_fx * pay_row.fxrate,
			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),
                 (pay_row.trans_id, 
                  case when pay_row.fxrate > t_rev_fx 
                       THEN t_fxloss_id ELSE t_fxgain_id END, 
                  pay_row.amount / t_paid_fx * (t_rev_fx - pay_row.fxrate),
                  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);

                   
	END LOOP;
	RETURN 1;
END;

Function: lsmb13.payment__search(in_currency text, in_account_class date, in_cash_accno date, in_credit_id integer, in_date_to text, in_date_from integer, in_source bpchar)

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, curr
			FROM ar WHERE in_account_class = 2
			UNION
			SELECT entity_credit_account, id, curr
			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 (in_currency IS NULL OR in_currency = arap.curr)
			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: lsmb13.payment_bulk_post(in_curr numeric[], in_exchangerate integer, in_payment_type text, in_account_class numeric, in_payment_date text, in_cash_accno text, in_ar_ap_accno date, in_total integer, in_source integer, in_batch_id numeric, in_transactions text)

Returns: integer

Language: PLPGSQL

This posts the payments for large batch workflows. Note that in_transactions is a two-dimensional numeric array. Of each sub-array, the first element is the (integer) transaction id, and the second is the amount for that transaction.

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, payment_type)
		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$ , $E$ || coalesce(quote_literal(in_payment_type), '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, payment_type)
		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$ , $E$ || coalesce(quote_literal(in_payment_type), '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)
		         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)
		         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: lsmb13.payment_bulk_post(in_currency 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

This posts the payments for large batch workflows. Note that in_transactions is a two-dimensional numeric array. Of each sub-array, the first element is the (integer) transaction id, and the second is the amount for that transaction.

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;
        t_cash_sign int;
BEGIN

        SELECT * INTO t_exchangerate FROM currency_get_exchangerate(
              in_currency, in_payment_date, in_account_class);

        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_currency IS NULL OR in_currency = t_currs[1]) THEN
                t_exchangerate := 1;
        ELSIF t_exchangerate IS NULL THEN
                t_exchangerate := in_exchangerate;
                PERFORM payments_set_exchangerate(in_account_class,
                                                  in_exchangerate, 
                                                  in_currency,
                                                  in_payment_date);
        ELSIF t_exchangerate <> in_exchangerate THEN
                RAISE EXCEPTION 'Exchange rate different than on file';
        END IF;
        IF t_exchangerate IS NULL THEN
            RAISE EXCEPTION 'No exchangerate provided and not default currency';
        END IF;

        CREATE TEMPORARY TABLE bulk_payments_in
           (id int, amount numeric, fxrate numeric, gain_loss_accno int);

        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
            -- Fill the bulk payments table
            INSERT INTO bulk_payments_in(id, amount)
            VALUES (in_transactions[out_count][1],
                    in_transactions[out_count][2]);
        END LOOP;

        IF in_account_class = 1 THEN
            t_cash_sign := 1;
        ELSE
            t_cash_sign := -1;
        END IF;

        IF (in_currency IS NULL OR in_currency = t_currs[1]) THEN
            UPDATE bulk_payments_in
               SET fxrate = 1;
        ELSE
            UPDATE bulk_payments_in
               SET fxrate =
                (SELECT CASE WHEN in_account_class = 1 THEN sell
                             ELSE buy
                        END
                   FROM exchangerate e
                   JOIN (SELECT transdate, id, curr FROM ar
                         UNION
                         SELECT transdate, id, curr FROM ap) a
                     ON (e.transdate = a.transdate
                         AND e.curr = a.curr)
                   WHERE a.id = bulk_payments_in.id);
            UPDATE bulk_payments_in
               SET gain_loss_accno =
                (SELECT value::int FROM defaults
                  WHERE setting_key = 'fxgain_accno_id')
             WHERE ((t_exchangerate - bulk_payments_in.fxrate) * t_cash_sign) < 0;
            UPDATE bulk_payments_in
               SET gain_loss_accno = (SELECT value::int FROM defaults
                  WHERE setting_key = 'fxloss_accno_id')
             WHERE ((t_exchangerate - bulk_payments_in.fxrate) * t_cash_sign) > 0;
            -- explicitly leave zero gain/loss accno_id entries at NULL
            -- so we have an easy check for which 
        END IF;

        -- Insert cash side
        INSERT INTO acc_trans
             (trans_id, chart_id, amount, approved,
              voucher_id, transdate, source)
           SELECT id, t_cash_id, amount * t_cash_sign * t_exchangerate/fxrate,
                  CASE WHEN t_voucher_id IS NULL THEN true
                       ELSE false END,
                  t_voucher_id, in_payment_date, in_source
             FROM bulk_payments_in  where amount <> 0;

        -- early payment discounts
        INSERT INTO acc_trans
               (trans_id, chart_id, amount, approved,
               voucher_id, transdate, source)
        SELECT bpi.id, eca.discount_account_id, 
               amount * t_cash_sign * t_exchangerate/fxrate 
               / (1 - discount::numeric/100) 
               * (discount::numeric/100),
               CASE WHEN t_voucher_id IS NULL THEN true
                       ELSE false END,
               t_voucher_id, in_payment_date, in_source
          FROM bulk_payments_in bpi
          JOIN (select entity_credit_account, id, transdate FROM ar 
                 WHERE in_account_class = 2
                 UNION
                SELECT entity_credit_account, id, transdate FROM ap
                 WHERE in_account_class = 1) gl ON gl.id = bpi.id
          JOIN entity_credit_account eca ON gl.entity_credit_account = eca.id
         WHERE bpi.amount <> 0 
               AND extract('days' from age(gl.transdate)) < eca.discount_terms;

        INSERT INTO acc_trans
               (trans_id, chart_id, amount, approved,
               voucher_id, transdate, source)
        SELECT bpi.id, t_ar_ap_id, 
               amount * t_cash_sign * -1 * t_exchangerate/fxrate 
               / (1 - discount::numeric/100) 
               * (discount::numeric/100),
               CASE WHEN t_voucher_id IS NULL THEN true
                       ELSE false END,
               t_voucher_id, in_payment_date, in_source
          FROM bulk_payments_in bpi
          JOIN (select entity_credit_account, id, transdate FROM ar 
                 WHERE in_account_class = 2
                 UNION
                SELECT entity_credit_account, id, transdate FROM ap
                 WHERE in_account_class = 1) gl ON gl.id = bpi.id
          JOIN entity_credit_account eca ON gl.entity_credit_account = eca.id
         WHERE bpi.amount <> 0 
               AND extract('days' from age(gl.transdate)) < eca.discount_terms;

        -- Insert ar/ap side
        INSERT INTO acc_trans
             (trans_id, chart_id, amount, approved,
              voucher_id, transdate, source)
           SELECT id, t_ar_ap_id,
                  amount * -1 * t_cash_sign,
                  CASE WHEN t_voucher_id IS NULL THEN true
                       ELSE false END,
                  t_voucher_id, in_payment_date, in_source
             FROM bulk_payments_in where amount <> 0;

        -- Insert fx gain/loss effects, if applicable
        INSERT INTO acc_trans
             (trans_id, chart_id, amount, approved,
              voucher_id, transdate, source)
           SELECT id, gain_loss_accno,
                  amount * t_cash_sign * (1 - t_exchangerate/fxrate),
                  CASE WHEN t_voucher_id IS NULL THEN true
                       ELSE false END,
                  t_voucher_id, in_payment_date, in_source
             FROM bulk_payments_in
            WHERE amount <> 0 AND gain_loss_accno IS NOT NULL;

        DROP TABLE bulk_payments_in;
        perform unlock_all();
        return out_count;
END;

Function: lsmb13.payment_bulk_queue(in_account_class numeric[], in_payment_date integer, in_cash_accno text, in_ar_ap_accno numeric, in_total text, in_source text, in_batch_id date, in_transactions integer)

Returns: integer

Language: PLPGSQL

BEGIN
	INSERT INTO payments_queue
	(transactions, batch_id, source, total, ar_ap_accno, cash_accno,
		payment_date, account_class)
	VALUES 
	(in_transactions, in_batch_id, in_source, in_total, in_ar_ap_accno,
		in_cash_accno, in_payment_date, in_account_class);

	RETURN array_upper(in_transactions, 1) - 
		array_lower(in_transactions, 1);
END;

Function: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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 (coalesce(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, 
                                 curr, 1 as invoice_class,
                                 entity_credit_account, on_hold, v.batch_id,
                                 approved, paid
                            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,
                                 curr, 2 as invoice_class,
                                 entity_credit_account, on_hold, v.batch_id,
                                 approved, paid
                            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 due <> 0
                         AND NOT a.on_hold
                         AND a.curr = in_currency
                         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: lsmb13.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: lsmb13.payment_get_entity_account_payment_info(in_entity_credit_id integer)

Returns: payment_vc_info

Language: SQL

Returns payment information on the entity credit account as required to for discount calculations and payment processing.

 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.id = $1;

Function: lsmb13.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: lsmb13.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 acc_trans
                                   JOIN chart ON (acc_trans.chart_id = chart.id)
                                   JOIN ap ON (acc_trans.trans_id = ap.id)
                                   WHERE link = 'AP'
                                   GROUP BY chart_id,
                                         trans_id, entity_credit_account
                                   HAVING SUM(acc_trans.amount) <> 0)
                               WHEN in_account_class = 2 THEN
                                ec.id IN (SELECT entity_credit_account
                                   FROM acc_trans
                                   JOIN chart ON (acc_trans.chart_id = chart.id)
                                   JOIN ar ON (acc_trans.trans_id = ar.id)
                                   WHERE link = 'AR'
                                   GROUP BY chart_id,
                                         trans_id, entity_credit_account
                                   HAVING SUM(acc_trans.amount) <> 0)
                          END
        LOOP
                RETURN NEXT out_entity;
        END LOOP;
END;

Function: lsmb13.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: lsmb13.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.invoice AS invoice, 
		       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 = 2
		              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 = 2
		              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 = 2
		              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 = 2
		         THEN ex.buy
		         ELSE ex.sell END)
		         END) AS exchangerate
                 --TODO HV prepare drop entity_id from ap,ar
                 --FROM  (SELECT id, invnumber, transdate, amount, entity_id,
                 FROM  (SELECT id, invnumber, invoice, transdate, amount,
		               1 as invoice_class, curr,
		               entity_credit_account, department_id, approved
		          FROM ap
                         UNION
		         --SELECT id, invnumber, transdate, amount, entity_id,
		         SELECT id, invnumber, invoice, transdate, amount,
		               2 AS invoice_class, 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
                        --### short term: ignore fractional cent differences
		        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, a.invoice
	LOOP
		RETURN NEXT payment_inv;
	END LOOP;
END;

Function: lsmb13.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: lsmb13.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: lsmb13.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 entity_to_location ctl ON (ctl.location_id = l.id)
                JOIN entity cp ON (ctl.entity_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: lsmb13.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 fx_gain_loss_amount numeric;
BEGIN
      IF array_upper(in_amount, 1) <> array_upper(in_cash_account_id, 1) THEN
          RAISE EXCEPTION 'Wrong number of accounts';
      END IF;
        
        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
	-- ### BUG: we don't have a guarantee that the transaction is
	--          the same currency as in_curr, so, we can't use
	--          current_exchangerate as the basis for fx gain/loss
	--          calculations
        IF (in_curr = default_currency) THEN 
           old_exchangerate := 1;
        ELSIF (in_account_class = 2) THEN
           SELECT buy INTO old_exchangerate 
           FROM exchangerate e
           JOIN ar a ON (a.transdate = e.transdate)
                        AND (a.curr = e.curr)
           WHERE a.id = in_transaction_id[out_count];
        ELSE 
           SELECT sell INTO old_exchangerate 
           FROM exchangerate e
           JOIN ap a ON (a.transdate = e.transdate)
                        AND (a.curr = e.curr)
           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  fx_gain_loss_amount equals zero then we dont need to post
        -- any transaction
       fx_gain_loss_amount := in_amount[out_count]*current_exchangerate - in_amount[out_count]*old_exchangerate;
       IF (in_account_class = 1) THEN
         -- in case of vendor invoices, the invoice amounts have been negated, do the same with the diff
         fx_gain_loss_amount := fx_gain_loss_amount * -1;
       END IF;

       IF (fx_gain_loss_amount < 0) THEN
           INSERT INTO acc_trans (chart_id, amount, trans_id, transdate, approved, source)
            VALUES ((select value::int from defaults WHERE setting_key = 'fxgain_accno_id'),
                    fx_gain_loss_amount, in_transaction_id[out_count], in_datepaid, coalesce(in_approved, true),
                    in_source[out_count]);
        ELSIF (fx_gain_loss_amount > 0) THEN
            INSERT INTO acc_trans (chart_id, amount, trans_id, transdate, approved, source)
            VALUES ((select value::int from defaults WHERE setting_key = 'fxloss_accno_id'),
                    fx_gain_loss_amount, in_transaction_id[out_count], in_datepaid, coalesce(in_approved, true),
                    in_source[out_count]);
        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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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_datepaid
              AND curr = in_curr;
IF current_exrate.transdate = in_datepaid THEN
   IF in_account_class = 2 THEN 
      UPDATE exchangerate set buy = in_exchangerate  where transdate = in_datepaid;
   ELSE
      UPDATE exchangerate set sell = in_exchangerate where transdate = in_datepaid;
   END IF;
   RETURN 0; 
ELSE
    IF in_account_class = 2 THEN
     INSERT INTO exchangerate (curr, transdate, buy) values (in_curr, in_datepaid, in_exchangerate);
  ELSE   
     INSERT INTO exchangerate (curr, transdate, sell) values (in_curr, in_datepaid, in_exchangerate);
  END IF;                                       
RETURN 0;
END IF;
END;

Function: lsmb13.periods_get()

Returns: SET OF periods

Language: SQL

Returns dates for year to date, and last year.

SELECT * FROM periods ORDER BY id

Function: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.person__list_languages()

Returns: SET OF language

Language: SQL

Returns a list of languages ordered by code

 SELECT * FROM language ORDER BY code ASC 

Function: lsmb13.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: lsmb13.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: lsmb13.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 FOUND THEN
        UPDATE entity 
           SET name = in_first_name || ' ' || in_last_name,
               country_id = in_country_id
         WHERE id = in_entity_id; 
    ELSE
        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: lsmb13.person__save_contact(in_old_contact_class integer, in_description integer, in_contact_new text, in_old_contact text, in_contact_class text, in_entity_id integer)

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_old_contact_class
    AND cc.contact = in_old_contact
    AND cc.person_id = p.id;
    
    IF NOT FOUND THEN
    
        -- create
        INSERT INTO person_to_contact(person_id, contact_class_id, contact, description)
        VALUES (
            (SELECT id FROM person WHERE entity_id = in_entity_id),
            in_contact_class,
            in_contact_new,
            in_description
        );
        return 1;
    ELSE
        -- edit.
        UPDATE person_to_contact
           SET contact = in_contact_new, description = in_description
         WHERE contact = in_old_contact
               AND person_id = v_orig.person_id
               AND contact_class_id = in_old_contact_class;
        return 0;
    END IF;
    
END;

Function: lsmb13.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: lsmb13.plainto_tsquery(oid, text)

Returns: tsquery

Language: INTERNAL

plainto_tsquery_byid

Function: lsmb13.plainto_tsquery(text)

Returns: tsquery

Language: INTERNAL

plainto_tsquery

Function: lsmb13.plainto_tsquery(text, text)

Returns: tsquery

Language: C

tsa_plainto_tsquery_name

Function: lsmb13.pricegroup__list()

Returns: SET OF pricegroup

Language: SQL

Returns an alphabetically ordered pricegroup list.

SELECT * FROM pricegroup ORDER BY pricegroup;

Function: lsmb13.pricegroups__list()

Returns: SET OF pricegroup

Language: SQL

SELECT * FROM pricegroup;

Function: lsmb13.pricelist__delete(credit_id integer, entry_id integer)

Returns: boolean

Language: SQL

delete from partscustomer where entry_id = $1 and credit_id = $2;
delete from partsvendor where entry_id = $1 and credit_id = $2;
select true;

Function: lsmb13.pricelist__save(in_entry_id integer, in_curr integer, in_validto numeric, in_validfrom numeric, in_partnumber smallint, in_lead_time text, in_price date, in_pricebreak date, in_credit_id bpchar, in_parts_id integer)

Returns: eca__pricematrix

Language: PLPGSQL

DECLARE 
   retval eca__pricematrix;
   t_insert bool;
   t_entity_class int;

BEGIN

t_insert := false;

SELECT entity_class INTO t_entity_class FROM entity_credit_account 
  WHERE id = in_credit_id;

IF t_entity_class = 1 THEN -- VENDOR
    UPDATE partsvendor
       SET lastcost = in_price,
           leadtime = in_lead_time,
           partnumber = in_partnumber,
           curr = in_curr
     WHERE credit_id = in_credit_id AND entry_id = in_entry_id;

    IF NOT FOUND THEN
        INSERT INTO partsvendor
               (parts_id, credit_id, lastcost, leadtime, partnumber, curr)
        VALUES (in_parts_id, in_credit_id, in_price, in_leadtime::int2, 
               in_partnumber, in_curr);
    END IF;

    SELECT pv.parts_id, p.partnumber, p.description, pv.credit_id, NULL, NULL,
           pv.lastcost, pv.leadtime::int, pv.partnumber, NULL, NULL, pv.curr, 
           pv.entry_id
      INTO retval
      FROM partsvendor pv
      JOIN parts p ON p.id = pv.parts_id
     WHERE parts_id = in_parts_id and credit_id = in_credit_id;

    RETURN retval;

ELSIF t_entity_class = 2 THEN -- CUSTOMER
    UPDATE partscustomer
       SET pricebreak = in_pricebreak,
           sellprice  = in_price,
           validfrom  = in_validfrom,
           validto    = in_validto,
           curr       = in_curr
     WHERE entry_id = in_entry_id and credit_id = in_credit_id;

    IF NOT FOUND THEN
        INSERT INTO partscustomer
               (parts_id, credit_id, sellprice, validfrom, validto, curr)
        VALUES (in_parts_id, in_credit_id, in_price, in_validfrom, in_validto, 
                in_curr);

        t_insert := true;
    END IF;

    SELECT pc.parts_id, p.partnumber, p.description, pc.credit_id, 
           pc.pricebreak, pc.sellprice, NULL, NULL, NULL, pc.validfrom, 
           pc.validto, pc.curr, pc.entry_id
      INTO retval
      FROM partscustomer pc
      JOIN parts p on pc.parts_id = p.id
     WHERE entry_id = CASE WHEN t_insert 
                           THEN currval('partscustomer_entry_id_seq') 
                           ELSE in_entry_id 
                      END;
                           
    RETURN retval;

ELSE

RAISE EXCEPTION 'No valid entity credit account found';

END IF;
END;

Function: lsmb13.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: lsmb13.prsd_end(internal)

Returns: void

Language: C

tsa_prsd_end

Function: lsmb13.prsd_getlexeme(internal, internal, internal)

Returns: integer

Language: C

tsa_prsd_getlexeme

Function: lsmb13.prsd_headline(internal, internal, internal)

Returns: internal

Language: C

tsa_prsd_headline

Function: lsmb13.prsd_lextype(internal)

Returns: internal

Language: C

tsa_prsd_lextype

Function: lsmb13.prsd_start(internal, integer)

Returns: internal

Language: C

tsa_prsd_start

Function: lsmb13.querytree(tsquery)

Returns: text

Language: INTERNAL

tsquerytree

Function: lsmb13.rank(real[], tsvector, tsquery)

Returns: real

Language: INTERNAL

ts_rank_wtt

Function: lsmb13.rank(real[], tsvector, tsquery, integer)

Returns: real

Language: INTERNAL

ts_rank_wttf

Function: lsmb13.rank(tsvector, tsquery)

Returns: real

Language: INTERNAL

ts_rank_tt

Function: lsmb13.rank(tsvector, tsquery, integer)

Returns: real

Language: INTERNAL

ts_rank_ttf

Function: lsmb13.rank_cd(real[], tsvector, tsquery)

Returns: real

Language: INTERNAL

ts_rankcd_wtt

Function: lsmb13.rank_cd(real[], tsvector, tsquery, integer)

Returns: real

Language: INTERNAL

ts_rankcd_wttf

Function: lsmb13.rank_cd(tsvector, tsquery)

Returns: real

Language: INTERNAL

ts_rankcd_tt

Function: lsmb13.rank_cd(tsvector, tsquery, integer)

Returns: real

Language: INTERNAL

ts_rankcd_ttf

Function: lsmb13.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: lsmb13.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;
        t_amount numeric;
    BEGIN
        SELECT CASE WHEN a.category in ('A', 'E') THEN in_amount * -1
                    ELSE in_amount
               END into t_amount
          FROM cr_report r JOIN account a ON r.chart_id = a.id
         WHERE r.id = in_report_id; 

	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
                -- could this be changed to update, if not found insert?
		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, t_amount, 0, in_date, t_uid,
				in_type);
		ELSIF in_count = 1 THEN
			UPDATE cr_report_line
			SET their_balance = t_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 = t_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 = t_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 = t_amount, 
					trans_type = in_type,
					clear_time = in_date,
					cleared = true
				WHERE t_scn = scn AND report_id = in_report_id
                                	AND our_value = t_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 = t_amount
				ORDER BY id ASC limit 1;

				UPDATE cr_report_line
                                SET their_balance = t_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 = t_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, t_amount, 0, in_date, t_uid,
			in_type);
		ELSIF in_count = 1 THEN -- perfect match
			UPDATE cr_report_line SET their_balance = t_amount,
					trans_type = in_type,
					clear_time = in_date,
					cleared = true
			WHERE report_id = in_report_id AND our_balance = t_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 = t_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 = t_amount,
					trans_type = in_type,
					clear_time = in_date,
					cleared = true
			WHERE id = lid;
			
		END IF;
	END IF;
        return 1; 
        
    END;    

Function: lsmb13.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: lsmb13.reconciliation__delete_report(in_report_id integer)

Returns: boolean

Language: PLPGSQL

Deletes the report if the report exists and is unapproved. Note this does not actually delete anything from the database. Deleted reports are kept around in case they need to be investigated. It only marks them as deleted so that they can never be approved.

    DECLARE
    BEGIN
        PERFORM id FROM cr_report WHERE id = in_report_id;
        
        IF NOT FOUND THEN
            RAISE NOTICE 'reconciliation__delete_report(): Cannot find specified report.';
            return FOUND;
        END IF;
        
        -- We found the entry. Update it.
        
        PERFORM id FROM cr_report WHERE id = in_report_id AND approved = TRUE;
        
        IF FOUND THEN --changing the verbose message to a notice and adding a
                      --program-helpful exception --CT
            RAISE NOTICE 'reconcilation__delete_report(): report % is approved; cannot delete.', in_report_id;
            RAISE EXCEPTION 'Cannot delete approved';
        END IF;
        
        PERFORM id 
           FROM cr_report 
          WHERE id = in_report_id 
            AND submitted = TRUE
            AND entered_by = person__get_my_entity_id();
        
        -- IF FOUND THEN
            -- Creators cannot delete their own reports if they've been submitted. -AS
            -- Why not?  If it hasn't been approved.....  
            -- Also concerned about single-user setups here so commenting out
            -- this block --CT
            -- RAISE EXCEPTION 'reconciliation__delete_report(): creators cannot delete their own report after submission. %', in_report_id;
        -- END IF;
        
        UPDATE cr_report
           SET deleted = TRUE,
               deleted_by = person__get_my_entity_id()
         WHERE id = in_report_id;
         
        return TRUE;
    END;

Function: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.reconciliation__new_report_id(in_recon_fx integer, in_end_date numeric, in_total date, in_chart_id boolean)

Returns: integer

Language: SQL

Inserts creates a new report and returns the id.


    INSERT INTO cr_report(chart_id, their_total, end_date, recon_fx) 
    values ($1, $2, $3, $4);
    SELECT currval('cr_report_id_seq')::int;


Function: lsmb13.reconciliation__pending_transactions(in_recon_fx date, in_their_total integer, in_report_id integer, in_chart_id numeric, in_end_date boolean)

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 / CASE WHEN gl.table = 'gl' THEN 1
                                         WHEN gl.table = 'ap' THEN ex.sell
                                         WHEN gl.table = 'ar' THEN ex.buy
                                    END) 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, curr, 
                             transdate, 'ar' as table 
                        FROM ar where approved
			UNION
		      select id, entity_credit_account::text, curr, 
                             transdate, 'ap' as table 
                        FROM ap WHERE approved
			UNION
		      select id, reference, '', 
                             transdate, '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)))
                LEFT JOIN exchangerate ex ON gl.transdate = ex.transdate
		WHERE ac.cleared IS FALSE
			AND ac.approved IS TRUE
			AND ac.chart_id = in_chart_id
			AND ac.transdate <= in_end_date
                        AND ((in_recon_fx is not true 
                                and ac.fx_transaction is not true) 
                            OR (in_recon_fx is true 
                                AND (gl.table <> 'gl' OR ac.fx_transaction
                                                      IS TRUE))) 
		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: lsmb13.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;
        t_recon_fx BOOL;
    BEGIN
                SELECT recon_fx INTO t_recon_fx FROM cr_report WHERE id = in_report_id;
 
		INSERT INTO cr_report_line (report_id, scn, their_balance, 
			our_balance, "user", voucher_id, ledger_id, post_date)
		SELECT in_report_id, 
		       CASE WHEN ac.source IS NULL OR ac.source = '' 
                            THEN gl.ref
                            ELSE ac.source END,
		       0, 
		       sum(amount / CASE WHEN t_recon_fx IS NOT TRUE OR gl.table = 'gl'
                                         THEN 1
                                         WHEN t_recon_fx and gl.table = 'ap' 
                                         THEN ex.sell
                                         WHEN t_recon_fx and gl.table = 'ar' 
                                         THEN ex.buy
                                    END) 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, curr, 
                             transdate, 'ar' as table 
                        FROM ar where approved
			UNION
		      select id, entity_credit_account::text, curr, 
                             transdate, 'ap' as table 
                        FROM ap WHERE approved
			UNION
		      select id, reference, '', 
                             transdate, '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)))
                LEFT JOIN exchangerate ex ON gl.transdate = ex.transdate
		WHERE ac.cleared IS FALSE
			AND ac.approved IS TRUE
			AND ac.chart_id = in_chart_id
			AND ac.transdate <= in_end_date
                        AND ((t_recon_fx is not true 
                                and ac.fx_transaction is not true) 
                            OR (t_recon_fx is true 
                                AND (gl.table <> 'gl' OR ac.fx_transaction
                                                      IS TRUE))) 
		GROUP BY gl.ref, ac.source, ac.transdate,
			ac.memo, ac.voucher_id, gl.table, 
                        case when gl.table = 'gl' then gl.id else 1 end
		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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.report__cash_summary(in_to_accno date, in_from_accno date, in_date_to text, in_date_from text)

Returns: SET OF cash_summary_item

Language: SQL

SELECT a.id, a.accno, a.is_heading, a.description, t.label, 
       sum(CASE WHEN ac.amount < 0 THEN ac.amount * -1 ELSE NULL END),
       sum(CASE WHEN ac.amount > 0 THEN ac.amount ELSE NULL END)
  FROM (select id, accno, false as is_heading, description FROM account
       UNION
        SELECT id, accno, true, description FROM account_heading) a
  LEFT
  JOIN acc_trans ac ON ac.chart_id = a.id 
  LEFT
  JOIN (select id, case when table_name ilike 'ar' THEN 'rcpt'
                        when table_name ilike 'ap' THEN 'pmt'
                        when table_name ilike 'gl' THEN 'xfer'
                    END AS label
          FROM transactions) t ON t.id = ac.trans_id
 WHERE accno BETWEEN $3 AND $4
        and ac.transdate BETWEEN $1 AND $2
GROUP BY a.id, a.accno, a.is_heading, a.description, t.label
ORDER BY accno;


Function: lsmb13.report__coa()

Returns: SET OF coa_entry

Language: SQL


WITH ac (chart_id, amount) AS (
     SELECT chart_id, CASE WHEN acc_trans.approved and gl.approved THEN amount
                           ELSE 0 
                       END
       FROM acc_trans
       JOIN (select id, approved from ar union all
             select id, approved from ap union all
             select id, approved from gl) gl ON gl.id = acc_trans.trans_id
),
l(account_id, link) AS (
     SELECT account_id, array_to_string(array_agg(description), ':')
       FROM account_link
   GROUP BY account_id
)
SELECT a.id, a.is_heading, a.accno, a.description, a.gifi_accno, 
       CASE WHEN sum(ac.amount) < 0 THEN sum(amount) * -1 ELSE null::numeric
        END,
       CASE WHEN sum(ac.amount) > 0 THEN sum(amount) ELSE null::numeric END,
       count(ac.*), l.link
  FROM (SELECT id,false as is_heading, accno, description, gifi_accno
          FROM account
         UNION
        SELECT id, true, accno, description, null::text 
          FROM account_heading) a

 LEFT JOIN ac ON ac.chart_id = a.id AND not a.is_heading
 LEFT JOIN l ON l.account_id = a.id AND NOT a.is_heading
  GROUP BY a.id, a.is_heading, a.accno, a.description, a.gifi_accno, l.link
  ORDER BY a.accno;


Function: lsmb13.report__general_balance(in_date_to date, in_date_from date)

Returns: SET OF general_balance_line

Language: SQL


SELECT a.id, a.accno, a.description,
      sum(CASE WHEN ac.transdate < $1 THEN abs(amount) ELSE null END),
      sum(CASE WHEN ac.transdate >= $1 AND ac.amount < 0 
               THEN ac.amount * -1 ELSE null END),
      SUM(CASE WHEN ac.transdate >= $1 AND ac.amount > 0
               THEN ac.amount ELSE null END),
      SUM(ABS(ac.amount))
 FROM account a 
 LEFT
 JOIN acc_trans ac ON ac.chart_id = a.id
 LEFT 
 JOIN (select id, approved from ar UNION
       SELECT id, approved from ap UNION
       SELECT id, approved FROM gl) gl ON ac.trans_id = gl.id
WHERE gl.approved and ac.approved
      and ac.transdate <= $2 
GROUP BY a.id, a.accno, a.description
ORDER BY a.accno;


Function: lsmb13.report__gl(in_business_units text, in_to_amount text, in_from_amount bpchar, in_approved text, in_to_date text, in_from_date text, in_description date, in_memo date, in_source boolean, in_category numeric, in_accno numeric, in_reference integer[])

Returns: SET OF gl_report_item

Language: PLPGSQL

DECLARE 
         retval gl_report_item;
         t_balance numeric;
         t_chart_id int;
BEGIN

IF in_from_date IS NULL THEN
   t_balance := 0;
ELSIF in_accno IS NOT NULL THEN
   SELECT id INTO t_chart_id FROM account WHERE accno  = in_accno;
   t_balance := account__obtain_balance(in_from_date , 
                                       (select id from account 
                                         where accno = in_accno));
ELSE
   t_balance := null;
END IF;

FOR retval IN
       WITH RECURSIVE bu_tree (id, path) AS (
            SELECT id, id::text AS path
              FROM business_unit
             WHERE parent_id is null
            UNION
            SELECT bu.id, bu_tree.path || ',' || bu.id
              FROM business_unit bu
              JOIN bu_tree ON bu_tree.id = bu.parent_id
            )
       SELECT g.id, g.type, g.invoice, g.reference, g.description, ac.transdate,
              ac.source, ac.amount, c.accno, c.gifi_accno, 
              g.till, ac.cleared, ac.memo, c.description AS accname, 
              ac.chart_id, ac.entry_id, 
              sum(ac.amount) over (rows unbounded preceding) + t_balance 
                as running_balance,
              compound_array(ARRAY[ARRAY[bac.class_id, bac.bu_id]])
         FROM (select id, 'gl' as type, false as invoice, reference, 
                      description, approved,
                      null::text as till 
                 FROM gl
               UNION
               SELECT ar.id, 'ar', invoice, invnumber, e.name, approved, till
                 FROM ar
                 JOIN entity_credit_account eca ON ar.entity_credit_account
                      = eca.id
                 JOIN entity e ON e.id = eca.entity_id
               UNION
               SELECT ap.id, 'ap', invoice, invnumber, e.name, approved,
                      null as till
                 FROM ap
                 JOIN entity_credit_account eca ON ap.entity_credit_account 
                      = eca.id
                 JOIN entity e ON e.id = eca.entity_id) g
         JOIN acc_trans ac ON ac.trans_id = g.id
         JOIN account c ON ac.chart_id = c.id
    LEFT JOIN business_unit_ac bac ON ac.entry_id = bac.entry_id 
    LEFT JOIN bu_tree ON bac.bu_id = bu_tree.id
        WHERE (g.reference ilike in_reference || '%' or in_reference is null)
              AND (c.accno = in_accno OR in_accno IS NULL)
              AND (ac.source ilike '%' || in_source || '%' 
                   OR in_source is null)
              AND (ac.memo ilike '%' || in_memo || '%' OR in_memo is null)
             AND (in_description IS NULL OR
                  g.description
                  @@
                  plainto_tsquery(get_default_lang()::regconfig, in_description))
              AND (transdate BETWEEN in_from_date AND in_to_date
                   OR (transdate >= in_from_date AND  in_to_date IS NULL)
                   OR (transdate <= in_to_date AND in_from_date IS NULL)
                   OR (in_to_date IS NULL AND in_from_date IS NULL))
              AND (in_approved is false OR (g.approved AND ac.approved))
              AND (in_from_amount IS NULL OR ac.amount >= in_from_amount)
              AND (in_to_amount IS NULL OR ac.amount <= in_to_amount)
              AND (in_category = c.category OR in_category IS NULL)
     GROUP BY g.id, g.type, g.invoice, g.reference, g.description, ac.transdate,
              ac.source, ac.amount, c.accno, c.gifi_accno,
              g.till, ac.cleared, ac.memo, c.description,
              ac.chart_id, ac.entry_id, ac.trans_id
       HAVING in_business_units is null or in_business_units 
                <@ compound_array(string_to_array(bu_tree.path, ',')::int[])
     ORDER BY ac.transdate, ac.trans_id, c.accno
LOOP
   RETURN NEXT retval;
END LOOP;
END;

Function: lsmb13.report__invoice_aging_detail(in_use_duedate integer, in_business_units integer, in_to_date text, in_accno date, in_entity_class integer[], in_entity_id boolean)

Returns: SET OF report_aging_item

Language: PLPGSQL

DECLARE
	item report_aging_item;
BEGIN
	FOR item IN
                  WITH RECURSIVE bu_tree (id, path) AS (
                SELECT id, id::text AS path
                  FROM business_unit
                 WHERE id = any(in_business_units)
                 UNION
                SELECT bu.id, bu_tree.path || ',' || bu.id
                  FROM business_unit bu
                  JOIN bu_tree ON bu_tree.id = bu.parent_id
                       )
		SELECT c.entity_id, c.meta_number, e.name,
		       e.name as contact_name, 
	               a.invnumber, a.transdate, a.till, a.ordnumber, 
		       a.ponumber, a.notes, 
		       CASE WHEN a.age/30 = 0
		                 THEN (a.sign * sum(ac.amount)) 
                            ELSE 0 END
		            as c0, 
		       CASE WHEN a.age/30 = 1
		                 THEN (a.sign * sum(ac.amount))
                            ELSE 0 END
		            as c30, 
		       CASE WHEN a.age/30 = 2
		            THEN (a.sign * sum(ac.amount))
                            ELSE 0 END
		            as c60, 
		       CASE WHEN a.age/30 > 2
		            THEN (a.sign * sum(ac.amount))
                            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,
                   (coalesce(in_to_date, now())::date - a.transdate) as age
		  FROM (select id, invnumber, till, ordnumber, amount, duedate,
                               curr, ponumber, notes, entity_credit_account,
                               -1 AS sign, transdate,
                               CASE WHEN in_use_duedate 
                                    THEN coalesce(in_to_date, now())::date
                                         - duedate
                                    ELSE coalesce(in_to_date, now())::date
                                         - transdate 
                               END as age
                          FROM ar
                         WHERE in_entity_class = 2
                         UNION 
                        SELECT id, invnumber, null, ordnumber, amount, duedate,
                               curr, ponumber, notes, entity_credit_account,
                               1 as sign, transdate,
                               CASE WHEN in_use_duedate 
                                    THEN coalesce(in_to_date, now())::date
                                         - duedate
                                    ELSE coalesce(in_to_date, now())::date
                                         - transdate 
                               END as age
                          FROM ap
                         WHERE in_entity_class = 1) a
                  JOIN acc_trans ac ON ac.trans_id = a.id
                  JOIN account acc ON ac.chart_id = acc.id
                  JOIN account_link acl ON acl.account_id = acc.id
                       AND ((in_entity_class = 1 
                              AND acl.description = 'AP')
                           OR (in_entity_class = 2
                              AND acl.description = 'AR'))
		  JOIN entity_credit_account c 
                       ON a.entity_credit_account = c.id
		  JOIN entity e ON (e.id = c.entity_id)
             LEFT JOIN business_unit_ac buac ON ac.entry_id = buac.entry_id
             LEFT JOIN bu_tree ON buac.bu_id = bu_tree.id
	     LEFT JOIN entity_to_location e2l 
                       ON e.id = e2l.entity_id 
                       AND e2l.location_class = 3
             LEFT JOIN location l ON l.id = e2l.location_id
	     LEFT JOIN country ON (country.id = l.country_id)
                 WHERE (e.id = in_entity_id OR in_entity_id IS NULL)
                       AND (in_accno IS NULL or acc.accno = in_accno)
              GROUP BY c.entity_id, c.meta_number, e.name,
                       l.line_one, l.line_two, l.line_three,
                       l.city, l.state, l.mail_code, country.name,
                       a.invnumber, a.transdate, a.till, a.ordnumber,
                       a.ponumber, a.notes, a.amount, a.sign,
                       a.duedate, a.id, a.curr, a.age
                HAVING in_business_units is null or in_business_units 
                       <@ compound_array(string_to_array(bu_tree.path, 
                                         ',')::int[])
	      ORDER BY entity_id, curr, transdate, invnumber
	LOOP
		return next item;
        END LOOP;
END;

Function: lsmb13.report__invoice_aging_summary(in_use_duedate integer, in_business_units integer, in_to_date text, in_accno date, in_entity_class integer[], in_entity_id boolean)

Returns: SET OF report_aging_item

Language: SQL

SELECT entity_id, account_number, name, contact_name, null::text, null::date, 
       null::text, null::text, null::text, null::text, 
       sum(c0), sum(c30), sum(c60), sum(c90), null::date, null::int, curr,
       null::numeric, null::text[], null::int
  FROM report__invoice_aging_detail($1, $2, $3, $4, $5, $6)
 GROUP BY entity_id, account_number, name, contact_name, curr
 ORDER BY account_number

Function: lsmb13.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: lsmb13.reset_tsearch()

Returns: void

Language: C

tsa_reset_tsearch

Function: lsmb13.rewrite(tsquery, text)

Returns: tsquery

Language: INTERNAL

tsquery_rewrite_query

Function: lsmb13.rewrite(tsquery, tsquery, tsquery)

Returns: tsquery

Language: INTERNAL

tsquery_rewrite

Function: lsmb13.rewrite(tsquery[])

Returns: tsquery

Language: INTERNAL

aggregate_dummy

Function: lsmb13.rewrite_accum(tsquery, tsquery[])

Returns: tsquery

Language: C

tsa_rewrite_accum

Function: lsmb13.rewrite_finish(tsquery)

Returns: tsquery

Language: C

tsa_rewrite_finish

Function: lsmb13.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: lsmb13.session_check(in_token integer, in_session_id text)

Returns: session

Language: PLPGSQL

Returns a session row. If no session exists, it returns null

DECLARE out_row session%ROWTYPE;
BEGIN
	DELETE FROM session
	 WHERE last_used < now() - coalesce((SELECT value FROM defaults
                                    WHERE setting_key = 'session_timeout')::interval,
	                            '90 minutes'::interval);
        UPDATE session 
           SET last_used = now()
         WHERE session_id = in_session_id
               AND token = in_token
	       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

               PERFORM * 
                  FROM defaults
                 WHERE setting_key = 'auto_logout' and value = '1';

                IF FOUND THEN
                    RAISE NOTICE 'auto logout';
                    RETURN NULL;
                ELSE
                    INSERT INTO session (users_id, token)
                    SELECT id, md5(random()::text)
                      FROM users 
                     WHERE username = SESSION_USER;

                    SELECT * INTO out_row FROM SESSION 
                     WHERE users_id = (select id from users
                                             where username = SESSION_USER);
                    RETURN out_row;
               END IF;
	END IF;
	RETURN out_row;
END;

Function: lsmb13.set_curcfg(integer)

Returns: void

Language: C

tsa_set_curcfg

Function: lsmb13.set_curcfg(text)

Returns: void

Language: C

tsa_set_curcfg_byname

Function: lsmb13.set_curdict(integer)

Returns: void

Language: C

tsa_set_curdict

Function: lsmb13.set_curdict(text)

Returns: void

Language: C

tsa_set_curdict_byname

Function: lsmb13.set_curprs(integer)

Returns: void

Language: C

tsa_set_curprs

Function: lsmb13.set_curprs(text)

Returns: void

Language: C

tsa_set_curprs_byname

Function: lsmb13.set_limit(real)

Returns: real

Language: C

set_limit

Function: lsmb13.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: lsmb13.setting__set(in_value character varying, in_setting_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_setting_key;
        IF NOT FOUND THEN
             INSERT INTO defaults (setting_key, value) 
                  VALUES (in_setting_key, in_value);
        END IF;
	RETURN TRUE;
END;

Function: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.setweight(tsvector, "char")

Returns: tsvector

Language: INTERNAL

tsvector_setweight

Function: lsmb13.show_curcfg()

Returns: oid

Language: INTERNAL

get_current_ts_config

Function: lsmb13.show_limit()

Returns: real

Language: C

show_limit

Function: lsmb13.show_trgm(text)

Returns: text[]

Language: C

show_trgm

Function: lsmb13.similarity(text, text)

Returns: real

Language: C

similarity

Function: lsmb13.similarity_op(text, text)

Returns: boolean

Language: C

similarity_op

Function: lsmb13.snb_en_init(internal)

Returns: internal

Language: C

tsa_snb_en_init

Function: lsmb13.snb_lexize(internal, internal, integer)

Returns: internal

Language: C

tsa_snb_lexize

Function: lsmb13.snb_ru_init(internal)

Returns: internal

Language: C

tsa_snb_ru_init

Function: lsmb13.snb_ru_init_koi8(internal)

Returns: internal

Language: C

tsa_snb_ru_init_koi8

Function: lsmb13.snb_ru_init_utf8(internal)

Returns: internal

Language: C

tsa_snb_ru_init_utf8

Function: lsmb13.spell_init(internal)

Returns: internal

Language: C

tsa_spell_init

Function: lsmb13.spell_lexize(internal, internal, integer)

Returns: internal

Language: C

tsa_spell_lexize

Function: lsmb13.stat(text)

Returns: SET OF statinfo

Language: INTERNAL

ts_stat1

Function: lsmb13.stat(text, text)

Returns: SET OF statinfo

Language: INTERNAL

ts_stat2

Function: lsmb13.strip(tsvector)

Returns: tsvector

Language: INTERNAL

tsvector_strip

Function: lsmb13.syn_init(internal)

Returns: internal

Language: C

tsa_syn_init

Function: lsmb13.syn_lexize(internal, internal, integer)

Returns: internal

Language: C

tsa_syn_lexize

Function: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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 entity_credit_account.id,
                     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, entity_credit_account.id
	LOOP
		RETURN NEXT out_row;
	END LOOP;
END;

Function: lsmb13.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 entity_credit_account.id,
                     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, entity_credit_account.id
    LOOP
		RETURN NEXT out_row;
	END LOOP;
END;

Function: lsmb13.tg_enforce_perms_eclass()

Returns: trigger

Language: PLPGSQL

DECLARE
   r_eclass entity_class;
   roll_pfx text;
BEGIN
IF TG_OP = 'DELETE' THEN
   RETURN OLD;
ELSE 
   SELECT value INTO roll_pfx FROM defaults WHERE setting_key = 'roll_prefix';
   SELECT * INTO r_eclass from entity_class WHERE id = NEW.entity_class;
   IF pg_has_role(SESSION_USER, coalesce(roll_pfx, 
                                         'lsmb_' || current_database() || '__')
                                || 'contact_class_' || lower(regexp_replace(
                                                        r_eclass.class, 
                                                        ' ', 
                                                        '_')), 'USAGE')
   THEN
      RETURN NEW;
   ELSE
      RAISE EXCEPTION 'Access Denied for class';
   END IF;
END IF;
END;

Function: lsmb13.thesaurus_init(internal)

Returns: internal

Language: C

tsa_thesaurus_init

Function: lsmb13.thesaurus_lexize(internal, internal, integer, internal)

Returns: internal

Language: C

tsa_thesaurus_lexize

Function: lsmb13.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: lsmb13.to_args(text[])

Returns: text[]

Language: INTERNAL

Turns a setof ARRAY[key,value] into an ARRAY[key||'='||value, key||'='||value,...]

aggregate_dummy

Function: lsmb13.to_tsquery(oid, text)

Returns: tsquery

Language: INTERNAL

to_tsquery_byid

Function: lsmb13.to_tsquery(text)

Returns: tsquery

Language: INTERNAL

to_tsquery

Function: lsmb13.to_tsquery(text, text)

Returns: tsquery

Language: C

tsa_to_tsquery_name

Function: lsmb13.to_tsvector(oid, text)

Returns: tsvector

Language: INTERNAL

to_tsvector_byid

Function: lsmb13.to_tsvector(text)

Returns: tsvector

Language: INTERNAL

to_tsvector

Function: lsmb13.to_tsvector(text, text)

Returns: tsvector

Language: C

tsa_to_tsvector_name

Function: lsmb13.token_type()

Returns: SET OF tokentype

Language: C

tsa_token_type_current

Function: lsmb13.token_type(integer)

Returns: SET OF tokentype

Language: INTERNAL

ts_token_type_byid

Function: lsmb13.token_type(text)

Returns: SET OF tokentype

Language: INTERNAL

ts_token_type_byname

Function: lsmb13.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: lsmb13.trigger_parts_short()

Returns: trigger

Language: PLPGSQL

BEGIN
  IF NEW.onhand >= NEW.rop THEN
    NOTIFY parts_short;
  END IF;
  RETURN NEW;
END;

Function: lsmb13.trigger_pending_job()

Returns: trigger

Language: PLPGSQL

BEGIN
  IF NEW.success IS NULL THEN
    NOTIFY job_entered;
  END IF;
  RETURN NEW;
END;

Function: lsmb13.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: lsmb13.tsearch2()

Returns: trigger

Language: C

tsa_tsearch2

Function: lsmb13.tsq_mcontained(tsquery, tsquery)

Returns: boolean

Language: INTERNAL

tsq_mcontained

Function: lsmb13.tsq_mcontains(tsquery, tsquery)

Returns: boolean

Language: INTERNAL

tsq_mcontains

Function: lsmb13.tsquery_and(tsquery, tsquery)

Returns: tsquery

Language: INTERNAL

tsquery_and

Function: lsmb13.tsquery_not(tsquery)

Returns: tsquery

Language: INTERNAL

tsquery_not

Function: lsmb13.tsquery_or(tsquery, tsquery)

Returns: tsquery

Language: INTERNAL

tsquery_or

Function: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.user__get_all_users()

Returns: SET OF user_listable

Language: SQL

    
    select * from user_listable;
    

Function: lsmb13.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: lsmb13.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: lsmb13.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: lsmb13.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.invoice, 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.invoice, 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, false, 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, false, 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, false, 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: lsmb13.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: lsmb13.warehouse__list_all()

Returns: SET OF warehouse

Language: SQL

SELECT * FROM warehouse order by description;

Schema lsmb14


Table: lsmb14.ac_tax_form

Mapping journal_line to country_tax_form for reporting purposes.

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

Index - Schema lsmb14


Table: lsmb14.acc_trans

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

lsmb14.acc_trans Structure
F-Key Name Type Description
lsmb14.transactions.id trans_id integer NOT NULL
lsmb14.account.id chart_id integer NOT NULL
amount numeric NOT NULL
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
memo text
invoice_id integer
approved boolean DEFAULT true
cleared_on date
reconciled_on date
lsmb14.voucher.id voucher_id integer
entry_id serial PRIMARY KEY

Tables referencing this one via Foreign Key Constraints:

ac_transdate_year_idx date_part('YEAR'::text, transdate) acc_trans_voucher_id_idx voucher_id

Index - Schema lsmb14


Table: lsmb14.account

This table stores the main account info.

lsmb14.account Structure
F-Key Name Type Description
id serial UNIQUE NOT NULL
accno text PRIMARY KEY
description text
is_temp boolean NOT NULL DEFAULT false

Only affects equity accounts. If set, close at end of year.
category character(1) NOT NULL
gifi_accno text
lsmb14.account_heading.id heading integer NOT NULL
contra boolean NOT NULL DEFAULT false
tax boolean NOT NULL DEFAULT false
obsolete boolean NOT NULL DEFAULT false

Tables referencing this one via Foreign Key Constraints:

Index - Schema lsmb14


Table: lsmb14.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.

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

Index - Schema lsmb14


Table: lsmb14.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.

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

Tables referencing this one via Foreign Key Constraints:

Index - Schema lsmb14


Table: lsmb14.account_link

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

Index - Schema lsmb14


Table: lsmb14.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.

lsmb14.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:

Index - Schema lsmb14


Table: lsmb14.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. HV TODO drop entity_id

lsmb14.ap Structure
F-Key Name Type Description
lsmb14.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
lsmb14.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
lsmb14.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.
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.
lsmb14.entity_credit_account.id entity_credit_account integer NOT NULL

reference for the vendor account used.

 

lsmb14.ap Constraints
Name Constraint
ap_check CHECK ((((amount IS NULL) AND (curr IS NULL)) OR ((amount IS NOT NULL) AND (curr IS NOT NULL))))

Index - Schema lsmb14


Table: lsmb14.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. HV TODO drop entity_id

lsmb14.ar Structure
F-Key Name Type Description
lsmb14.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
lsmb14.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
lsmb14.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.
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.
lsmb14.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
is_return boolean DEFAULT false

 

lsmb14.ar Constraints
Name Constraint
ar_check CHECK ((((amount IS NULL) AND (curr IS NULL)) OR ((amount IS NOT NULL) AND (curr IS NOT NULL))))

Index - Schema lsmb14


Table: lsmb14.assembly

Holds mapping for parts that are members of assemblies.

lsmb14.assembly Structure
F-Key Name Type Description
lsmb14.parts.id id integer PRIMARY KEY

This is the id of the assembly the part is being mapped to.
lsmb14.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 lsmb14


Table: lsmb14.asset_class

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

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

Tables referencing this one via Foreign Key Constraints:

Index - Schema lsmb14


Table: lsmb14.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.

lsmb14.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
lsmb14.asset_unit_class.id unit_class integer NOT NULL

Tables referencing this one via Foreign Key Constraints:

Index - Schema lsmb14


Table: lsmb14.asset_disposal_method

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

 

lsmb14.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:

Index - Schema lsmb14


Table: lsmb14.asset_item

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

lsmb14.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
lsmb14.warehouse.id location_id integer
lsmb14.business_unit.id department_id integer
lsmb14.eca_invoice.journal_id invoice_id integer
lsmb14.account.id asset_account_id integer
lsmb14.account.id dep_account_id integer
lsmb14.account.id exp_account_id integer
lsmb14.asset_item.id obsolete_by integer UNIQUE#1
lsmb14.asset_class.id asset_class_id integer

Tables referencing this one via Foreign Key Constraints:

Index - Schema lsmb14


Table: lsmb14.asset_note

lsmb14.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"()
lsmb14.asset_item.id ref_key integer NOT NULL
subject text

Table lsmb14.asset_note Inherits note,

 

lsmb14.asset_note Constraints
Name Constraint
asset_note_note_class_check CHECK ((note_class = 4))

Index - Schema lsmb14


Table: lsmb14.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.

lsmb14.asset_report Structure
F-Key Name Type Description
id serial PRIMARY KEY
report_date date
lsmb14.journal_entry.id gl_id bigint UNIQUE
lsmb14.asset_class.id asset_class bigint
lsmb14.asset_report_class.id report_class integer
lsmb14.entity.id entered_by bigint NOT NULL DEFAULT person__get_my_entity_id()
lsmb14.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:

Index - Schema lsmb14


Table: lsmb14.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.

lsmb14.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:

Index - Schema lsmb14


Table: lsmb14.asset_report_line

lsmb14.asset_report_line Structure
F-Key Name Type Description
lsmb14.asset_item.id asset_id bigint PRIMARY KEY
lsmb14.asset_report.id report_id bigint PRIMARY KEY
amount numeric
lsmb14.business_unit.id department_id integer

In case assets are moved between departments, we have to store this here.
lsmb14.warehouse.id warehouse_id integer

Index - Schema lsmb14


Table: lsmb14.asset_rl_to_disposal_method

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

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

Index - Schema lsmb14


Table: lsmb14.asset_unit_class

lsmb14.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:

Index - Schema lsmb14


Table: lsmb14.audittrail

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

lsmb14.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()
lsmb14.person.entity_id person_id integer NOT NULL
entry_id bigserial PRIMARY KEY
audittrail_trans_id_key trans_id

Index - Schema lsmb14


Table: lsmb14.batch

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

lsmb14.batch Structure
F-Key Name Type Description
id serial PRIMARY KEY
lsmb14.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
lsmb14.entity_employee.entity_id approved_by integer
lsmb14.entity_employee.entity_id created_by integer
lsmb14.session.session_id locked_by integer
created_on date DEFAULT now()

 

lsmb14.batch Constraints
Name Constraint
batch_control_code_check CHECK ((length(control_code) > 0))

Tables referencing this one via Foreign Key Constraints:

Index - Schema lsmb14


Table: lsmb14.batch_class

These values are hard-coded. Please coordinate before adding standard values. Values from 900 to 999 are reserved for local use.

lsmb14.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:

Index - Schema lsmb14


Table: lsmb14.bu_class_to_module

lsmb14.bu_class_to_module Structure
F-Key Name Type Description
lsmb14.business_unit_class.id bu_class_id integer PRIMARY KEY
lsmb14.lsmb_module.id module_id integer PRIMARY KEY

Index - Schema lsmb14


Table: lsmb14.budget_info

lsmb14.budget_info Structure
F-Key Name Type Description
id serial UNIQUE NOT NULL
start_date date NOT NULL
end_date date NOT NULL
reference text PRIMARY KEY
description text NOT NULL
lsmb14.entity.id entered_by integer NOT NULL DEFAULT person__get_my_entity_id()
lsmb14.entity.id approved_by integer
lsmb14.entity.id obsolete_by integer
entered_at timestamp without time zone NOT NULL DEFAULT now()
approved_at timestamp without time zone
obsolete_at timestamp without time zone

 

lsmb14.budget_info Constraints
Name Constraint
budget_info_check CHECK ((start_date < end_date))

Tables referencing this one via Foreign Key Constraints:

Index - Schema lsmb14


Table: lsmb14.budget_line

lsmb14.budget_line Structure
F-Key Name Type Description
lsmb14.budget_info.id budget_id integer PRIMARY KEY
lsmb14.account.id account_id integer PRIMARY KEY
description text
amount numeric NOT NULL

Index - Schema lsmb14


Table: lsmb14.budget_note

lsmb14.budget_note Structure
F-Key Name Type Description
id integer PRIMARY KEY DEFAULT nextval('note_id_seq'::regclass)
note_class integer NOT NULL DEFAULT 6
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"()
lsmb14.budget_info.id ref_key integer NOT NULL
subject text

Table lsmb14.budget_note Inherits note,

 

lsmb14.budget_note Constraints
Name Constraint
budget_note_note_class_check CHECK ((note_class = 6))

Index - Schema lsmb14


Table: lsmb14.budget_to_business_unit

lsmb14.budget_to_business_unit Structure
F-Key Name Type Description
lsmb14.budget_info.id budget_id integer UNIQUE PRIMARY KEY
lsmb14.business_unit.id bu_id integer NOT NULL
lsmb14.business_unit_class.id bu_class integer PRIMARY KEY

Index - Schema lsmb14


Table: lsmb14.business

Groups of Customers assigned joint discounts.

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

Index - Schema lsmb14


Table: lsmb14.business_unit

Tracks Projects, Departments, Funds, Etc.

lsmb14.business_unit Structure
F-Key Name Type Description
id serial UNIQUE#1 PRIMARY KEY
lsmb14.business_unit_class.id class_id integer UNIQUE#2 UNIQUE#1 NOT NULL
control_code text UNIQUE#2
description text
start_date date
end_date date
lsmb14.business_unit.id parent_id integer
lsmb14.entity_credit_account.id credit_id integer

Tables referencing this one via Foreign Key Constraints:

Index - Schema lsmb14


Table: lsmb14.business_unit_ac

lsmb14.business_unit_ac Structure
F-Key Name Type Description
lsmb14.acc_trans.entry_id entry_id integer PRIMARY KEY
lsmb14.business_unit_class.id lsmb14.business_unit.class_id#1 class_id integer PRIMARY KEY
lsmb14.business_unit.id#1 bu_id integer PRIMARY KEY

Index - Schema lsmb14


Table: lsmb14.business_unit_class

Consolidates projects and departments, and allows this to be extended for funds accounting and other purposes.

lsmb14.business_unit_class Structure
F-Key Name Type Description
id serial UNIQUE NOT NULL
label text PRIMARY KEY
active boolean NOT NULL DEFAULT false
ordering integer

Tables referencing this one via Foreign Key Constraints:

Index - Schema lsmb14


Table: lsmb14.business_unit_inv

lsmb14.business_unit_inv Structure
F-Key Name Type Description
lsmb14.invoice.id entry_id integer PRIMARY KEY
lsmb14.business_unit_class.id lsmb14.business_unit.class_id#1 class_id integer PRIMARY KEY
lsmb14.business_unit.id#1 bu_id integer PRIMARY KEY

Index - Schema lsmb14


Table: lsmb14.business_unit_jl

lsmb14.business_unit_jl Structure
F-Key Name Type Description
lsmb14.journal_line.id entry_id integer PRIMARY KEY
lsmb14.business_unit_class.id bu_class integer PRIMARY KEY
lsmb14.business_unit.id bu_id integer NOT NULL

Index - Schema lsmb14


Table: lsmb14.business_unit_oitem

lsmb14.business_unit_oitem Structure
F-Key Name Type Description
lsmb14.orderitems.id entry_id integer PRIMARY KEY
lsmb14.business_unit_class.id lsmb14.business_unit.class_id#1 class_id integer PRIMARY KEY
lsmb14.business_unit.id#1 bu_id integer PRIMARY KEY

Index - Schema lsmb14


Table: lsmb14.business_unit_translation

Translation information for projects, departments, etc.

lsmb14.business_unit_translation Structure
F-Key Name Type Description
lsmb14.business_unit.id trans_id integer PRIMARY KEY
language_code character varying(6) PRIMARY KEY
description text

Table lsmb14.business_unit_translation Inherits translation,

Index - Schema lsmb14


View: lsmb14.cash_impact

This view is used by cash basis reports to determine the fraction of a transaction to be counted.

lsmb14.cash_impact Structure
F-Key Name Type Description
id integer
portion numeric
rel text
(
SELECT gl.id
     , 1::numeric AS portion
     ,'gl'::text AS rel 
  FROM lsmb14.gl 
UNION ALLSELECT gl.id
     , CASE WHEN 
     (
           (gl.amount - sum
                 (ac.amount)
           ) = 
           (0)::numeric
     ) THEN 
     (0)::numeric ELSE 
     (gl.amount / 
           (gl.amount - sum
                 (ac.amount)
           )
     ) END AS portion
     ,'ar'::text AS rel 
  FROM (
           (lsmb14.ar gl 
              JOIN lsmb14.acc_trans ac 
                ON (
                       (ac.trans_id = gl.id)
                 )
           )
        JOIN lsmb14.account_link al 
          ON (
                 (
                       (ac.chart_id = al.account_id)
                     AND (al.description = 'AR'::text)
                 )
           )
     )
GROUP BY gl.id
     , gl.amount
)
UNION ALLSELECT gl.id
, CASE WHEN 
(
     (gl.amount - sum
           (ac.amount)
     ) = 
     (0)::numeric
) THEN 
(0)::numeric ELSE 
(gl.amount / 
     (gl.amount - sum
           (ac.amount)
     )
) END AS portion
,'ap'::text AS rel 
FROM (
     (lsmb14.ap gl 
        JOIN lsmb14.acc_trans ac 
          ON (
                 (ac.trans_id = gl.id)
           )
     )
  JOIN lsmb14.account_link al 
    ON (
           (
                 (ac.chart_id = al.account_id)
               AND (al.description = 'AP'::text)
           )
     )
)
GROUP BY gl.id
, gl.amount;

Index - Schema lsmb14


View: lsmb14.chart

Compatibility chart for 1.2 and earlier.

lsmb14.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'::text AS charttype
, NULL::bpchar AS category
, NULL::text AS link
, NULL::integer AS account_heading
, NULL::text AS gifi_accno
, false AS contra
, false AS tax 
FROM lsmb14.account_heading 
UNIONSELECT c.id
, c.accno
, c.description
,'A'::text AS charttype
, c.category
, lsmb14.concat_colon
(l.description) AS link
, c.heading AS account_heading
, c.gifi_accno
, c.contra
, c.tax 
FROM (lsmb14.account c 
LEFT JOIN lsmb14.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 lsmb14


Table: lsmb14.company

lsmb14.company Structure
F-Key Name Type Description
id serial UNIQUE NOT NULL
lsmb14.entity.id entity_id integer PRIMARY KEY
legal_name text PRIMARY KEY
tax_id text

In the US this would be a EIN.
sales_tax_id text
license_number text
sic_code character varying
created date NOT NULL DEFAULT ('now'::text)::date

 

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

Tables referencing this one via Foreign Key Constraints:

Index - Schema lsmb14


Table: lsmb14.contact_class

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

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

 

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

Tables referencing this one via Foreign Key Constraints:

Index - Schema lsmb14


Table: lsmb14.country

lsmb14.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

 

lsmb14.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:

Index - Schema lsmb14


Table: lsmb14.country_tax_form

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

lsmb14.country_tax_form Structure
F-Key Name Type Description
lsmb14.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:

Index - Schema lsmb14


Table: lsmb14.cr_coa_to_account

Provides name mapping for the cash reconciliation screen.

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

Index - Schema lsmb14


Table: lsmb14.cr_report

This table holds header data for cash reports.

lsmb14.cr_report Structure
F-Key Name Type Description
id bigserial PRIMARY KEY
lsmb14.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()
lsmb14.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
lsmb14.entity.id deleted_by integer
lsmb14.entity.id approved_by integer
approved_username text
recon_fx boolean DEFAULT false

 

lsmb14.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:

Index - Schema lsmb14


Table: lsmb14.cr_report_line

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

lsmb14.cr_report_line Structure
F-Key Name Type Description
id bigserial PRIMARY KEY
lsmb14.cr_report.id report_id integer NOT NULL
scn text

This is the check number. Maps to journal_entry.reference
their_balance numeric
our_balance numeric
errorcode integer
lsmb14.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
lsmb14.acc_trans.entry_id ledger_id integer
voucher_id integer
overlook boolean NOT NULL DEFAULT false
cleared boolean NOT NULL DEFAULT false

Index - Schema lsmb14


Table: lsmb14.custom_field_catalog

Deprecated, use only with old code.

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

Index - Schema lsmb14


Table: lsmb14.custom_table_catalog

Deprecated, use only with old code.

lsmb14.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:

Index - Schema lsmb14


Table: lsmb14.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.

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

Index - Schema lsmb14


Table: lsmb14.eca_invoice

Replaces the rest of the ar and ap tables. Also tracks payments and receipts.

lsmb14.eca_invoice Structure
F-Key Name Type Description
order_id integer

Link to order it was created from
lsmb14.journal_entry.id journal_id integer PRIMARY KEY
on_hold boolean DEFAULT false

On hold invoices can not be paid, and overpayments that are on hold cannot be used to pay invoices.
reverse boolean DEFAULT false

When this is set to true, the invoice is shown with opposite normal numbers, i.e. negatives appear as positives, and positives appear as negatives.
lsmb14.entity_credit_account.id credit_id integer NOT NULL
due date NOT NULL
lsmb14.language.code language_code character(6)
force_closed boolean NOT NULL DEFAULT false

When this is set to true, the invoice does not show up on outstanding reports and cannot be paid. Overpayments where this is set to true do not appear on outstanding reports and cannot be paid.
order_number text

This is the order number of the other party. So for a sales invoice, this would be a purchase order, and for a vendor invoice, this would be a sales order.

Tables referencing this one via Foreign Key Constraints:

Index - Schema lsmb14


Table: lsmb14.eca_note

Notes for entity_credit_account entries.

lsmb14.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"()
lsmb14.entity_credit_account.id ref_key integer NOT NULL

references entity_credit_account.id
subject text

Table lsmb14.eca_note Inherits note,

 

lsmb14.eca_note Constraints
Name Constraint
eca_note_note_class_check CHECK ((note_class = 3))

Index - Schema lsmb14


Table: lsmb14.eca_tax

Mapping customers and vendors to taxes.

lsmb14.eca_tax Structure
F-Key Name Type Description
lsmb14.entity_credit_account.id eca_id integer PRIMARY KEY
lsmb14.account.id chart_id integer PRIMARY KEY

Index - Schema lsmb14


Table: lsmb14.eca_to_contact

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

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

 

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

Index - Schema lsmb14


Table: lsmb14.eca_to_location

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

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

Index - Schema lsmb14


Table: lsmb14.employee_class

lsmb14.employee_class Structure
F-Key Name Type Description
label text PRIMARY KEY
id serial UNIQUE NOT NULL

Tables referencing this one via Foreign Key Constraints:

Index - Schema lsmb14


View: lsmb14.employee_search

lsmb14.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 (
     (
           (
                 (lsmb14.entity_employee e 
               LEFT JOIN lsmb14.entity en 
                      ON (
                             (e.entity_id = en.id)
                       )
                 )
         LEFT JOIN lsmb14.entity_employee m 
                ON (
                       (e.manager_id = m.entity_id)
                 )
           )
   LEFT JOIN lsmb14.entity em 
          ON (
                 (em.id = m.entity_id)
           )
     )
LEFT JOIN lsmb14.entity_note emn 
    ON (
           (emn.ref_key = em.id)
     )
);

Index - Schema lsmb14


Table: lsmb14.employee_to_ec

lsmb14.employee_to_ec Structure
F-Key Name Type Description
lsmb14.entity_employee.entity_id employee_id integer PRIMARY KEY
lsmb14.employee_class.id ec_id integer

Index - Schema lsmb14


View: lsmb14.employees

lsmb14.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 (
     (lsmb14.person p 
        JOIN lsmb14.entity_employee ee 
       USING (entity_id)
     )
LEFT JOIN lsmb14.salutation s 
    ON (
           (p.salutation_id = s.id)
     )
);

Index - Schema lsmb14


Table: lsmb14.entity

The primary entity table to map to all contacts

lsmb14.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
lsmb14.entity_class.id lsmb14.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)
lsmb14.country.id country_id integer NOT NULL

 

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

Tables referencing this one via Foreign Key Constraints:

Index - Schema lsmb14


Table: lsmb14.entity_bank_account

This stores bank account information for both companies and persons.

lsmb14.entity_bank_account Structure
F-Key Name Type Description
id serial UNIQUE NOT NULL
lsmb14.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:

Index - Schema lsmb14


Table: lsmb14.entity_class

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

lsmb14.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
lsmb14.country.id country_id integer
active boolean NOT NULL DEFAULT true

 

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

Tables referencing this one via Foreign Key Constraints:

entity_class_idx lower(class)

Index - Schema lsmb14


Table: lsmb14.entity_credit_account

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

lsmb14.entity_credit_account Structure
F-Key Name Type Description
id serial UNIQUE NOT NULL
lsmb14.entity.id entity_id integer PRIMARY KEY
lsmb14.entity_class.id entity_class integer PRIMARY KEY
pay_to_name text
discount numeric
description text
discount_terms integer
lsmb14.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
lsmb14.language.code language_code character varying(6) DEFAULT 'en'::character varying
lsmb14.pricegroup.id pricegroup_id integer
curr character(3)
startdate date DEFAULT ('now'::text)::date
enddate date
threshold numeric
lsmb14.entity_employee.entity_id employee_id integer
lsmb14.person.id primary_contact integer
lsmb14.account.id ar_ap_account_id integer
lsmb14.account.id cash_account_id integer
lsmb14.entity_bank_account.id bank_account integer
lsmb14.country_tax_form.id taxform_id integer

 

lsmb14.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:

Index - Schema lsmb14


Table: lsmb14.entity_employee

This contains employee-specific extensions to person/entity.

lsmb14.entity_employee Structure
F-Key Name Type Description
lsmb14.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
lsmb14.entity.id manager_id integer
employeenumber character varying(32)
dob date

Tables referencing this one via Foreign Key Constraints:

Index - Schema lsmb14


Table: lsmb14.entity_note

lsmb14.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"()
lsmb14.entity.id ref_key integer NOT NULL
subject text
lsmb14.entity.id entity_id integer

Table lsmb14.entity_note Inherits note,

 

lsmb14.entity_note Constraints
Name Constraint
entity_note_note_class_check CHECK ((note_class = 1))
entity_note_id_idx id entity_note_vectors_idx vector

Index - Schema lsmb14


Table: lsmb14.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.

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

 

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

Index - Schema lsmb14


Table: lsmb14.entity_to_contact

This table stores contact information for entities

lsmb14.entity_to_contact Structure
F-Key Name Type Description
lsmb14.entity.id entity_id integer PRIMARY KEY
lsmb14.contact_class.id contact_class_id integer PRIMARY KEY
contact text PRIMARY KEY
description text

 

lsmb14.entity_to_contact Constraints
Name Constraint
entity_to_contact_contact_check CHECK ((contact ~ '[[:alnum:]_]'::text))

Index - Schema lsmb14


Table: lsmb14.entity_to_location

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

lsmb14.entity_to_location Structure
F-Key Name Type Description
lsmb14.location.id location_id integer PRIMARY KEY
lsmb14.location_class.id location_class integer PRIMARY KEY
lsmb14.entity.id entity_id integer PRIMARY KEY

Index - Schema lsmb14


Table: lsmb14.exchangerate

When you receive money in a foreign currency, it is worth to you in your local currency whatever you can get for it when you sell the acquired currency (sell rate). When you have to pay someone in a foreign currency, the equivalent amount is the amount you have to spend to acquire the foreign currency (buy rate).

lsmb14.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 lsmb14


Table: lsmb14.file_base

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

lsmb14.file_base Structure
F-Key Name Type Description
content bytea NOT NULL
lsmb14.mime_type.id mime_type_id integer NOT NULL
file_name text PRIMARY KEY
description text
lsmb14.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.
lsmb14.file_class.id file_class integer PRIMARY KEY

Index - Schema lsmb14


Table: lsmb14.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.

lsmb14.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:

Index - Schema lsmb14


Table: lsmb14.file_eca

File attachments primarily attached to customer and vendor agreements.

lsmb14.file_eca 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)
lsmb14.entity_credit_account.id ref_key integer PRIMARY KEY
file_class integer PRIMARY KEY

Table lsmb14.file_eca Inherits file_base,

 

lsmb14.file_eca Constraints
Name Constraint
file_eca_file_class_check CHECK ((file_class = 5))

Index - Schema lsmb14


Table: lsmb14.file_entity

File attachments primarily attached to entities.

lsmb14.file_entity 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)
lsmb14.entity.id ref_key integer PRIMARY KEY
file_class integer PRIMARY KEY

Table lsmb14.file_entity Inherits file_base,

 

lsmb14.file_entity Constraints
Name Constraint
file_entity_file_class_check CHECK ((file_class = 4))

Index - Schema lsmb14


View: lsmb14.file_links

lsmb14.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 lsmb14.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 lsmb14.file_order_links;

Index - Schema lsmb14


Table: lsmb14.file_order

File attachments primarily attached to orders and quotations.

lsmb14.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)
lsmb14.oe.id ref_key integer PRIMARY KEY
file_class integer PRIMARY KEY

Table lsmb14.file_order Inherits file_base,

 

lsmb14.file_order Constraints
Name Constraint
file_order_file_class_check CHECK ((file_class = 2))

Tables referencing this one via Foreign Key Constraints:

Index - Schema lsmb14


View: lsmb14.file_order_links

lsmb14.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 (
     (lsmb14.file_secondary_attachment sl 
        JOIN lsmb14.oe 
          ON (
                 (sl.ref_key = oe.id)
           )
     )
  JOIN lsmb14.oe_class oc 
    ON (
           (oe.oe_class_id = oc.id)
     )
)
WHERE (sl.source_class = 2);

Index - Schema lsmb14


Table: lsmb14.file_order_to_order

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

lsmb14.file_order_to_order Structure
F-Key Name Type Description
lsmb14.file_order.id file_id integer PRIMARY KEY
source_class integer PRIMARY KEY
lsmb14.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 lsmb14.file_order_to_order Inherits file_secondary_attachment,

 

lsmb14.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 lsmb14


Table: lsmb14.file_order_to_tx

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

lsmb14.file_order_to_tx Structure
F-Key Name Type Description
lsmb14.file_order.id file_id integer PRIMARY KEY
source_class integer PRIMARY KEY
lsmb14.journal_entry.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 lsmb14.file_order_to_tx Inherits file_secondary_attachment,

 

lsmb14.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 lsmb14


Table: lsmb14.file_part

File attachments primarily attached to goods and services.

lsmb14.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)
lsmb14.parts.id ref_key integer PRIMARY KEY
file_class integer PRIMARY KEY

Table lsmb14.file_part Inherits file_base,

 

lsmb14.file_part Constraints
Name Constraint
file_part_file_class_check CHECK ((file_class = 3))

Index - Schema lsmb14


Table: lsmb14.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.

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

Index - Schema lsmb14


Table: lsmb14.file_transaction

File attachments primarily attached to AR/AP/GL.

lsmb14.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)
lsmb14.journal_entry.id ref_key integer PRIMARY KEY
file_class integer PRIMARY KEY

Table lsmb14.file_transaction Inherits file_base,

 

lsmb14.file_transaction Constraints
Name Constraint
file_transaction_file_class_check CHECK ((file_class = 1))

Tables referencing this one via Foreign Key Constraints:

Index - Schema lsmb14


View: lsmb14.file_tx_links

lsmb14.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 (lsmb14.file_secondary_attachment sl 
  JOIN (
           (
            SELECT gl.id
                 , gl.reference
                 ,'gl'::text AS type 
              FROM lsmb14.gl 
             UNIONSELECT ar.id
                 , ar.invnumber
                 , CASE WHEN ar.invoice THEN 'is'::text ELSE 'ar'::text END AS type 
              FROM lsmb14.ar
           )
       UNIONSELECT ap.id
           , ap.invnumber
           , CASE WHEN ap.invoice THEN 'ir'::text ELSE 'ap'::text END AS type 
        FROM lsmb14.ap
     ) gl 
    ON (
           (
                 (sl.ref_key = gl.id)
               AND (sl.source_class = 1)
           )
     )
);

Index - Schema lsmb14


Table: lsmb14.file_tx_to_order

Secondary links from journal entries to orders.

lsmb14.file_tx_to_order Structure
F-Key Name Type Description
lsmb14.file_transaction.id file_id integer PRIMARY KEY
source_class integer PRIMARY KEY
lsmb14.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 lsmb14.file_tx_to_order Inherits file_secondary_attachment,

 

lsmb14.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 lsmb14


Table: lsmb14.file_view_catalog

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

Index - Schema lsmb14


Table: lsmb14.gifi

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

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

Index - Schema lsmb14


Table: lsmb14.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.

lsmb14.gl Structure
F-Key Name Type Description
lsmb14.transactions.id id integer PRIMARY KEY DEFAULT nextval('id'::regclass)
reference text
description text
transdate date DEFAULT ('now'::text)::date
lsmb14.person.id person_id integer

the person_id of the employee who created the entry.
notes text
approved boolean DEFAULT true

Tables referencing this one via Foreign Key Constraints:

Index - Schema lsmb14


Table: lsmb14.inventory

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

lsmb14.inventory Structure
F-Key Name Type Description
lsmb14.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 lsmb14


Table: lsmb14.inventory_report

lsmb14.inventory_report Structure
F-Key Name Type Description
id serial PRIMARY KEY
transdate date NOT NULL
source text
ar_trans_id integer
ap_trans_id integer

Tables referencing this one via Foreign Key Constraints:

Index - Schema lsmb14


Table: lsmb14.inventory_report_line

lsmb14.inventory_report_line Structure
F-Key Name Type Description
lsmb14.inventory_report.id report_id integer PRIMARY KEY
lsmb14.parts.id parts_id integer PRIMARY KEY
counted numeric
expected numeric

Index - Schema lsmb14


Table: lsmb14.invoice

Line items of invoices with goods/services attached.

lsmb14.invoice Structure
F-Key Name Type Description
id serial PRIMARY KEY
lsmb14.transactions.id trans_id integer
lsmb14.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
deliverydate date
serialnumber text
notes text

Tables referencing this one via Foreign Key Constraints:

invoice_id_key id invoice_trans_id_key trans_id

Index - Schema lsmb14


Table: lsmb14.invoice_note

lsmb14.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"()
lsmb14.invoice.id ref_key integer NOT NULL
subject text

Table lsmb14.invoice_note Inherits note,

invoice_note_id_idx id invoice_note_vectors_idx vector

Index - Schema lsmb14


Table: lsmb14.invoice_tax_form

Maping invoice to country_tax_form.

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

Index - Schema lsmb14


Table: lsmb14.jcitems

Time and materials cards. Materials cards not implemented.

lsmb14.jcitems Structure
F-Key Name Type Description
id serial PRIMARY KEY
lsmb14.business_unit.id business_unit_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
lsmb14.person.id person_id integer NOT NULL
notes text
total numeric NOT NULL
non_billable numeric NOT NULL
jcitems_id_key id

Index - Schema lsmb14


Table: lsmb14.job

lsmb14.job Structure
F-Key Name Type Description
lsmb14.business_unit.id bu_id integer PRIMARY KEY
parts_id integer

Job costing/manufacturing here not implemented.
production numeric
completed numeric

Index - Schema lsmb14


Table: lsmb14.journal_entry

This tale records the header information for each transaction. It replaces parts of the following tables: acc_trans, ar, ap, gl, transactions. Note now all ar/ap transactions are also journal entries.

lsmb14.journal_entry Structure
F-Key Name Type Description
id serial PRIMARY KEY
reference text NOT NULL

Invoice number or journal entry number.
description text
lsmb14.session.session_id locked_by integer
lsmb14.journal_type.id journal integer
post_date date NOT NULL DEFAULT now()
effective_start date NOT NULL

For transactions whose effects are spread out over a period of time, this is the effective start date for the transaction. To be used by add-ons for automating adjustments.
effective_end date NOT NULL

For transactions whose effects are spread out over a period of time, this is the effective end date for the transaction. To be used by add-ons for automating adjustments.
currency character(3) NOT NULL
approved boolean DEFAULT false
is_template boolean DEFAULT false

Set true for template transactions. Templates can never be approved but can be copied into new transactions and are useful for recurrances.
lsmb14.entity.id entered_by integer NOT NULL
lsmb14.entity.id approved_by integer

 

lsmb14.journal_entry Constraints
Name Constraint
journal_entry_check CHECK (((is_template IS FALSE) OR (approved IS FALSE)))

Tables referencing this one via Foreign Key Constraints:

Index - Schema lsmb14


Table: lsmb14.journal_line

Replaces acc_trans as the main account transaction line table.

lsmb14.journal_line Structure
F-Key Name Type Description
id serial PRIMARY KEY
lsmb14.account.id account_id integer NOT NULL
lsmb14.journal_entry.id journal_id integer NOT NULL
amount numeric NOT NULL
cleared boolean NOT NULL DEFAULT false

Still needed both for legacy data and in case reconciliation data must eventually be purged.
lsmb14.cr_report.id reconciliation_report integer
lsmb14.account_link_description.description line_type text

 

lsmb14.journal_line Constraints
Name Constraint
journal_line_amount_check CHECK ((amount <> 'NaN'::numeric))

Tables referencing this one via Foreign Key Constraints:

Index - Schema lsmb14


Table: lsmb14.journal_note

This stores notes attached to journal entries, including payments and invoices.

lsmb14.journal_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"()
lsmb14.journal_entry.id ref_key integer NOT NULL
subject text
internal_only boolean NOT NULL DEFAULT false

When set to true, does not show up in notes list for invoice templates

Table lsmb14.journal_note Inherits note,

 

lsmb14.journal_note Constraints
Name Constraint
journal_note_note_class_check CHECK ((note_class = 5))

Index - Schema lsmb14


Table: lsmb14.journal_type

This table describes the journal entry type of the transaction. The following values are hard coded by default: 1: General journal 2: Sales (AR) 3: Purchases (AP) 4: Receipts 5: Payments

lsmb14.journal_type Structure
F-Key Name Type Description
id serial UNIQUE NOT NULL
name text PRIMARY KEY

Tables referencing this one via Foreign Key Constraints:

Index - Schema lsmb14


Table: lsmb14.language

Languages for manual translations and so forth.

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

Tables referencing this one via Foreign Key Constraints:

Index - Schema lsmb14


Table: lsmb14.location

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

lsmb14.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
lsmb14.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

 

lsmb14.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:

Index - Schema lsmb14


Table: lsmb14.location_class

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

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

 

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

Tables referencing this one via Foreign Key Constraints:

Index - Schema lsmb14


Table: lsmb14.lsmb_group

lsmb14.lsmb_group Structure
F-Key Name Type Description
role_name text PRIMARY KEY

Tables referencing this one via Foreign Key Constraints:

Index - Schema lsmb14


Table: lsmb14.lsmb_group_grants

lsmb14.lsmb_group_grants Structure
F-Key Name Type Description
lsmb14.lsmb_group.role_name group_name text PRIMARY KEY
granted_role text PRIMARY KEY

Index - Schema lsmb14


Table: lsmb14.lsmb_module

This stores categories functionality into modules. Addons may add rows here, but the id should be hardcoded. As always 900-1000 will be reserved for internal use, and negative numbers will be reserved for testing.

lsmb14.lsmb_module Structure
F-Key Name Type Description
id integer UNIQUE NOT NULL
label text PRIMARY KEY

Tables referencing this one via Foreign Key Constraints:

Index - Schema lsmb14


Table: lsmb14.lsmb_roles

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

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

Index - Schema lsmb14


Table: lsmb14.makemodel

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

lsmb14.makemodel Structure
F-Key Name Type Description
parts_id integer PRIMARY KEY
barcode text
make text PRIMARY KEY
model text PRIMARY KEY
makemodel_make_key lower(make) makemodel_model_key lower(model) makemodel_parts_id_key parts_id

Index - Schema lsmb14


Table: lsmb14.menu_acl

Provides access control list entries for menu nodes.

lsmb14.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'.
lsmb14.menu_node.id node_id integer PRIMARY KEY

 

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

Index - Schema lsmb14


Table: lsmb14.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.

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

Index - Schema lsmb14


View: lsmb14.menu_friendly

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

lsmb14.menu_friendly Structure
F-Key Name Type Description
level integer
path text
label text
id integer
position integer
WITH RECURSIVE tree
(path
     , id
     , parent
     , level
     , positions
) AS 
(
SELECT (menu_node.id)::text AS path
     , menu_node.id
     , menu_node.parent
     , 0 AS level
     , (menu_node."position")::text AS "position"
  FROM lsmb14.menu_node 
 WHERE (menu_node.parent IS NULL)
 UNIONSELECT (
           (t.path || 
               ','::text
           ) || 
           (n.id)::text
     )
     , n.id
     , n.parent
     , (t.level + 1)
     , (
           (t.positions || 
               ','::text
           ) || n."position"
     )
  FROM (lsmb14.menu_node n 
        JOIN tree t 
          ON (
                 (t.id = n.parent)
           )
     )
)
SELECT t.level
, t.path
, (repeat
     (' '::text
           , (2 * t.level)
     ) || 
     (n.label)::text
) AS label
, n.id
, n."position"
FROM (tree t 
  JOIN lsmb14.menu_node n 
 USING (id)
)
ORDER BY (string_to_array
     (t.positions
           ,','::text
     )
)::integer[];

Index - Schema lsmb14


Table: lsmb14.menu_node

This table stores the tree structure of the menu.

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

Tables referencing this one via Foreign Key Constraints:

Index - Schema lsmb14


Table: lsmb14.mime_type

This is a lookup table for storing MIME types.

lsmb14.mime_type Structure
F-Key Name Type Description
id serial UNIQUE NOT NULL
mime_type text PRIMARY KEY
invoice_include boolean DEFAULT false

Tables referencing this one via Foreign Key Constraints:

Index - Schema lsmb14


Table: lsmb14.new_shipto

Tracks ship_to information for orders and invoices.

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

Index - Schema lsmb14


Table: lsmb14.note

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

lsmb14.note Structure
F-Key Name Type Description
id serial PRIMARY KEY
lsmb14.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 lsmb14


Table: lsmb14.note_class

Coordinate with others before adding entries.

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

 

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

Tables referencing this one via Foreign Key Constraints:

Index - Schema lsmb14


Table: lsmb14.oe

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

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

Tables referencing this one via Foreign Key Constraints:

oe_id_key id oe_ordnumber_key ordnumber oe_transdate_key transdate

Index - Schema lsmb14


Table: lsmb14.oe_class

Hardwired classifications for orders and quotations. Coordinate before adding.

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

 

lsmb14.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:

Index - Schema lsmb14


Table: lsmb14.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.

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

Index - Schema lsmb14


Table: lsmb14.orderitems

Line items for sales/purchase orders and quotations.

lsmb14.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)
reqdate date
ship numeric
serialnumber text
notes text

Tables referencing this one via Foreign Key Constraints:

orderitems_id_key id orderitems_trans_id_key trans_id

Index - Schema lsmb14


View: lsmb14.overpayments

lsmb14.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
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
, abs
(sum
     (ac.amount)
) AS available
, cmp.legal_name
, eca.id AS entity_credit_id
, eca.entity_id
, eca.discount
, eca.meta_number 
FROM (
     (
           (
                 (
                       (lsmb14.payment p 
                          JOIN lsmb14.payment_links pl 
                            ON (
                                   (pl.payment_id = p.id)
                             )
                       )
                    JOIN lsmb14.acc_trans ac 
                      ON (
                             (ac.entry_id = pl.entry_id)
                       )
                 )
              JOIN lsmb14.chart c 
                ON (
                       (c.id = ac.chart_id)
                 )
           )
        JOIN lsmb14.entity_credit_account eca 
          ON (
                 (eca.id = p.entity_credit_id)
           )
     )
  JOIN lsmb14.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
, cmp.legal_name
, eca.id
, eca.entity_id
, eca.discount
, eca.meta_number;

Index - Schema lsmb14


Table: lsmb14.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.

lsmb14.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.
lsmb14.account.id inventory_accno_id integer
lsmb14.account.id income_accno_id integer
lsmb14.account.id expense_accno_id integer
lsmb14.account.id returns_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
avgcost numeric

Tables referencing this one via Foreign Key Constraints:

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

Index - Schema lsmb14


Table: lsmb14.parts_translation

Translation information for parts.

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

Table lsmb14.parts_translation Inherits translation,

Index - Schema lsmb14


Table: lsmb14.partscustomer

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

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

Index - Schema lsmb14


Table: lsmb14.partsgroup

Groups of parts for Point of Sale screen.

lsmb14.partsgroup Structure
F-Key Name Type Description
id serial PRIMARY KEY
partsgroup text
lsmb14.partsgroup.id parent integer

Tables referencing this one via Foreign Key Constraints:

partsgroup_id_key id

Index - Schema lsmb14


Table: lsmb14.partsgroup_translation

Translation information for partsgroups.

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

Table lsmb14.partsgroup_translation Inherits translation,

Index - Schema lsmb14


Table: lsmb14.partstax

Mapping of parts to taxes.

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

Index - Schema lsmb14


Table: lsmb14.partsvendor

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

lsmb14.partsvendor Structure
F-Key Name Type Description
lsmb14.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 lsmb14


Table: lsmb14.payment

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

lsmb14.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
lsmb14.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
lsmb14.entity_credit_account.id entity_credit_id integer
lsmb14.person.id employee_id integer
currency character(3)
notes text

Tables referencing this one via Foreign Key Constraints:

payment_id_idx id

Index - Schema lsmb14


Table: lsmb14.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.

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

Index - Schema lsmb14


Table: lsmb14.payment_map

This maps the payment journal entry to the invoices it pays. A couple notes here: 1) There is no requirement tht the payment "invoice" be linked to the same entity_credit_account as the paid invoice. People can pay eachothers invoices if LedgerSMB supports this at an app level. 2) This now means that payments are first class transactions.

lsmb14.payment_map Structure
F-Key Name Type Description
lsmb14.journal_line.id line_id integer PRIMARY KEY
lsmb14.eca_invoice.journal_id pays integer NOT NULL

Index - Schema lsmb14


Table: lsmb14.payment_type

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

Index - Schema lsmb14


Table: lsmb14.payroll_deduction

lsmb14.payroll_deduction Structure
F-Key Name Type Description
entry_id serial UNIQUE NOT NULL
lsmb14.entity.id entity_id integer PRIMARY KEY
lsmb14.payroll_deduction_type.id type_id integer PRIMARY KEY
rate numeric NOT NULL

Index - Schema lsmb14


Table: lsmb14.payroll_deduction_class

lsmb14.payroll_deduction_class Structure
F-Key Name Type Description
id integer UNIQUE#1 NOT NULL
lsmb14.country.id country_id integer UNIQUE#1 PRIMARY KEY
label text PRIMARY KEY

Tables referencing this one via Foreign Key Constraints:

Index - Schema lsmb14


Table: lsmb14.payroll_deduction_type

lsmb14.payroll_deduction_type Structure
F-Key Name Type Description
id serial UNIQUE NOT NULL
lsmb14.account.id account_id integer NOT NULL
lsmb14.payroll_deduction_class.id#1 pdc_id integer NOT NULL
lsmb14.payroll_deduction_class.country_id#1 country_id integer NOT NULL
label text NOT NULL
unit text NOT NULL
default_amount numeric
calc_percent boolean NOT NULL

Tables referencing this one via Foreign Key Constraints:

Index - Schema lsmb14


Table: lsmb14.payroll_employee_class

lsmb14.payroll_employee_class Structure
F-Key Name Type Description
id serial UNIQUE NOT NULL
label text PRIMARY KEY

Tables referencing this one via Foreign Key Constraints:

Index - Schema lsmb14


Table: lsmb14.payroll_employee_class_to_income_type

lsmb14.payroll_employee_class_to_income_type Structure
F-Key Name Type Description
lsmb14.payroll_employee_class.id ec_id integer PRIMARY KEY
lsmb14.payroll_income_type.id it_id integer PRIMARY KEY

Index - Schema lsmb14


Table: lsmb14.payroll_income_category

lsmb14.payroll_income_category Structure
F-Key Name Type Description
id serial UNIQUE NOT NULL
label text

Index - Schema lsmb14


Table: lsmb14.payroll_income_class

lsmb14.payroll_income_class Structure
F-Key Name Type Description
id integer UNIQUE#1 NOT NULL
lsmb14.country.id country_id integer UNIQUE#1 PRIMARY KEY
label text PRIMARY KEY

Tables referencing this one via Foreign Key Constraints:

Index - Schema lsmb14


Table: lsmb14.payroll_income_type

lsmb14.payroll_income_type Structure
F-Key Name Type Description
id serial UNIQUE NOT NULL
lsmb14.account.id account_id integer NOT NULL
lsmb14.payroll_income_class.id#1 pic_id integer NOT NULL
lsmb14.payroll_income_class.country_id#1 country_id integer NOT NULL
label text NOT NULL
unit text NOT NULL
default_amount numeric

Tables referencing this one via Foreign Key Constraints:

Index - Schema lsmb14


Table: lsmb14.payroll_paid_timeoff

lsmb14.payroll_paid_timeoff Structure
F-Key Name Type Description
lsmb14.entity.id employee_id integer NOT NULL
lsmb14.payroll_pto_class.id pto_class_id integer NOT NULL
lsmb14.payroll_report.id report_id integer NOT NULL
amount numeric NOT NULL

Index - Schema lsmb14


Table: lsmb14.payroll_pto_class

lsmb14.payroll_pto_class Structure
F-Key Name Type Description
id serial UNIQUE NOT NULL
label text PRIMARY KEY

Tables referencing this one via Foreign Key Constraints:

Index - Schema lsmb14


Table: lsmb14.payroll_report

lsmb14.payroll_report Structure
F-Key Name Type Description
id serial PRIMARY KEY
lsmb14.payroll_employee_class.id ec_id integer NOT NULL
payment_date date NOT NULL
lsmb14.entity_employee.entity_id created_by integer
lsmb14.entity_employee.entity_id approved_by integer

Tables referencing this one via Foreign Key Constraints:

Index - Schema lsmb14


Table: lsmb14.payroll_report_line

lsmb14.payroll_report_line Structure
F-Key Name Type Description
id serial UNIQUE NOT NULL
lsmb14.payroll_report.id report_id integer PRIMARY KEY
lsmb14.entity.id employee_id integer PRIMARY KEY
lsmb14.payroll_income_type.id it_id integer PRIMARY KEY
qty numeric NOT NULL
rate numeric NOT NULL
description text

Index - Schema lsmb14


Table: lsmb14.payroll_wage

lsmb14.payroll_wage Structure
F-Key Name Type Description
entry_id serial UNIQUE NOT NULL
lsmb14.entity.id entity_id integer PRIMARY KEY
lsmb14.payroll_income_type.id type_id integer PRIMARY KEY
rate numeric NOT NULL

Index - Schema lsmb14


View: lsmb14.periods

lsmb14.periods Structure
F-Key Name Type Description
id text
label text
date_to date
date_from date
SELECT'ytd'::text AS id
,'Year to Date'::text AS label
, (now
     ()
)::date AS date_to
, (
     (
           (date_part
                 ('year'::text
                       , now
                       ()
                 )
           )::text || '-01-01'::text
     )
)::date AS date_from 
UNIONSELECT'last_year'::text AS id
,'Last Year'::text 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 lsmb14


Table: lsmb14.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.

lsmb14.person Structure
F-Key Name Type Description
id serial PRIMARY KEY
lsmb14.entity.id entity_id integer UNIQUE NOT NULL
lsmb14.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

 

lsmb14.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:

Index - Schema lsmb14


Table: lsmb14.person_to_company

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

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

Index - Schema lsmb14


Table: lsmb14.pricegroup

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

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

Tables referencing this one via Foreign Key Constraints:

pricegroup_id_key id pricegroup_pricegroup_key pricegroup

Index - Schema lsmb14


View: lsmb14.recon_payee

lsmb14.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
, ac.voucher_id
, rr.overlook
, rr.cleared 
FROM (
     (
           (lsmb14.cr_report_line rr 
         LEFT JOIN lsmb14.acc_trans ac 
                ON (
                       (rr.ledger_id = ac.entry_id)
                 )
           )
   LEFT JOIN lsmb14.gl 
          ON (
                 (ac.trans_id = gl.id)
           )
     )
LEFT JOIN (
           (
            SELECT ap.id
                 , e.name 
              FROM (
                       (lsmb14.ap 
                          JOIN lsmb14.entity_credit_account eca 
                            ON (
                                   (ap.entity_credit_account = eca.id)
                             )
                       )
                    JOIN lsmb14.entity e 
                      ON (
                             (eca.entity_id = e.id)
                       )
                 )
             UNIONSELECT ar.id
                 , e.name 
              FROM (
                       (lsmb14.ar 
                          JOIN lsmb14.entity_credit_account eca 
                            ON (
                                   (ar.entity_credit_account = eca.id)
                             )
                       )
                    JOIN lsmb14.entity e 
                      ON (
                             (eca.entity_id = e.id)
                       )
                 )
           )
       UNIONSELECT gl.id
           , gl.description 
        FROM lsmb14.gl
     ) n 
    ON (
           (n.id = ac.trans_id)
     )
);

Index - Schema lsmb14


Table: lsmb14.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.

lsmb14.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 lsmb14


Table: lsmb14.recurringemail

Email to be sent out when recurring transaction is posted.

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

Index - Schema lsmb14


Table: lsmb14.recurringprint

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

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

Index - Schema lsmb14


View: lsmb14.role_view

lsmb14.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
rolreplication 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.rolreplication
, 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 lsmb14


Table: lsmb14.salutation

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

Tables referencing this one via Foreign Key Constraints:

Index - Schema lsmb14


Table: lsmb14.session

This table is used to track sessions on a database level across page requests (discretionary locks,open forms for anti-xsrf measures). 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.

lsmb14.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
lsmb14.users.id users_id integer NOT NULL
notify_pasword interval NOT NULL DEFAULT '7 days'::interval

 

lsmb14.session Constraints
Name Constraint
session_token_check CHECK ((length((token)::text) = 32))

Tables referencing this one via Foreign Key Constraints:

Index - Schema lsmb14


Table: lsmb14.sic

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

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

Index - Schema lsmb14


Table: lsmb14.status

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

lsmb14.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 lsmb14


Table: lsmb14.tax

Information on tax rates.

lsmb14.tax Structure
F-Key Name Type Description
lsmb14.account.id lsmb14.account.id chart_id integer PRIMARY KEY
rate numeric
minvalue numeric
maxvalue 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).
lsmb14.taxmodule.taxmodule_id taxmodule_id integer NOT NULL DEFAULT 1

Index - Schema lsmb14


Table: lsmb14.tax_extended

This stores extended information for manual tax calculations.

lsmb14.tax_extended Structure
F-Key Name Type Description
tax_basis numeric
rate numeric
lsmb14.journal_line.id entry_id integer PRIMARY KEY

Index - Schema lsmb14


Table: lsmb14.taxcategory

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

Tables referencing this one via Foreign Key Constraints:

Index - Schema lsmb14


Table: lsmb14.taxmodule

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

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

Tables referencing this one via Foreign Key Constraints:

Index - Schema lsmb14


Table: lsmb14.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.

lsmb14.transactions Structure
F-Key Name Type Description
id integer PRIMARY KEY
table_name text
lsmb14.session.session_id locked_by integer

This should only be used in pessimistic locking measures as required by large batch work flows.
approved boolean
lsmb14.entity.id approved_by integer
approved_at timestamp without time zone

Tables referencing this one via Foreign Key Constraints:

transactions_locked_by_i locked_by

Index - Schema lsmb14


Table: lsmb14.translation

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

lsmb14.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 lsmb14


Table: lsmb14.trial_balance

lsmb14.trial_balance Structure
F-Key Name Type Description
id serial PRIMARY KEY
date_from date
date_to date
description text NOT NULL
lsmb14.trial_balance__yearend_types.type yearend text NOT NULL

Tables referencing this one via Foreign Key Constraints:

Index - Schema lsmb14


Table: lsmb14.trial_balance__account_to_report

lsmb14.trial_balance__account_to_report Structure
F-Key Name Type Description
lsmb14.trial_balance.id report_id integer NOT NULL
lsmb14.account.id account_id integer NOT NULL

Index - Schema lsmb14


Table: lsmb14.trial_balance__heading_to_report

lsmb14.trial_balance__heading_to_report Structure
F-Key Name Type Description
lsmb14.trial_balance.id report_id integer NOT NULL
lsmb14.account_heading.id heading_id integer NOT NULL

Index - Schema lsmb14


Table: lsmb14.trial_balance__yearend_types

lsmb14.trial_balance__yearend_types Structure
F-Key Name Type Description
type text PRIMARY KEY

Tables referencing this one via Foreign Key Constraints:

Index - Schema lsmb14


View: lsmb14.tx_report

This view provides join and approval information for transactions.

lsmb14.tx_report Structure
F-Key Name Type Description
id integer
reference text
entity_credit_account integer
table text
approved boolean
(
SELECT gl.id
     , gl.reference
     , NULL::integer AS entity_credit_account
     ,'gl'::text AS "table"
     , gl.approved 
  FROM lsmb14.gl 
UNION ALLSELECT ap.id
     , ap.invnumber AS reference
     , ap.entity_credit_account
     ,'ap'::text AS "table"
     , ap.approved 
  FROM lsmb14.ap
)
UNION ALLSELECT ar.id
, ar.invnumber AS reference
, ar.entity_credit_account
,'ar'::text AS "table"
, ar.approved 
FROM lsmb14.ar;

Index - Schema lsmb14


View: lsmb14.user_listable

lsmb14.user_listable Structure
F-Key Name Type Description
id integer
username character varying(30)
created date
SELECT u.id
, u.username
, e.created 
FROM (lsmb14.entity e 
  JOIN lsmb14.users u 
    ON (
           (u.entity_id = e.id)
     )
);

Index - Schema lsmb14


Table: lsmb14.user_preference

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

lsmb14.user_preference Structure
F-Key Name Type Description
lsmb14.users.id id integer PRIMARY KEY
lsmb14.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 lsmb14


Table: lsmb14.users

lsmb14.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
lsmb14.entity.id entity_id integer NOT NULL

Tables referencing this one via Foreign Key Constraints:

Index - Schema lsmb14


Table: lsmb14.voucher

Mapping transactions to batches for batch approval.

lsmb14.voucher Structure
F-Key Name Type Description
lsmb14.transactions.id trans_id integer NOT NULL
lsmb14.batch.id batch_id integer NOT NULL
id serial PRIMARY KEY

This is simply a surrogate key for easy reference.
lsmb14.batch_class.id batch_class integer NOT NULL

This is the authoritative class of the voucher.

Tables referencing this one via Foreign Key Constraints:

Index - Schema lsmb14


Table: lsmb14.warehouse

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

Tables referencing this one via Foreign Key Constraints:

Index - Schema lsmb14


Table: lsmb14.yearend

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

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

Index - Schema lsmb14


Function: lsmb14._entity_location_save(in_country_id 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 entity_to_location
	WHERE entity_id = in_entity_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_id) 
	INTO l_id;

	INSERT INTO entity_to_location
		(entity_id, location_class, location_id)
	VALUES  (in_entity_id, in_location_class, l_id);

	RETURN l_id;    
    END;


Function: lsmb14.account__delete(in_id integer)

Returns: boolean

Language: PLPGSQL

This deletes an account with the id specified. If the account has transactions associated with it, it will fail and raise a foreign key constraint.

BEGIN
DELETE FROM account_link WHERE account_id = in_id;
DELETE FROM account WHERE id = in_id;
RETURN FOUND;
END;

Function: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.account__is_recon(in_accno text)

Returns: boolean

Language: SQL

Returns true if account is set up for reconciliation, false otherwise. Note that returns false on invalid account number too

 SELECT count(*) > 0 
     FROM cr_coa_to_account c2a
     JOIN account ON account.id = c2a.chart_id 
    WHERE accno = $1; 

Function: lsmb14.account__list_by_heading()

Returns: SET OF account

Language: SQL

SELECT * FROM account ORDER BY heading;

Function: lsmb14.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: lsmb14.account__save(in_is_temp integer, in_obsolete text, in_link text, in_tax bpchar, in_contra text, in_heading integer, in_gifi_accno boolean, in_category boolean, in_description text[], in_accno boolean, in_id boolean)

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,
                obsolete = in_obsolete,
                tax = t_tax,
                is_temp = in_is_temp
	WHERE id = in_id;

	IF FOUND THEN
		t_id := in_id;
	ELSE
                -- can't obsolete on insert, but this can be changed if users
                -- request it --CT
		INSERT INTO account (accno, description, category, gifi_accno,
			heading, contra, tax, is_temp)
		VALUES (in_accno, in_description, in_category, in_gifi_accno,
			t_heading_id, in_contra, in_tax, in_is_temp);

		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: lsmb14.account__save_tax(in_old_validto integer, in_taxmodule_id date, in_pass numeric, in_taxnumber numeric, in_maxvalue numeric, in_minvalue 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,
               minvalue = in_minvalue,
               maxvalue = in_maxvalue,
               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, minvalue, maxvalue, taxnumber,
                        pass, taxmodule_id)
         VALUES (in_chart_id, in_validto, in_rate, in_minvalue, in_maxvalue, 
                in_taxnumber, in_pass, in_taxmodule_id);

         RETURN TRUE;

END;

Function: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.account_heading_list()

Returns: SET OF account_heading

Language: SQL

Lists all existing account headings.

SELECT * FROM account_heading order by accno;

Function: lsmb14.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: lsmb14.add_custom_field(field_datatype character varying, new_field_name character varying, table_name character varying)

Returns: boolean

Language: PLPGSQL

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: lsmb14.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: lsmb14.admin__add_group_to_role(in_role_name text, in_group_name text)

Returns: boolean

Language: PLPGSQL

This function inserts the arguments into lsmb_group_grants for future reference and issues the db-level grant. It then returns true if there are no exceptions.

 
BEGIN
   PERFORM * FROM lsmb_group_grants 
     WHERE group_name = in_group_name AND
           granted_role = in_role_name;

 IF NOT FOUND THEN
   INSERT INTO lsmb_group_grants(group_name, granted_role) 
   VALUES (in_group_name, in_role_name);
 END IF;

   EXECUTE 'GRANT ' || quote_ident(in_role_name) || ' TO ' ||
           quote_literal('lsmb_' || t_dbname || '__' || in_group_name);
   RETURN TRUE;
END;

Function: lsmb14.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: lsmb14.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 '|| quote_ident('lsmb_' || t_dbname || '__' || in_group_name);
        execute stmt;
        INSERT INTO lsmb_group (role_name) 
             values (quote_literal('lsmb_' || t_dbname || '__' || in_group_name));
        return 1;
    END;
    

Function: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.admin__get_user(in_entity_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 entity_id = in_entity_id;
        return next a_user;
        return;
    
    END;    

Function: lsmb14.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: lsmb14.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: lsmb14.admin__list_group_grants(in_group_name text)

Returns: SET OF lsmb_group_grants

Language: SQL

SELECT * FROM lsmb_group_grants WHERE group_name = $1
ORDER BY granted_role;

Function: lsmb14.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: lsmb14.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: lsmb14.admin__remove_group_from_role(in_role_name text, in_group_name text)

Returns: boolean

Language: PLPGSQL

Returns true if the grant record was found and deleted, false otherwise. Issues db-level revoke in all cases.

BEGIN

   EXECUTE 'REVOKE ' || quote_ident(in_role_name) || ' FROM ' ||
           quote_literal('lsmb_' || t_dbname || '__' || in_group_name);

   DELETE FROM lsmb_group_grants 
    WHERE group_name = in_group_name AND granted_role = in_role_name;

   RETURN FOUND;

END;


Function: lsmb14.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: lsmb14.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 and in_import 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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.asset_dep_straight_line_yr_d(in_report_id integer[], in_report_date date, in_asset_ids integer)

Returns: boolean

Language: SQL

     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, -- years
                  ai.usable_life - 
                  get_fractional_year(coalesce(max(report_date),
                                         start_depreciation,
                                         purchase_date),
                                       coalesce(start_depreciation,
                                         purchase_date)),
                  get_fractional_year(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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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.id IS NULL OR 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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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
	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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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 batch WHERE id = in_batch_id;
	DELETE FROM transactions WHERE id = ANY(t_transaction_ids);

	RETURN 1;
END;

Function: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.budget__approve(in_id integer)

Returns: budget_info_ext

Language: SQL

UPDATE budget_info 
   set approved_at = now(), approved_by = person__get_my_entity_id()
 WHERE id = $1;

SELECT budget__get_info($1);

Function: lsmb14.budget__get_business_units(in_id integer)

Returns: SET OF business_unit

Language: SQL

 select bu.*
     FROM business_unit bu
     JOIN budget_to_business_unit b2bu ON b2bu.bu_id = bu.id
     JOIN budget_info bi ON bi.id = b2bu.budget_id
    WHERE bi.id = $1
 ORDER BY bu.class_id;

Function: lsmb14.budget__get_details(in_id integer)

Returns: SET OF budget_line

Language: SQL

This retrieves the budget lines associated with a budget.

  SELECT * FROM budget_line where budget_id = $1;

Function: lsmb14.budget__get_info(in_id integer)

Returns: budget_info_ext

Language: SQL

Selects the budget info.

 
select bi.id, bi.start_date, bi.end_date, bi.reference, bi.description, 
       bi.entered_by, bi.approved_by, bi.obsolete_by, bi.entered_at, 
       bi.approved_at, bi.obsolete_at, 
       ee.name, ae.name, oe.name
  from budget_info bi
  JOIN entity ee ON bi.entered_by = ee.id
  LEFT JOIN entity ae ON bi.approved_by = ae.id
  LEFT JOIN entity oe ON bi.obsolete_by = oe.id
 where bi.id = $1; 

Function: lsmb14.budget__get_notes(in_id integer)

Returns: SET OF budget_note

Language: SQL

Returns all notes associated with a budget, by default in the order they were created.

 
SELECT * FROM budget_note WHERE ref_key = $1
 ORDER BY created;

Function: lsmb14.budget__mark_obsolete(in_id integer)

Returns: budget_info_ext

Language: SQL

Marks a budget as obsolete

UPDATE budget_info 
   set obsolete_by = person__get_my_entity_id(), obsolete_at = now()
 WHERE id = $1 and approved_by is not null;
SELECT budget__get_info($1)

Function: lsmb14.budget__reject(in_id integer)

Returns: boolean

Language: PLPGSQL

Deletes unapproved budgets only.

BEGIN

DELETE FROM budget_line 
 WHERE budget_id IN (SELECT id from budget_info 
                      WHERE id = in_id AND approved_by IS NULL);

DELETE FROM budget_to_project
 WHERE budget_id IN (SELECT id from budget_info 
                      WHERE id = in_id AND approved_by IS NULL);

DELETE FROM budget_to_department
 WHERE budget_id IN (SELECT id from budget_info 
                      WHERE id = in_id AND approved_by IS NULL);

DELETE FROM budget_info WHERE id = in_id AND approved_by IS NULL;

RETURN FOUND;
END;

Function: lsmb14.budget__save_details(in_details integer, in_id text[])

Returns: budget_info_ext

Language: PLPGSQL

This saves the line items for the budget. in_details is an array n long where each entry is {int account_id, text description, numeric amount}. The in_id parameter is the budget_id.

DECLARE
   loop_count int;
   retval budget_info_ext;
BEGIN
    FOR loop_count in   
        array_lower(in_details, 1) ..
        array_upper(in_details, 1)
    LOOP
        INSERT INTO budget_line 
                    (budget_id, 
                     account_id, 
                     description, 
                     amount)
             VALUES (in_id, 
                     in_details[loop_count][1]::int, 
                     in_details[loop_count][2], 
                     in_details[loop_count][3]::numeric);
    END LOOP;
    retval := budget__get_info(in_id);
    return retval;
END;

Function: lsmb14.budget__save_info(in_business_units integer, in_description date, in_reference date, in_end_date text, in_start_date text, in_id integer[])

Returns: budget_info_ext

Language: PLPGSQL

Saves the extended budget info passed through to the function. See the comment on type budget_info_ext for more information.

DECLARE 
   retval budget_info_ext;
   t_id int;
BEGIN

   PERFORM * FROM budget_info WHERE id = in_id and approved_by is not null;
   IF FOUND THEN
       RAISE EXCEPTION 'report approved';
   END IF;

  UPDATE budget_info
     SET start_date = in_start_date,
         end_date = in_end_date,
         reference = in_reference,
         description = in_description
   WHERE id = in_id and approved_by is null;
  IF FOUND THEN
      t_id := in_id;
  ELSE
       INSERT INTO budget_info (start_date, end_date, reference, description)
            VALUES (in_start_date, in_end_date, in_reference, in_description);
       t_id = currval('budget_info_id_seq');

       INSERT INTO budget_to_business_unit(budget_id, bu_id, bu_class)
       SELECT t_id, id, class_id
         FROM business_unit
        WHERE id = ANY(in_business_units);
  END IF;
  retval := budget__get_info(t_id);
  return retval;
END;

Function: lsmb14.budget__save_note(in_note integer, in_subject text, in_id text)

Returns: budget_note

Language: SQL

Saves a note attached to a budget.

INSERT INTO budget_note (subject, note, ref_key) 
     values ($2, $3, $1);

SELECT * FROM budget_note WHERE id = currval('note_id_seq'::regclass);

Function: lsmb14.budget__search(in_is_obsolete date, in_is_approved date, in_business_units date, in_obsolete_by text, in_approved_by text, in_entered_by integer, in_description integer, in_reference integer, in_includes_date integer[], in_end_date boolean, in_start_date boolean)

Returns: SET OF budget_info_ext

Language: SQL

This is a general search for budgets

select bi.id, bi.start_date, bi.end_date, bi.reference, bi.description, 
       bi.entered_by, bi.approved_by, bi.obsolete_by, bi.entered_at, 
       bi.approved_at, bi.obsolete_at, 
       ee.name, ae.name, oe.name
  from budget_info bi 
  JOIN entity ee ON bi.entered_by = ee.id
  LEFT JOIN entity ae ON bi.approved_by = ae.id
  LEFT JOIN entity oe ON bi.obsolete_by = oe.id
 WHERE (start_date = $1 or $1 is null) AND ($2 = end_date or $2 is null) 
       AND ($3 BETWEEN start_date AND end_date or $2 is null)
       AND ($4 ilike reference || '%' or $4 is null) 
       AND (bi.description @@ plainto_tsquery($5) or $5 is null) 
       AND ($6 = entered_by or $6 is null) 
       AND ($7 = approved_by or $7 is null) 
       AND ($8 = obsolete_by or $8 is null) 
       AND ($10 IS NULL OR ($10 = (approved_by IS NOT NULL)))
       AND ($11 IS NULL OR ($11 = (obsolete_by IS NOT NULL)))
 ORDER BY reference;

Function: lsmb14.budget__variance_report(in_id integer)

Returns: SET OF budget_variance_report

Language: SQL

Retrieves a variance report for budget with an id of in_id.

   WITH agg_account (amount, id, transdate)
        AS ( SELECT ac.amount *
                    CASE WHEN a.contra THEN -1 ELSE 1 END *
                    CASE WHEN a.category IN ('A', 'E') THEN -1 ELSE 1 END
                    AS amount,
                    ac.chart_id, ac.transdate
               FROM acc_trans ac
               JOIN account a ON ac.chart_id = a.id
           )
   SELECT act.accno, act.description, act.id, b.description, b.amount,
          coalesce(sum(a.amount), 0), 
          b.amount - coalesce(sum(a.amount), 0) AS variance
     FROM budget_info bi
     JOIN budget_line b ON bi.id = b.budget_id
     JOIN account act ON act.id = b.account_id
LEFT JOIN agg_account a ON a.transdate BETWEEN bi.start_date and bi.end_date
                           AND a.id = b.account_id
    WHERE bi.id = $1
 GROUP BY act.accno, act.description, act.id, b.description, b.amount
 ORDER BY act.accno;

Function: lsmb14.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: lsmb14.business_unit__get(in_id integer)

Returns: business_unit

Language: SQL

 SELECT * FROM business_unit where id = $1; 

Function: lsmb14.business_unit__get_tree_for(in_id integer)

Returns: SET OF business_unit_short

Language: SQL

This function returns tree-related records with the root of the tree being the business unit of in_id.

WITH RECURSIVE tree  (id, control_code, description,  start_date, end_date, 
                      parent_id, path, level)
AS (
   SELECT id, control_code, description, start_date, end_date, parent_id, 
          ARRAY[parent_id] AS path, 1 as level
     FROM business_unit WHERE $1 = id
    UNION
   SELECT t.id, t.control_code, t.description, t.start_date, t.end_date, 
          t.parent_id,   
          t.path || bu.id AS path, t.level + 1 as level
     FROM business_unit bu JOIN tree t ON t.parent_id = bu.id
)
SELECT * FROM tree ORDER BY path;

Function: lsmb14.business_unit__list_by_class(in_strict_credit integer, in_credit_id date, in_active_on integer, in_business_unit_class_id boolean)

Returns: SET OF business_unit

Language: PLPGSQL

This function retUrns a list of all units (projects, departments, funds, etc) active on the in_active_on date, where in_credit_id matches the credit id of the customer or vendor requested, and where in_business_uni_class_id is the class id of the class of business units (1 for department, 2 for project, etc). With the exception of in_business_unit_class_id, the null matches all records.

BEGIN
RETURN QUERY SELECT * FROM business_unit 
              WHERE (in_active_on BETWEEN coalesce(start_date, in_active_on) 
                                      AND coalesce(end_date, in_active_on) 
                      OR in_active_on IS NULL)
                    AND (in_credit_id = credit_id
                        OR (credit_id IS NULL and in_strict_credit IS NOT TRUE)
                        OR (in_credit_id IS NULL))
                    AND class_id = in_business_unit_class_id
           ORDER BY control_code;
END;

Function: lsmb14.business_unit__list_classes(in_module boolean, in_active text)

Returns: SET OF business_unit_class

Language: SQL

This function lists all business unit clases. If in_active is true, then only active classes are listed. If it is false then only inactive classes are listed. If it is null, then all classes are listed.


SELECT bc.* 
  FROM business_unit_class bc
 WHERE     (active = $1 OR $1 IS NULL)
       AND (id IN (select bu_class_id 
                     FROM bu_class_to_module bcm
                     JOIN lsmb_module mod ON mod.id = bcm.module_id
                    WHERE lower(label) = lower($2))
            OR $2 is null)
ORDER BY ordering;


Function: lsmb14.business_unit__save(in_credit_id integer, in_parent_id integer, in_end_date text, in_start_date text, in_description date, in_control_code date, in_class_id integer, in_id integer)

Returns: business_unit

Language: PLPGSQL

DECLARE retval business_unit;
        t_id int;

BEGIN

UPDATE business_unit
   SET class_id = in_class_id,
       control_code = in_control_code,
       description = in_description,
       start_date = in_start_date,
       end_date = in_end_date,
       credit_id = in_credit_id
 WHERE id = in_id;


IF FOUND THEN
   t_id := in_id;
ELSE
   INSERT INTO business_unit 
          (class_id, control_code, description, start_date, end_date, parent_id,
           credit_id)
   VALUES (in_class_id, in_control_code, in_description, in_start_date, 
           in_end_date, in_parent_id, in_credit_id);
    t_id := currval('business_unit_id_seq');
END IF;

SELECT * INTO retval FROM business_unit WHERE id = t_id;

RETURN retval;
END;

Function: lsmb14.business_unit_class__get_modules(in_id integer)

Returns: SET OF lsmb_module

Language: SQL

 SELECT * FROM lsmb_module 
    WHERE id IN (select module_id from bu_class_to_module where bu_class_id = $1)
 ORDER BY id;

Function: lsmb14.business_unit_class__save(in_ordering integer, in_active text, in_label boolean, in_id integer)

Returns: business_unit_class

Language: PLPGSQL

DECLARE retval business_unit_class;
        t_id int;
BEGIN

t_id := in_id;
UPDATE business_unit_class
   SET label = in_label,
       active = in_active,
       ordering = in_ordering
 WHERE id = in_id;

IF NOT FOUND THEN

   INSERT INTO business_unit_class (label, active, ordering)
   VALUES (in_label, in_active, in_ordering);

   t_id := currval('business_unit_class_id_seq');

END IF;

SELECT * INTO retval FROM business_unit_class WHERE id = t_id;

RETURN retval;

END;


Function: lsmb14.business_unit_class__save_modules(in_mod_ids integer, in_id integer[])

Returns: boolean

Language: SQL

DELETE FROM bu_class_to_module WHERE bu_class_id = $1;

INSERT INTO bu_class_to_module (bu_class_id, module_id)
SELECT $1, unnest
  FROM unnest($2);

SELECT true;

Function: lsmb14.business_unit_get(in_id integer)

Returns: business_unit

Language: SQL

 SELECT * FROM business_unit WHERE id = $1; 

Function: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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))
                       AND not obsolete
              ORDER BY accno
	LOOP
		RETURN next out_row;
	END LOOP;
END;

Function: lsmb14.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: lsmb14.cogs__add_for_ap(in_lastcost integer, in_qty numeric, in_parts_id numeric)

Returns: numeric

Language: PLPGSQL

DECLARE t_alloc numeric := 0;
        t_cogs numeric := 0;
        t_inv invoice;
        t_cp account_checkpoint;
        t_ar ar;
        t_avail numeric;
BEGIN


IF in_qty > 0 THEN
   return cogs__reverse_ap(in_parts_id, in_qty * -1) * in_lastcost;
END IF;

SELECT * INTO t_cp FROM account_checkpoint ORDER BY end_date DESC LIMIT 1;

FOR t_inv IN
    SELECT i.*
      FROM invoice i
      JOIN ar a ON a.id = i.trans_id
     WHERE qty + allocated > 0 and parts_id  = in_parts_id
  ORDER BY a.transdate, a.id, i.id
LOOP
   t_avail := t_inv.qty + t_inv.allocated;
   SELECT * INTO t_ar FROM ar WHERE id = t_inv.trans_id;
   IF t_alloc < in_qty THEN
       RAISE EXCEPTION 'TOO MANY ALLOCATED';
   ELSIF t_alloc = in_qty THEN
       return t_alloc;
   ELSIF (in_qty + t_alloc) * -1 <=  t_avail  THEN
       UPDATE invoice SET allocated = allocated + (in_qty + t_alloc)
        WHERE id = t_inv.id;

       INSERT INTO acc_trans 
              (chart_id, transdate, amount, invoice_id, approved, trans_id)
       SELECT expense_accno_id, 
              CASE WHEN t_ar.transdate > t_cp.end_date THEN t_ar.transdate
                   ELSE t_cp.end_date + '1 day'::interval
               END, (in_qty + t_alloc) * in_lastcost, t_inv.id, true,
              t_inv.trans_id
         FROM parts 
       WHERE  id = t_inv.parts_id AND inventory_accno_id IS NOT NULL
              AND expense_accno_id IS NOT NULL
       UNION
       SELECT income_accno_id,
              CASE WHEN t_ar.transdate > t_cp.end_date THEN t_ar.transdate
                   ELSE t_cp.end_date + '1 day'::interval
               END, -1 * (in_qty + t_alloc) * in_lastcost, t_inv.id, true,
              t_inv.trans_id
         FROM parts 
       WHERE  id = t_inv.parts_id AND inventory_accno_id IS NOT NULL
              AND expense_accno_id IS NOT NULL;
                                    
       t_cogs := t_cogs + (in_qty + t_alloc) * in_lastcost;
       return in_qty * -1;
   ELSE
       UPDATE invoice SET allocated = qty * -1
        WHERE id = t_inv.id;
       t_cogs := t_cogs + t_avail * in_lastcost;

       INSERT INTO acc_trans
              (chart_id, transdate, amount, invoice_id, approved, trans_id)
       SELECT expense_accno_id,
              CASE WHEN t_ar.transdate > t_cp.end_date THEN t_ar.transdate
                   ELSE t_cp.end_date + '1 day'::interval
               END,  -1 * t_avail * in_lastcost, 
              t_inv.id, true, t_inv.trans_id
         FROM parts
       WHERE  id = t_inv.parts_id AND inventory_accno_id IS NOT NULL
              AND expense_accno_id IS NOT NULL
       UNION
       SELECT income_accno_id,
              CASE WHEN t_ar.transdate > t_cp.end_date THEN t_ar.transdate
                   ELSE t_cp.end_date + '1 day'::interval
               END, -t_avail * in_lastcost, t_inv.id, true, t_inv.trans_id
         FROM parts
       WHERE  id = t_inv.parts_id AND inventory_accno_id IS NOT NULL
              AND expense_accno_id IS NOT NULL;
       t_alloc := t_alloc + t_avail;
       t_cogs := t_cogs + t_avail * in_lastcost;
   END IF;


END LOOP;

RETURN t_alloc;
END;

Function: lsmb14.cogs__add_for_ap_line(in_invoice_id integer)

Returns: numeric

Language: PLPGSQL

DECLARE retval numeric;
        r_cogs numeric[];
        t_inv invoice;
        t_adj numeric;
        t_ap  ap;
BEGIN

SELECT * INTO t_inv FROM invoice 
 WHERE id = in_invoice_id;

IF t_inv.qty + t_inv.allocated = 0 THEN
   return 0;
END IF;

SELECT * INTO t_ap FROM ap WHERE id = t_inv.trans_id;

IF t_inv.qty < 0 THEN -- normal COGS

    SELECT cogs__add_for_ap(i.parts_id, i.qty + i.allocated, i.sellprice) 
      INTO retval
      FROM invoice i
      JOIN parts p ON p.id = i.parts_id
     WHERE i.id = $1;

    UPDATE invoice 
       SET allocated = allocated + retval
     WHERE id = $1;
ELSE -- reversal

   r_cogs := cogs__reverse_ap(t_inv.parts_id, t_inv.qty + t_inv.allocated);

   UPDATE invoice
      SET allocated = allocated + r_cogs[1]
    WHERE id = in_invoice_id;

   t_adj := t_inv.sellprice * r_cogs[1] + r_cogs[2];

   INSERT INTO acc_trans 
          (chart_id, trans_id, approved,  amount, transdate, invoice_id)
   SELECT p.inventory_accno_id, t_inv.trans_id, true, t_adj, t_ap.transdate, 
          in_invoice_id
     FROM parts p
    WHERE id = t_inv.parts_id
    UNION
   SELECT p.expense_accno_id, t_inv.trans_id, true, t_adj * -1, t_ap.transdate,
          in_invoice_id
     FROM parts p
    WHERE id = t_inv.parts_id;
   retval := r_cogs[1];
   raise notice 'cogs reversal returned %', r_cogs;

END IF;

RETURN retval;

END;


Function: lsmb14.cogs__add_for_ar(in_qty integer, in_parts_id numeric)

Returns: numeric[]

Language: PLPGSQL

This function accepts a parts_id and a quantity, and iterates through AP records in order, calculating COGS on a FIFO basis and returning it to the application to attach to the current transaction. Return values are an array of {allocated, cogs}.

DECLARE t_alloc numeric := 0;
        t_cogs numeric := 0;
        t_inv invoice;
        t_avail numeric;
BEGIN


FOR t_inv IN
    SELECT i.*
      FROM invoice i
      JOIN (select id, approved, transdate from ap
             union
            select id, approved, transdate from gl) a ON a.id = i.trans_id
     WHERE qty + allocated < 0 AND i.parts_id = in_parts_id
  ORDER BY a.transdate asc, a.id asc, i.id asc
LOOP
   t_avail := (t_inv.qty + t_inv.allocated) * -1;
   IF t_alloc > in_qty THEN
       RAISE EXCEPTION 'TOO MANY ALLOCATED';
   ELSIF t_alloc = in_qty THEN
       return ARRAY[t_alloc, t_cogs];
   ELSIF (in_qty + t_alloc) <= t_avail THEN
       UPDATE invoice SET allocated = allocated + (in_qty - t_alloc)
        WHERE id = t_inv.id;
       t_cogs := t_cogs + (in_qty - t_alloc) * t_inv.sellprice;
       t_alloc := in_qty;
       return ARRAY[t_alloc, t_cogs];
   ELSE
       UPDATE invoice SET allocated = qty * -1
        WHERE id = t_inv.id;
       t_cogs := t_cogs + (t_avail * t_inv.sellprice);
       t_alloc := t_alloc + t_avail;
   END IF;
END LOOP;

RETURN ARRAY[t_alloc, t_cogs];

END;

Function: lsmb14.cogs__add_for_ar_line(in_invoice_id integer)

Returns: numeric

Language: PLPGSQL

DECLARE 
   t_cogs numeric[];
   t_inv invoice;
   t_part parts;
   t_ar ar;
   t_transdate date;
BEGIN

SELECT * INTO t_inv FROM invoice WHERE id = in_invoice_id;
SELECT * INTO t_part FROM parts WHERE id = t_inv.parts_id;
SELECT * INTO t_ar FROM ar WHERE id = t_inv.trans_id;

IF t_inv.qty + t_inv.allocated = 0 THEN
   return 0;
END IF;

IF t_inv.qty > 0 THEN 
   t_cogs := cogs__add_for_ar(t_inv.parts_id, t_inv.qty + t_inv.allocated);
ELSE
   t_cogs := cogs__reverse_ar(t_inv.parts_id, t_inv.qty + t_inv.allocated);
END IF;


UPDATE invoice set allocated = allocated - t_cogs[1]
 WHERE id = in_invoice_id;

SELECT CASE WHEN t_ar.transdate > max(end_date) THEN t_ar.transdate
            ELSE max(end_date) + '1 day'::interval
        END INTO t_transdate
  from account_checkpoint td; 
INSERT INTO acc_trans 
       (trans_id, chart_id, approved, amount, transdate,  invoice_id)
VALUES (t_inv.trans_id, CASE WHEN t_inv.qty < 0 AND t_ar.is_return 
                           THEN t_part.returns_accno_id
                           ELSE t_part.expense_accno_id
                      END, TRUE, t_cogs[2] * -1, t_transdate, t_inv.id),
       (t_inv.trans_id, t_part.inventory_accno_id, TRUE, t_cogs[2], 
       t_transdate, t_inv.id);

RETURN t_cogs[1];

END;


Function: lsmb14.cogs__reverse_ap(in_qty integer, in_parts_id numeric)

Returns: numeric[]

Language: PLPGSQL

This function iterates through invoice rows attached to ap transactions and allocates them on a first-in first-out basis. The sort of pseudo-"COGS" value is returned to the application for further handling.

DECLARE t_alloc numeric :=0;
        t_inv invoice;
        t_cogs numeric :=0;
        retval numeric[];
BEGIN
RAISE NOTICE 'reversing AP: parts_id %, qty %', in_parts_id, in_qty;

FOR t_inv IN
    SELECT i.*
      FROM invoice i
      JOIN ap a ON a.id = i.trans_id
     WHERE qty + allocated < 0 AND parts_id = in_parts_id
  ORDER BY a.transdate, a.id, i.id
LOOP
   RAISE NOTICE 'id %, avail %, allocated %, requesting %', t_inv.id, t_inv.qty + t_inv.allocated, t_alloc, in_qty - t_alloc;
   IF t_alloc > in_qty THEN
       RAISE EXCEPTION 'TOO MANY ALLOCATED';
   ELSIF t_alloc = in_qty THEN
       return ARRAY[t_alloc, t_cogs];
   ELSIF (in_qty - t_alloc) <= -1 * (t_inv.qty + t_inv.allocated) THEN
       raise notice 'partial reversal';
       UPDATE invoice SET allocated = allocated + (in_qty - t_alloc)
        WHERE id = t_inv.id;
       return ARRAY[in_qty * -1, t_cogs + (in_qty - t_alloc) * t_inv.sellprice];
   ELSE
       raise notice 'total reversal';
       UPDATE invoice SET allocated = qty * -1
        WHERE id = t_inv.id;
       t_alloc := t_alloc - (t_inv.qty + t_inv.allocated);
       t_cogs := t_cogs - (t_inv.qty + t_inv.allocated) * t_inv.sellprice;
   END IF;
END LOOP;

RETURN ARRAY[t_alloc, t_cogs];

RAISE EXCEPTION 'TOO FEW TO ALLOCATE';
END;

Function: lsmb14.cogs__reverse_ar(in_qty integer, in_parts_id numeric)

Returns: numeric[]

Language: PLPGSQL

This function accepts a part id and quantity to reverse. It then iterates backwards over AP related records, calculating COGS. This does not save COGS but rather returns it to the application to save. Return values are an array of {allocated, cogs}.

DECLARE t_alloc numeric := 0;
        t_cogs numeric := 0;
        t_inv invoice;
        t_avail numeric;
BEGIN

FOR t_inv IN
    SELECT i.*
      FROM invoice i
      JOIN (select id, approved, transdate from ap 
            union
            select id, approved, transdate from gl) a ON a.id = i.trans_id
     WHERE allocated > 0 and a.approved and parts_id = in_parts_id
  ORDER BY a.transdate DESC, a.id DESC, i.id DESC
LOOP
   t_avail := t_inv.allocated;
   IF t_alloc < in_qty THEN
       RAISE EXCEPTION 'TOO MANY ALLOCATED';
   ELSIF t_alloc = in_qty THEN
       RETURN ARRAY[t_alloc, t_cogs];
   ELSIF (in_qty - t_alloc) * -1 <=  t_inv.allocated THEN
       raise notice 'partial reversal';
       UPDATE invoice SET allocated = allocated + (in_qty - t_alloc)
        WHERE id = t_inv.id;
       t_cogs := t_cogs +  (in_qty - t_alloc) * t_inv.sellprice;
       return ARRAY[t_alloc + (in_qty - t_alloc), t_cogs];
   ELSE
       raise notice 'full reversal';
       UPDATE invoice SET allocated = 0
        WHERE id = t_inv.id;
       t_alloc := t_alloc + t_inv.allocated * -1;
       t_cogs := t_cogs + -1 * (t_inv.allocated) * t_inv.sellprice;
   END IF;
END LOOP;

RAISE EXCEPTION 'TOO FEW TO REVERSE';

END;

Function: lsmb14.company__get(in_entity_id integer)

Returns: company_entity

Language: SQL

Returns all attributes for the company attached to the entity.

	SELECT c.entity_id, e.entity_class, c.legal_name, c.tax_id, c.sales_tax_id,
               c.license_number, c.sic_code, e.control_code, e.country_id 
          FROM company c
          JOIN entity e ON e.id = c.entity_id
         WHERE entity_id = $1;

Function: lsmb14.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: lsmb14.company__get_by_cc(in_control_code text)

Returns: company_entity

Language: SQL

Returns the entity/company row attached to the control code.

        SELECT c.entity_id, e.entity_class, c.legal_name, c.tax_id, c.sales_tax_id,
               c.license_number, c.sic_code, e.control_code, e.country_id
          FROM company c
          JOIN entity e ON e.id = c.entity_id
         WHERE e.control_code = $1;

Function: lsmb14.company__next_id()

Returns: bigint

Language: SQL

    
    select nextval('company_id_seq');
    

Function: lsmb14.company__save(in_license_number integer, in_sales_tax_id text, in_country_id integer, in_sic_code text, in_entity_id text, in_tax_id integer, in_legal_name text, in_entity_class integer, in_control_code text, in_id text)

Returns: company

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;
        t_retval COMPANY;
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_legal_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_legal_name, in_entity_class, t_control_code,in_country_id);
		t_entity_id := currval('entity_id_seq');
	END IF;

	UPDATE company
	SET legal_name = in_legal_name,
		tax_id = in_tax_id,
		sic_code = in_sic_code,
                sales_tax_id = in_sales_tax_id,
                license_number = in_license_number
	WHERE id = t_company_id;


	IF NOT FOUND THEN
		INSERT INTO company(entity_id, legal_name, tax_id, sic_code,
                                    sales_tax_id, license_number)
		VALUES (t_entity_id, in_legal_name, in_tax_id, in_sic_code, 
                        in_sales_tax_id, in_license_number);

	END IF;
        SELECT * INTO t_retval FROM company WHERE entity_id = t_entity_id;
        RETURN t_retval;	
END;

Function: lsmb14.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 (select legal_name, tax_id, entity_id 
                FROM company
               UNION ALL
              SELECT last_name || ', ' || first_name, null, entity_id 
                FROM person) 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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.contact__search(in_notes integer, in_control_code text, in_name_part text[], in_business_id text, in_active_date_to text, in_active_date_from text, in_country text, in_mail_code text, in_state text, in_city date, in_address date, in_meta_number integer, in_contact_info text, in_contact text, in_entity_class text)

Returns: SET OF contact_search_result

Language: PLPGSQL

DECLARE
	out_row contact_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, 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 control_code like in_control_code || '%'
                      union
                      select * from entity where in_control_code is null) e
		JOIN (SELECT legal_name, sic_code, entity_id 
                        FROM company 
                       WHERE legal_name @@ plainto_tsquery(in_name_part)
                      UNION ALL
                      SELECT legal_name, sic_code, entity_id
                        FROM company
                       WHERE in_name_part IS NULL
                      UNION ALL
                     SELECT coalesce(first_name, '') || ' ' 
                            || coalesce(middle_name, '') || ' ' 
                            || coalesce(last_name, ''), null, entity_id
                       FROM person
                      WHERE coalesce(first_name, '') || ' ' 
                            || coalesce(middle_name, '') || ' '
                            || coalesce(last_name, '') 
                             @@ plainto_tsquery(in_name_part)
                      UNION ALL
                     SELECT coalesce(first_name, '') || ' ' 
                            || coalesce(middle_name, '') || ' ' 
                            || coalesce(last_name, ''), null, entity_id
                       FROM person
                       WHERE in_name_part IS NULL) c ON (e.id = c.entity_id)
		LEFT JOIN entity_credit_account ec ON (ec.entity_id = e.id)
		LEFT JOIN business b ON (ec.business_id = b.id)
		WHERE coalesce(ec.entity_class,e.entity_class) = in_entity_class
			AND (c.entity_id IN (select entity_id 
                                               FROM entity_to_contact
                                              WHERE contact ILIKE 
                                                            ANY(t_contact_info))
				                    OR '' ILIKE 
                                                          ALL(t_contact_info)
                                                    OR t_contact_info IS NULL)
			
			AND ((in_address IS NULL AND in_city IS NULL 
					AND in_state IS NULL 
					AND in_country IS NULL)
				OR (c.entity_id IN 
				(select entity_id FROM entity_to_location
				WHERE location_id IN 
					(SELECT id FROM location
					WHERE (line_one @@ plainto_tsquery(
                                                              in_address)
                                               OR
					       line_two @@ plainto_tsquery(
                                                              in_address)
                                               OR
					       line_three @@ plainto_tsquery(
                                                              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_active_date_to, 
						ec.startdate)
				OR (ec.startdate IS NULL))
			AND (ec.enddate >= coalesce(in_active_date_from, ec.enddate)
				OR (ec.enddate IS NULL))
	 		AND (ec.meta_number like in_meta_number || '%'
			     OR in_meta_number IS NULL)
                        AND (in_notes IS NULL OR e.id in (
                                     SELECT entity_id from entity_note
                                      WHERE note @@ plainto_tsquery(in_notes))
                                  OR ec.id IN (select ref_key FROM eca_note
                                     WHERE note @@ plainto_tsquery(in_notes)))
	LOOP
		RETURN NEXT out_row;
	END LOOP;
END;

Function: lsmb14.contact_class__list()

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: lsmb14.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: lsmb14.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: lsmb14.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 = 2 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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.del_yearend()

Returns: trigger

Language: PLPGSQL

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

Function: lsmb14.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.invoice, 
                       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, false as invoice,
                                approved from gl
			WHERE lower(in_type) = 'gl'
			UNION
			SELECT id, transdate, invnumber as reference, 
				(SELECT name FROM eca__get_entity(entity_credit_account)),
				invoice, approved from ap
			WHERE lower(in_type) = 'ap'
			UNION
			SELECT id, transdate, invnumber as reference,
				description, 
				invoice, 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 and charttype = 'A')
           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, trans.invoice
		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: lsmb14.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
                PERFORM cogs__add_for_ar_line(id) FROM invoice 
                  WHERE trans_id = in_id;
		UPDATE ar set approved = true where id = in_id;
	ELSIF (t_table = 'ap') THEN
                PERFORM cogs__add_for_ap_line(id) FROM invoice 
                  WHERE trans_id = in_id;
		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: lsmb14.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: lsmb14.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: lsmb14.eca__delete_contact(in_contact integer, in_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_class_id
       and contact= in_contact;
RETURN FOUND;

END;


Function: lsmb14.eca__delete_location(in_location_class integer, in_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_id 
       AND location_class = in_location_class;

RETURN FOUND;

END;

Function: lsmb14.eca__delete_pricematrix(in_entry_id integer, in_credit_id integer)

Returns: boolean

Language: PLPGSQL

DECLARE retval bool;

BEGIN

retval := false;

DELETE FROM partsvendor 
 WHERE entry_id = in_entry_id 
       AND credit_id = in_credit_id;

retval := FOUND;

DELETE FROM partscustomer
 WHERE entry_id = in_entry_id
       AND credit_id = in_credit_id;

RETURN FOUND or retval;

END;

Function: lsmb14.eca__get_by_meta_number(in_entity_class text, in_meta_number integer)

Returns: entity_credit_account

Language: SQL

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

Function: lsmb14.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: lsmb14.eca__get_pricematrix(in_credit_id integer)

Returns: SET OF eca__pricematrix

Language: SQL

This returns the pricematrix for the customer or vendor (entity_credit_account identified by in_id), orderd by partnumber, validfrom


SELECT pc.parts_id, p.partnumber, p.description, pc.credit_id, pc.pricebreak,
       pc.sellprice, NULL, NULL::int, NULL, pc.validfrom, pc.validto, pc.curr,
       pc.entry_id
  FROM partscustomer pc
  JOIN parts p on pc.parts_id = p.id
  JOIN entity_credit_account eca ON pc.credit_id = eca.id
 WHERE pc.credit_id = $1 AND eca.entity_class = 2
 UNION
SELECT pv.parts_id, p.partnumber, p.description, pv.credit_id, NULL, NULL,
       pv.lastcost, pv.leadtime::int, pv.partnumber, NULL, NULL, pv.curr, 
       pv.entry_id
  FROM partsvendor pv
  JOIN parts p on pv.parts_id = p.id
  JOIN entity_credit_account eca ON pv.credit_id = eca.id
 WHERE pv.credit_id = $1 and eca.entity_class = 1
 ORDER BY partnumber, validfrom


Function: lsmb14.eca__get_pricematrix_by_pricegroup(in_credit_id integer)

Returns: SET OF eca__pricematrix

Language: SQL

SELECT pc.parts_id, p.partnumber, p.description, pc.credit_id, pc.pricebreak,
       pc.sellprice, NULL::numeric, NULL::int, NULL::text, pc.validfrom, 
       pc.validto, pc.curr, pc.entry_id
  FROM partscustomer pc
  JOIN parts p on pc.parts_id = p.id
  JOIN entity_credit_account eca ON pc.pricegroup_id = eca.pricegroup_id
 WHERE eca.id = $1 AND eca.entity_class = 2

Function: lsmb14.eca__get_taxes(in_id integer)

Returns: SET OF eca_tax

Language: SQL

Returns a set of taxable account id's.

select * from eca_tax where eca_id = $1;

Function: lsmb14.eca__history(in_inc_closed text, in_inc_open text, in_entity_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, null::int as project_id, null::text as projectnumber,
            i.serialnumber, 
            case when $16 = 1 then ex.buy else ex.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, notes
             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, notes
             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, notes
           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, notes
           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, notes
           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, notes
           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 id, trans_id, parts_id, qty, description, unit, discount,
                   deliverydate, serialnumber, sellprice
             FROM  invoice where $13 = 'i'
            union 
            select id, trans_id, parts_id, qty, description, unit, discount,
                   reqdate, serialnumber, 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 entity ee ON (a.person_id = ee.id)
LEFT JOIN person ep ON (ep.entity_id = ee.id)
    -- 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)
--          and (a.notes @@ plainto_tsquery($9) or $9 is null)
 ORDER BY eca.meta_number;

Function: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.eca__location_save(in_old_location_class integer, 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_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_id;
           
         IF FOUND THEN
            SELECT location_save(
                in_id, 
                in_line_one, 
                in_line_two, 
                in_line_three, 
                in_city,
                in_state, 
                in_mail_code, 
                in_country_id
            )
        	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_id
            )
        	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: lsmb14.eca__save(in_discount_account_id integer, in_taxform_id integer, in_pay_to_name integer, in_cash_account_id text, in_ar_ap_account_id numeric, in_threshold boolean, in_enddate numeric, in_startdate integer, in_curr integer, in_pricegroup_id character varying, in_language_code integer, in_business_id character varying, in_meta_number integer, in_terms bpchar, in_discount_terms date, in_creditlimit date, in_taxincluded numeric, in_discount integer, in_description integer, in_entity_id text, in_entity_class integer, in_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,
                discount_account_id = in_discount_account_id,
                meta_number = t_meta_number,
                business_id = in_business_id,
                language_code = in_language_code,
                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_id;
        
         IF FOUND THEN
            RETURN in_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,
                discount_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_code,
                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,
                in_discount_account_id
            );
            RETURN currval('entity_credit_account_id_seq');
       END IF;

    END;
    

Function: lsmb14.eca__save_contact(in_old_contact_class integer, in_old_contact integer, in_contact text, in_description text, in_class_id 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_class_id
         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_class_id, in_description, in_contact);
        
    END IF;

	RETURN 1;
END;

Function: lsmb14.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: lsmb14.eca__save_pricematrix(in_entry_id integer, in_curr integer, in_validto numeric, in_validfrom numeric, in_partnumber smallint, in_lead_time text, in_price date, in_pricebreak date, in_credit_id bpchar, in_parts_id integer)

Returns: eca__pricematrix

Language: PLPGSQL

DECLARE 
   retval eca__pricematrix;
   t_insert bool;

BEGIN

t_insert := false;

PERFORM * FROM entity_credit_account 
  WHERE id = in_credit_id AND entity_class = 1;

IF FOUND THEN -- VENDOR
    UPDATE partsvendor
       SET lastcost = in_price,
           leadtime = in_lead_time,
           partnumber = in_partnumber,
           curr = in_curr
     WHERE credit_id = in_credit_id AND entry_id = in_entry_id;

    IF NOT FOUND THEN
        INSERT INTO partsvendor
               (parts_id, credit_id, lastcost, leadtime, partnumber, curr)
        VALUES (in_parts_id, in_credit_id, in_price, in_leadtime::int2, 
               in_partnumber, in_curr);
    END IF;

    SELECT pv.parts_id, p.partnumber, p.description, pv.credit_id, NULL, NULL,
           pv.lastcost, pv.leadtime::int, pv.partnumber, NULL, NULL, pv.curr, 
           pv.entry_id
      INTO retval
      FROM partsvendor pv
      JOIN parts p ON p.id = pv.parts_id
     WHERE parts_id = in_parts_id and credit_id = in_credit_id;

    RETURN retval;
END IF;

PERFORM * FROM entity_credit_account
  WHERE id = in_credit_id AND entity_class = 2;

IF FOUND THEN -- CUSTOMER
    UPDATE partscustomer
       SET pricebreak = in_pricebreak,
           sellprice  = in_price,
           validfrom  = in_validfrom,
           validto    = in_validto,
           curr       = in_curr
     WHERE entry_id = in_entry_id and credit_id = in_credit_id;

    IF NOT FOUND THEN
        INSERT INTO partscustomer
               (parts_id, credit_id, sellprice, validfrom, validto, curr)
        VALUES (in_parts_id, in_credit_id, in_price, in_validfrom, in_validto, 
                in_curr);

        t_insert := true;
    END IF;

    SELECT pc.parts_id, p.partnumber, p.description, pc.credit_id, 
           pc.pricebreak, pc.sellprice, NULL, NULL, NULL, pc.validfrom, 
           pc.validto, pc.curr, pc.entry_id
      INTO retval
      FROM partscustomer pc
      JOIN parts p on pc.parts_id = p.id
     WHERE entry_id = CASE WHEN t_insert 
                           THEN currval('partscustomer_entry_id_seq') 
                           ELSE in_entry_id 
                      END;
                           
    RETURN retval;

END IF;

RAISE EXCEPTION 'No valid entity credit account found';
   
END;

Function: lsmb14.eca__set_taxes(in_tax_ids integer, in_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
     IF in_tax_ids = '{}' THEN
         RETURN NULL;
     END IF;
     SELECT * FROM entity_credit_account into eca WHERE id = in_id;

     DELETE FROM eca_tax WHERE eca_id = in_id;
     FOR iter in array_lower(in_tax_ids, 1) .. array_upper(in_tax_ids, 1)
     LOOP
          INSERT INTO eca_tax (eca_id, chart_id)
          values (in_id, in_tax_ids[iter]);
     END LOOP;
     RETURN TRUE;
end;

Function: lsmb14.eca_bu_trigger()

Returns: trigger

Language: PLPGSQL

BEGIN
  IF TG_OP = 'INSERT' THEN
      INSERT INTO business_unit(class_id, description, credit_id)
      VALUES (7 - NEW.entity_class, NEW.meta_number, NEW.id);
  ELSIF TG_OP = 'UPDATE' THEN
      IF new.meta_number <> old.meta_number THEN
         UPDATE business_unit SET description = new.meta_number
          WHERE class_id = 7 - NEW.entity_class
                AND credit_id = new.id;
      END IF;
  ELSIF TG_OP = 'DELETE'THEN
      DELETE FROM business_unit WHERE class_id = 7 - NEW.entity_class
                  AND credit_id = old_id;
      RETURN OLD;
  END IF;
  RETURN NEW;
END;

Function: lsmb14.employee__all_managers()

Returns: SET OF employee_result

Language: SQL

   SELECT p.entity_id, e.control_code, p.id, s.salutation, s.id,
          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, e.country_id
     FROM person p
     JOIN entity_employee ee on (ee.entity_id = p.entity_id)
     JOIN entity e ON (p.entity_id = e.id)
LEFT JOIN salutation s on (p.salutation_id = s.id)
LEFT JOIN person mp ON ee.manager_id = p.entity_id
    WHERE ee.role = 'manager'
 ORDER BY ee.employeenumber;

Function: lsmb14.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, e.control_code, p.id, s.salutation, s.id,
          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, e.country_id
     FROM person p
     JOIN entity_employee ee on (ee.entity_id = p.entity_id)
     JOIN entity e ON (p.entity_id = e.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: lsmb14.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: lsmb14.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: lsmb14.employee__save(in_employeenumber 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_employeenumber
	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_employeenumber, 
                                in_entity_id);
		RETURN in_entity_id;
	END IF;
        RETURN out_id;
END;

Function: lsmb14.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, e.control_code, p.id, s.salutation, s.id, 
          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, e.country_id
     FROM person p
     JOIN entity e ON p.entity_id = e.id
     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: lsmb14.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: lsmb14.employee_set_location(in_location integer, in_employee integer)

Returns: void

Language: SQL


    INSERT INTO entity_to_location (entity_id,location_id) 
    SELECT entity_id, $2
      FROM person WHERE id = $1;

Function: lsmb14.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: lsmb14.entity__delete_contact(in_contact integer, in_class_id integer, in_entity_id text)

Returns: boolean

Language: PLPGSQL

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

BEGIN

DELETE FROM entity_to_contact
 WHERE entity_id = in_entity_id
       and contact_class_id = in_class_id
       and contact= in_contact;
RETURN FOUND;

END;


Function: lsmb14.entity__delete_location(in_location_class integer, in_id integer, in_entity_id integer)

Returns: boolean

Language: PLPGSQL

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

BEGIN

DELETE FROM entity_to_location
 WHERE entity_id = in_entity_id AND location_id = in_id 
       AND location_class = in_location_class;

RETURN FOUND;

END;

Function: lsmb14.entity__get(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: lsmb14.entity__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: lsmb14.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
             LEFT JOIN defaults ON setting_key = 'roll_prefix'
		WHERE active and pg_has_role(SESSION_USER, 
                                     coalesce(defaults.value, 
                                     'lsmb_' || current_database() || '__') ||
                                     'contact_class_' ||
                                     lower(regexp_replace(class, ' ', '_')), 
                                     'USAGE')
		ORDER BY id
	LOOP
		RETURN NEXT out_row;
	END LOOP;
END;

Function: lsmb14.entity__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 entity_to_contact c
		JOIN contact_class cl ON (c.contact_class_id = cl.id)
		WHERE c.entity_id = in_entity_id
	LOOP
		return next out_row;
	END LOOP;
END;

Function: lsmb14.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  ec.id, e.id, ec.entity_class, ec.discount, 
                        ec.discount_terms,
			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.discount_account_id,
			ec.threshold, e.control_code, ec.id, ec.pay_to_name,
                        ec.taxform_id
		FROM entity e 
		JOIN entity_credit_account ec ON (e.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: lsmb14.entity__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 entity_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.entity_id = in_entity_id
		ORDER BY lc.id, l.id, c.name
	LOOP
		RETURN NEXT out_row;
	END LOOP;
END;

Function: lsmb14.entity__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: lsmb14.entity__location_save(in_created integer, in_country_id integer, in_mail_code integer, in_state text, in_city text, in_line_two text, in_line_one text, in_location_class text, in_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_id,
        in_location_class, in_line_one, in_line_two, 
        '', in_city , in_state, in_mail_code, in_country_id);
    END;


Function: lsmb14.entity__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: lsmb14.entity__save_contact(in_old_class_id integer, in_old_contact integer, in_contact text, in_description text, in_class_id text, in_entity_id integer)

Returns: integer

Language: PLPGSQL

Saves company contact information. The return value is meaningless.

DECLARE out_id int;
BEGIN
        DELETE FROM entity_to_contact 
         WHERE entity_id = in_entity_id AND contact = in_old_contact 
               AND contact_class_id = in_old_class_id;

	INSERT INTO entity_to_contact 
               (entity_id, contact_class_id, description, contact)
	VALUES (in_entity_id, in_class_id, in_description, in_contact);

	RETURN 1;
END;

Function: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.eoy_reopen_books(in_end_date date)

Returns: boolean

Language: PLPGSQL

Removes checkpoints and reverses yearend transactions 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: lsmb14.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')
                      OR acc.category = 'Q' AND acc.is_temp)
	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: lsmb14.file__attach_to_eca(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 entity credit accounts.

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_eca
                   (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: lsmb14.file__attach_to_entity(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 contact or entity. 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 entities

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_entity
                   (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: lsmb14.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_order
                   (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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.file__get_for_template(in_file_class integer, in_ref_key integer)

Returns: SET OF file_list_item

Language: SQL

 

SELECT m.mime_type, CASE WHEN f.file_class = 3 THEN ref_key ||'-'|| f.file_name
                         ELSE f.file_name END, 
       f.description, f.uploaded_by, e.name, 
       f.uploaded_at, f.id, f.ref_key, f.file_class,  f.content
  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
       AND m.invoice_include 
       OR f.id IN (SELECT max(fb.id) 
                   FROM file_base fb
                   JOIN mime_type m ON fb.mime_type_id = m.id
                        AND m.mime_type ilike 'image%'
                   JOIN invoice i ON i.trans_id = $1
                        AND i.parts_id = fb.ref_key
                  WHERE fb.file_class = 3)

Function: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.get_default_lang()

Returns: text

Language: SQL

 SELECT coalesce((select description FROM language 
    WHERE code = (SELECT substring(value, 1, 2) FROM defaults
                   WHERE setting_key = 'default_language')), 'english');

Function: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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,tablename,reference, action, person_id)
values (t_row.id,TG_RELNAME,t_reference, TG_OP, person__get_my_entity_id());

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

Function: lsmb14.in_tree(in_search_array integer, in_node_id lsmb14.tree_record[])

Returns: boolean

Language: SQL

SELECT CASE WHEN count(*) > 0 THEN true ELSE false END
  FROM unnest($2) r
 WHERE t @> array[$1];

Function: lsmb14.in_tree(in_search_array integer[], in_node_id lsmb14.tree_record[])

Returns: boolean

Language: SQL

SELECT bool_and(in_tree(e, $2))
  FROM unnest($1) e;

Function: lsmb14.inventory_adj__details(in_id integer)

Returns: SET OF inventory_adjustment_line

Language: SQL

 

   SELECT l.parts_id, p.partnumber, p.description, l.counted, l.expected, 
          l.counted - l.expected
     FROM inventory_report_line l
     JOIN parts p ON l.parts_id = p.id
    WHERE l.report_id = $1;


Function: lsmb14.inventory_adj__get(in_id integer)

Returns: SET OF inventory_adjustment_info

Language: SQL


   SELECT r.id, r.transdate, r.source, r.ar_trans_id, r.ap_trans_id,
          ar.invnumber, ap.invnumber
     FROM inventory_report r
     JOIN inventory_report_line l ON l.report_id = r.id 
LEFT JOIN ar ON ar.id = r.ar_trans_id
LEFT JOIN ap ON ap.id = r.ap_trans_id
    WHERE r.id = $1;


Function: lsmb14.inventory_adj__search(in_source date, in_partnumber date, in_to_date text, in_from_date text)

Returns: SET OF inventory_adjustment_info

Language: SQL


   SELECT r.id, r.transdate, r.source, r.ar_trans_id, r.ap_trans_id,
          ar.invnumber, ap.invnumber
     FROM inventory_report r
     JOIN inventory_report_line l ON l.report_id = r.id 
     JOIN parts p ON l.parts_id = p.id
LEFT JOIN ar ON ar.id = r.ar_trans_id
LEFT JOIN ap ON ap.id = r.ap_trans_id
    WHERE ($1 is null or $1 <= r.transdate) AND
          ($2 is null OR $2 >= r.transdate) AND
          ($3 IS NULL OR plainto_tsquery($3) @@ tsvector(p.partnumber)) AND
          ($4 IS NULL OR source LIKE $4 || '%');
 

Function: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.je_get_default_lines()

Returns: character varying

Language: SQL

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

Function: lsmb14.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: lsmb14.journal__add(in_is_template text, in_approved text, in_transaction_date integer, in_entry_type date, in_description boolean, in_source boolean)

Returns: journal_entry

Language: PLPGSQL

DECLARE retval journal_entry;
BEGIN
	INSERT INTO journal_entry (source, description, entry_type, transaction_date,
			approved, is_template)
	VALUES (in_source, in_description, in_entry_type, in_transaction_date,
			coalesce(in_approved, false), 
			coalesce(in_is_template, false));

	SELECT * INTO retval FROM journal_entry WHERE id = currval('journal_id_seq');
	RETURN retval;
END;

Function: lsmb14.journal__add_line(in_business_units integer, in_memo integer, in_cleared numeric, in_amount boolean, in_journal_id text, in_account_id integer[])

Returns: journal_line

Language: PLPGSQL

DECLARE retval journal_line;
BEGIN
	INSERT INTO journal_line(account_id, journal_id, amount, cleared, memo)
	VALUES (in_account_id, in_journal_id, in_amount, 
		coalesce(in_cleared, false), in_memo);

        INSERT INTO business_unit_jl(entry_id, bu_class, bu_id)
        SELECT currval('journal_line_line_id_seq'), business_unit_class, bu
          FROM business_unit
         WHERE id = any(in_business_units);

	SELECT * INTO retval FROM journal_line where line_id = currval('journal_line_line_id_seq');
	return retval;
END;

Function: lsmb14.journal__get_entry(in_id integer)

Returns: journal_entry

Language: SQL

SELECT * FROM journal_entry where id = $1;

Function: lsmb14.journal__get_invoice(in_id integer)

Returns: eca_invoice

Language: SQL

SELECT * FROM eca_invoice where journal_id = $1;

Function: lsmb14.journal__lines(in_id integer)

Returns: SET OF journal_line

Language: SQL

select * from journal_line where journal_id = $1;

Function: lsmb14.journal__make_invoice(in_language_code integer, in_credit_id integer, in_reverse boolean, in_on_hold boolean, in_journal_id integer, in_order_id character varying)

Returns: eca_invoice

Language: PLPGSQL

DECLARE retval eca_invoice;
BEGIN	
	INSERT INTO eca_invoice (order_id, journal_id, on_hold, reverse,
		credit_id, language_code)
	VALUES (in_order_id, in_journal_id, coalesce(in_on_hold, false), 
		in_reverse, in_credit_id, in_language_code);

	SELECT * INTO retval FROM eca_invoice WHERE journal_id = in_journal_id;

	RETURN retval;
END;

Function: lsmb14.journal__search(in_entity_class text, in_meta_number text, in_is_template integer, in_department_id date, in_approved boolean, in_transaction_date integer, in_entry_type boolean, in_description text, in_source integer)

Returns: SET OF journal_search_result

Language: PLPGSQL

DECLARE retval journal_search_result;
BEGIN
	FOR retval IN 
		SELECT j.id, j.source, j.description, j.entry_type, 
			j.transaction_date, j.approved, 
			j.is_template, eca.meta_number, 
			e.name, ec.class
		FROM journal_entry j
		LEFT JOIN eca_invoice i ON (i.journal_id = j.id)
		LEFT JOIN entity_credit_account eca ON (eca.id = credit_id)
		LEFT JOIN entity e ON (eca.entity_id = e.id)
		LEFT JOIN entity_class ec ON (eca.entity_class = ec.id)
		WHERE (in_source IS NULL OR in_source = j.source) AND
			(in_description IS NULL 
				or in_description = j.description) AND
			(in_entry_type is null or in_entry_type = j.entry_type)
			and (in_transaction_date is null 
				or in_transaction_date = j.transaction_date) and
			j.approved = coalesce(in_approved, true) and
			j.is_template = coalesce(in_is_template, false) and
			(in_department_id is null 
				or j.department_id = in_department_id) and
			(in_meta_number is null 
				or eca.meta_number = in_meta_number) and
			(in_entity_class is null
				or eca.entity_class = in_entity_class)
	LOOP
		RETURN NEXT retval;
	END LOOP;
END;

Function: lsmb14.journal__validate_entry(in_id integer)

Returns: boolean

Language: SQL

	SELECT sum(amount) = 0 FROM journal_line WHERE journal_id = $1;

Function: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.location__deactivate(in_id integer)

Returns: location

Language: SQL


UPDATE location set active = false, inactive_date = now()
 WHERE id = $1;

SELECT * FROM location WHERE id = 1;


Function: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.lsmb_module__get(in_id integer)

Returns: lsmb_module

Language: SQL

Retrieves a single module's info by id.

 SELECT * FROM lsmb_module where id = $1; 

Function: lsmb14.lsmb_module__list()

Returns: SET OF lsmb_module

Language: SQL

Returns a list of all defined modules, ordered by id.

 SELECT * FROM lsmb_module ORDER BY id 

Function: lsmb14.menu_children(in_parent_id integer)

Returns: SET OF menu_item

Language: SQL

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.

SELECT * FROM menu_generate() where parent = $1;

Function: lsmb14.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 
               WITH RECURSIVE tree (path, id, parent, level, positions)
                               AS (select id::text as path, id, parent, 
                                           0 as level, position::text
                                      FROM menu_node where parent is null
                                     UNION 
                                    select path || ',' || n.id::text, n.id, 
                                           n.parent,
                                           t.level + 1, 
                                           t.positions || ',' || n.position
                                      FROM menu_node n
                                      JOIN tree t ON t.id = n.parent) 
		SELECT n.position, n.id, c.level, n.label, c.path, n.parent,
                       to_args(array[ma.attribute, ma.value])
		FROM tree c
		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 acl
                          LEFT JOIN pg_roles pr on pr.rolname = acl.role_name
                               WHERE CASE WHEN role_name 
                                                           ilike 'public'
                                                      THEN true
                                                      WHEN rolname IS NULL
                                                      THEN FALSE
                                                      ELSE pg_has_role(rolname,
                                                                       'USAGE')
                                      END
                            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 tree cc
                                 JOIN menu_node cn USING(id)
                                WHERE cn.id IN 
                                      (select node_id 
                                         FROM menu_acl acl
                                    LEFT JOIN pg_roles pr 
                                              on pr.rolname = acl.role_name
                                        WHERE CASE WHEN rolname 
                                                           ilike 'public'
                                                      THEN true
                                                      WHEN rolname IS NULL
                                                      THEN FALSE
                                                      ELSE pg_has_role(rolname,
                                                                       'USAGE')
                                                END
                                     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::text 
                                           like c.path::text || ',%')
            GROUP BY n.position, n.id, c.level, n.label, c.path, c.positions,
                     n.parent
            ORDER BY string_to_array(c.positions, ',')::int[]
	LOOP
		RETURN NEXT item;
	END LOOP;
END;

Function: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.parse_date(in_date date)

Returns: date

Language: SQL

Simple way to cast a Perl string to a date format of known type.

 select $1; 

Function: lsmb14.parts__get_by_id(in_id integer)

Returns: parts

Language: SQL

SELECT * FROM parts WHERE id = $1;

Function: lsmb14.parts__get_by_partnumber(in_partnumber text)

Returns: parts

Language: SQL

SELECT * FROM PARTS WHERE partnumber = $1 and obsolete is not true; 

Function: lsmb14.parts__search_lite(in_description text, in_partnumber text)

Returns: SET OF parts

Language: SQL

SELECT * 
  FROM parts 
 WHERE ($1 IS NULL OR (partnumber like $1 || '%'))
       AND ($2 IS NULL 
            OR (description
                @@
                plainto_tsquery(get_default_lang()::regconfig, $2)))
       AND not obsolete
ORDER BY partnumber;

Function: lsmb14.payment__reverse(in_currency text, in_exchangerate date, in_voucher_id integer, in_batch_id text, in_account_class date, in_date_reversed integer, in_cash_accno integer, in_credit_id integer, in_date_paid numeric, in_source bpchar)

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;
        t_currs text[];
        t_rev_fx numeric;
        t_fxgain_id int;
        t_fxloss_id int;
        t_paid_fx numeric;
BEGIN
        SELECT * INTO t_rev_fx FROM currency_get_exchangerate(
              in_currency, in_date_reversed, in_account_class);

        SELECT * INTO t_paid_fx FROM currency_get_exchangerate(
              in_currency, in_date_paid, in_account_class);

       select value::int INTO t_fxgain_id FROM setting_get('fxgain_accno_id');
       select value::int INTO t_fxloss_id FROM setting_get('fxloss_accno_id');

       SELECT string_to_array(value, ':') into t_currs
          from defaults
         where setting_key = 'curr';

        IF in_currency IS NULL OR in_currency = t_currs[1] THEN
                t_rev_fx := 1;
                t_paid_fx := 1;
        ELSIF t_rev_fx IS NULL THEN
                t_rev_fx := in_exchangerate;
                PERFORM payments_set_exchangerate(in_account_class,
                                                  in_exchangerate,
                                                  in_currency,
                                                  in_date_reversed);
        ELSIF t_rev_fx <> in_exchangerate THEN
                RAISE EXCEPTION 'Exchange rate different than on file';
        END IF;
        IF t_rev_fx IS NULL THEN
            RAISE EXCEPTION 'No exchangerate provided and not default currency';
        END IF;


        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, arap.curr, arap.fxrate
		FROM acc_trans a
		JOIN (select id, curr, entity_credit_account, 
                             CASE WHEN curr = t_currs[1] THEN 1
                                   ELSE buy END as fxrate
			FROM ar 
                   LEFT JOIN exchangerate USING (transdate, curr)
                       WHERE in_account_class = 2
			UNION
			SELECT id, curr, entity_credit_account, 
                               CASE WHEN curr = t_currs[1] THEN 1
                                    ELSE sell END as fxrate
			FROM ap
                   LEFT JOIN exchangerate USING (transdate, curr)
                       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 account ch ON (a.chart_id = ch.id)
		WHERE a.source IS NOT DISTINCT FROM in_source
			AND a.transdate = in_date_paid
			AND in_credit_id = arap.entity_credit_account
			AND in_cash_accno = ch.accno
                        and in_voucher_id IS NOT DISTINCT FROM voucher_id
	LOOP
                IF pay_row.curr = t_currs[1] THEN
                   pay_row.fxrate = 1;
                END IF;

		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 / t_paid_fx * -1 * t_rev_fx, 
			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),
                 (pay_row.trans_id, pay_row.ar_ap_account_id, 
                        pay_row.amount / t_paid_fx * pay_row.fxrate,
			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),
                 (pay_row.trans_id, 
                  case when pay_row.fxrate > t_rev_fx 
                       THEN t_fxloss_id ELSE t_fxgain_id END, 
                  pay_row.amount / t_paid_fx * (t_rev_fx - pay_row.fxrate),
                  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);

                   
	END LOOP;
	RETURN 1;
END;

Function: lsmb14.payment__search(in_currency text, in_account_class date, in_cash_accno date, in_credit_id integer, in_date_to text, in_date_from integer, in_source bpchar)

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, curr
			FROM ar WHERE in_account_class = 2
			UNION
			SELECT entity_credit_account, id, curr
			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 (in_currency IS NULL OR in_currency = arap.curr)
			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: lsmb14.payment_bulk_post(in_currency 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

This posts the payments for large batch workflows. Note that in_transactions is a two-dimensional numeric array. Of each sub-array, the first element is the (integer) transaction id, and the second is the amount for that transaction.

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;
        t_cash_sign int;
BEGIN

        SELECT * INTO t_exchangerate FROM currency_get_exchangerate(
              in_currency, in_payment_date, in_account_class);

        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_currency IS NULL OR in_currency = t_currs[1]) THEN
                t_exchangerate := 1;
        ELSIF t_exchangerate IS NULL THEN
                t_exchangerate := in_exchangerate;
                PERFORM payments_set_exchangerate(in_account_class,
                                                  in_exchangerate, 
                                                  in_currency,
                                                  in_payment_date);
        ELSIF t_exchangerate <> in_exchangerate THEN
                RAISE EXCEPTION 'Exchange rate different than on file';
        END IF;
        IF t_exchangerate IS NULL THEN
            RAISE EXCEPTION 'No exchangerate provided and not default currency';
        END IF;

        CREATE TEMPORARY TABLE bulk_payments_in
           (id int, amount numeric, fxrate numeric, gain_loss_accno int);

        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
            -- Fill the bulk payments table
            INSERT INTO bulk_payments_in(id, amount)
            VALUES (in_transactions[out_count][1],
                    in_transactions[out_count][2]);
        END LOOP;

        IF in_account_class = 1 THEN
            t_cash_sign := 1;
        ELSE
            t_cash_sign := -1;
        END IF;

        IF (in_currency IS NULL OR in_currency = t_currs[1]) THEN
            UPDATE bulk_payments_in
               SET fxrate = 1;
        ELSE
            UPDATE bulk_payments_in
               SET fxrate =
                (SELECT CASE WHEN in_account_class = 1 THEN sell
                             ELSE buy
                        END
                   FROM exchangerate e
                   JOIN (SELECT transdate, id, curr FROM ar
                         UNION
                         SELECT transdate, id, curr FROM ap) a
                     ON (e.transdate = a.transdate
                         AND e.curr = a.curr)
                   WHERE a.id = bulk_payments_in.id);
            UPDATE bulk_payments_in
               SET gain_loss_accno =
                (SELECT value::int FROM defaults
                  WHERE setting_key = 'fxgain_accno_id')
             WHERE ((t_exchangerate - bulk_payments_in.fxrate) * t_cash_sign) < 0;
            UPDATE bulk_payments_in
               SET gain_loss_accno = (SELECT value::int FROM defaults
                  WHERE setting_key = 'fxloss_accno_id')
             WHERE ((t_exchangerate - bulk_payments_in.fxrate) * t_cash_sign) > 0;
            -- explicitly leave zero gain/loss accno_id entries at NULL
            -- so we have an easy check for which 
        END IF;

        -- Insert cash side
        INSERT INTO acc_trans
             (trans_id, chart_id, amount, approved,
              voucher_id, transdate, source)
           SELECT id, t_cash_id, amount * t_cash_sign * t_exchangerate/fxrate,
                  CASE WHEN t_voucher_id IS NULL THEN true
                       ELSE false END,
                  t_voucher_id, in_payment_date, in_source
             FROM bulk_payments_in  where amount <> 0;

        -- early payment discounts
        INSERT INTO acc_trans
               (trans_id, chart_id, amount, approved,
               voucher_id, transdate, source)
        SELECT bpi.id, eca.discount_account_id, 
               amount * t_cash_sign * t_exchangerate/fxrate 
               / (1 - discount::numeric/100) 
               * (discount::numeric/100),
               CASE WHEN t_voucher_id IS NULL THEN true
                       ELSE false END,
               t_voucher_id, in_payment_date, in_source
          FROM bulk_payments_in bpi
          JOIN (select entity_credit_account, id, transdate FROM ar 
                 WHERE in_account_class = 2
                 UNION
                SELECT entity_credit_account, id, transdate FROM ap
                 WHERE in_account_class = 1) gl ON gl.id = bpi.id
          JOIN entity_credit_account eca ON gl.entity_credit_account = eca.id
         WHERE bpi.amount <> 0 
               AND extract('days' from age(gl.transdate)) < eca.discount_terms;

        INSERT INTO acc_trans
               (trans_id, chart_id, amount, approved,
               voucher_id, transdate, source)
        SELECT bpi.id, t_ar_ap_id, 
               amount * t_cash_sign * -1 * t_exchangerate/fxrate 
               / (1 - discount::numeric/100) 
               * (discount::numeric/100),
               CASE WHEN t_voucher_id IS NULL THEN true
                       ELSE false END,
               t_voucher_id, in_payment_date, in_source
          FROM bulk_payments_in bpi
          JOIN (select entity_credit_account, id, transdate FROM ar 
                 WHERE in_account_class = 2
                 UNION
                SELECT entity_credit_account, id, transdate FROM ap
                 WHERE in_account_class = 1) gl ON gl.id = bpi.id
          JOIN entity_credit_account eca ON gl.entity_credit_account = eca.id
         WHERE bpi.amount <> 0 
               AND extract('days' from age(gl.transdate)) < eca.discount_terms;

        -- Insert ar/ap side
        INSERT INTO acc_trans
             (trans_id, chart_id, amount, approved,
              voucher_id, transdate, source)
           SELECT id, t_ar_ap_id,
                  amount * -1 * t_cash_sign,
                  CASE WHEN t_voucher_id IS NULL THEN true
                       ELSE false END,
                  t_voucher_id, in_payment_date, in_source
             FROM bulk_payments_in where amount <> 0;

        -- Insert fx gain/loss effects, if applicable
        INSERT INTO acc_trans
             (trans_id, chart_id, amount, approved,
              voucher_id, transdate, source)
           SELECT id, gain_loss_accno,
                  amount * t_cash_sign * (1 - t_exchangerate/fxrate),
                  CASE WHEN t_voucher_id IS NULL THEN true
                       ELSE false END,
                  t_voucher_id, in_payment_date, in_source
             FROM bulk_payments_in
            WHERE amount <> 0 AND gain_loss_accno IS NOT NULL;

        DROP TABLE bulk_payments_in;
        perform unlock_all();
        return out_count;
END;

Function: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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 (coalesce(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, 
                                 curr, 1 as invoice_class,
                                 entity_credit_account, on_hold, v.batch_id,
                                 approved, paid
                            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,
                                 curr, 2 as invoice_class,
                                 entity_credit_account, on_hold, v.batch_id,
                                 approved, paid
                            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 due <> 0
                         AND NOT a.on_hold
                         AND a.curr = in_currency
                         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: lsmb14.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: lsmb14.payment_get_entity_account_payment_info(in_entity_credit_id integer)

Returns: payment_vc_info

Language: SQL

Returns payment information on the entity credit account as required to for discount calculations and payment processing.

 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.id = $1;

Function: lsmb14.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: lsmb14.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 acc_trans
                                   JOIN chart ON (acc_trans.chart_id = chart.id)
                                   JOIN ap ON (acc_trans.trans_id = ap.id)
                                   WHERE link = 'AP'
                                   GROUP BY chart_id,
                                         trans_id, entity_credit_account
                                   HAVING SUM(acc_trans.amount) <> 0)
                               WHEN in_account_class = 2 THEN
                                ec.id IN (SELECT entity_credit_account
                                   FROM acc_trans
                                   JOIN chart ON (acc_trans.chart_id = chart.id)
                                   JOIN ar ON (acc_trans.trans_id = ar.id)
                                   WHERE link = 'AR'
                                   GROUP BY chart_id,
                                         trans_id, entity_credit_account
                                   HAVING SUM(acc_trans.amount) <> 0)
                          END
        LOOP
                RETURN NEXT out_entity;
        END LOOP;
END;

Function: lsmb14.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: lsmb14.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.invoice AS invoice, 
		       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 = 2
		              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 = 2
		              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 = 2
		              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 = 2
		         THEN ex.buy
		         ELSE ex.sell END)
		         END) AS exchangerate
                 --TODO HV prepare drop entity_id from ap,ar
                 --FROM  (SELECT id, invnumber, transdate, amount, entity_id,
                 FROM  (SELECT id, invnumber, invoice, transdate, amount,
		               1 as invoice_class, curr,
		               entity_credit_account, department_id, approved
		          FROM ap
                         UNION
		         --SELECT id, invnumber, transdate, amount, entity_id,
		         SELECT id, invnumber, invoice, transdate, amount,
		               2 AS invoice_class, 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
                        --### short term: ignore fractional cent differences
		        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, a.invoice
	LOOP
		RETURN NEXT payment_inv;
	END LOOP;
END;

Function: lsmb14.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: lsmb14.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: lsmb14.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 entity_to_location ctl ON (ctl.location_id = l.id)
                JOIN entity cp ON (ctl.entity_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: lsmb14.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 fx_gain_loss_amount numeric;
BEGIN
      IF array_upper(in_amount, 1) <> array_upper(in_cash_account_id, 1) THEN
          RAISE EXCEPTION 'Wrong number of accounts';
      END IF;
        
        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
	-- ### BUG: we don't have a guarantee that the transaction is
	--          the same currency as in_curr, so, we can't use
	--          current_exchangerate as the basis for fx gain/loss
	--          calculations
        IF (in_curr = default_currency) THEN 
           old_exchangerate := 1;
        ELSIF (in_account_class = 2) THEN
           SELECT buy INTO old_exchangerate 
           FROM exchangerate e
           JOIN ar a ON (a.transdate = e.transdate)
                        AND (a.curr = e.curr)
           WHERE a.id = in_transaction_id[out_count];
        ELSE 
           SELECT sell INTO old_exchangerate 
           FROM exchangerate e
           JOIN ap a ON (a.transdate = e.transdate)
                        AND (a.curr = e.curr)
           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  fx_gain_loss_amount equals zero then we dont need to post
        -- any transaction
       fx_gain_loss_amount := in_amount[out_count]*current_exchangerate - in_amount[out_count]*old_exchangerate;
       IF (in_account_class = 1) THEN
         -- in case of vendor invoices, the invoice amounts have been negated, do the same with the diff
         fx_gain_loss_amount := fx_gain_loss_amount * -1;
       END IF;

       IF (fx_gain_loss_amount < 0) THEN
           INSERT INTO acc_trans (chart_id, amount, trans_id, transdate, approved, source)
            VALUES ((select value::int from defaults WHERE setting_key = 'fxgain_accno_id'),
                    fx_gain_loss_amount, in_transaction_id[out_count], in_datepaid, coalesce(in_approved, true),
                    in_source[out_count]);
        ELSIF (fx_gain_loss_amount > 0) THEN
            INSERT INTO acc_trans (chart_id, amount, trans_id, transdate, approved, source)
            VALUES ((select value::int from defaults WHERE setting_key = 'fxloss_accno_id'),
                    fx_gain_loss_amount, in_transaction_id[out_count], in_datepaid, coalesce(in_approved, true),
                    in_source[out_count]);
        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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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_datepaid
              AND curr = in_curr;
IF current_exrate.transdate = in_datepaid THEN
   IF in_account_class = 2 THEN 
      UPDATE exchangerate set buy = in_exchangerate  where transdate = in_datepaid;
   ELSE
      UPDATE exchangerate set sell = in_exchangerate where transdate = in_datepaid;
   END IF;
   RETURN 0; 
ELSE
    IF in_account_class = 2 THEN
     INSERT INTO exchangerate (curr, transdate, buy) values (in_curr, in_datepaid, in_exchangerate);
  ELSE   
     INSERT INTO exchangerate (curr, transdate, sell) values (in_curr, in_datepaid, in_exchangerate);
  END IF;                                       
RETURN 0;
END IF;
END;

Function: lsmb14.periods_get()

Returns: SET OF periods

Language: SQL

Returns dates for year to date, and last year.

SELECT * FROM periods ORDER BY id

Function: lsmb14.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 entity_to_contact
 WHERE person_id = (SELECT entity_id FROM person WHERE id = in_person_id) 
       and contact_class_id = in_contact_class_id
       and contact= in_contact;
RETURN FOUND;

END;


Function: lsmb14.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 entity_to_location
 WHERE person_id = (select entity_id from person where id = in_person_id) 
       AND location_id = in_location_id 
       AND location_class = in_location_class;

RETURN FOUND;

END;

Function: lsmb14.person__get(in_entity_id integer)

Returns: person_entity

Language: SQL

SELECT e.id, e.control_code, e.name, e.country_id, c.name, 
       p.first_name, p.middle_name, p.last_name
  FROM entity e
  JOIN country c ON c.id = e.country_id
  JOIN person p ON p.entity_id = e.id
 WHERE e.id = $1;

Function: lsmb14.person__get_by_cc(in_control_code text)

Returns: person_entity

Language: SQL

SELECT e.id, e.control_code, e.name, e.country_id, c.name, 
       p.first_name, p.middle_name, p.last_name
  FROM entity e
  JOIN country c ON c.id = e.country_id
  JOIN person p ON p.entity_id = e.id
 WHERE e.control_code = $1;

Function: lsmb14.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: lsmb14.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: lsmb14.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 entity_to_contact c
		JOIN contact_class cc ON (c.contact_class_id = cc.id)
		JOIN person p ON (c.person_id = p.entity_id)
		WHERE p.entity_id = in_entity_id
	LOOP
		RETURN NEXT out_row;
	END LOOP;
END;

Function: lsmb14.person__list_languages()

Returns: SET OF language

Language: SQL

Returns a list of languages ordered by code

 SELECT * FROM language ORDER BY code ASC 

Function: lsmb14.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 entity_to_location ctl ON (ctl.location_id = l.id)
		JOIN person p ON (ctl.person_id = p.entity_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: lsmb14.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: lsmb14.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: lsmb14.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 FOUND THEN
        UPDATE entity 
           SET name = in_first_name || ' ' || in_last_name,
               country_id = in_country_id
         WHERE id = in_entity_id; 
    ELSE
        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: lsmb14.person__save_contact(in_old_contact_class integer, in_description integer, in_contact_new text, in_old_contact text, in_contact_class text, in_entity_id integer)

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 entity_to_contact;
BEGIN
    
    SELECT cc.* into v_orig 
      FROM entity_to_contact cc
      JOIN person p ON (p.entity_id = cc.entity_id)
     WHERE p.entity_id = in_entity_id 
    and cc.contact_class_id = in_old_contact_class
    AND cc.contact = in_old_contact;
    
    IF NOT FOUND THEN
    
        -- create
        INSERT INTO entity_to_contact
               (entity_id, contact_class_id, contact, description)
        VALUES (in_entity_id, in_contact_class, in_contact_new, in_description);

        return 1;
    ELSE
        -- edit.
        UPDATE entity_to_contact
           SET contact = in_contact_new, description = in_description
         WHERE contact = in_old_contact
               AND entity_id = in_entity_id
               AND contact_class_id = in_old_contact_class;
        return 0;
    END IF;
    
END;

Function: lsmb14.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 entity_to_location
       SET location_class = in_location_class
     WHERE entity_id = in_entity_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 entity_to_location 
    		(entity_id, location_id, location_class)
    	VALUES  (in_entity_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: lsmb14.pnl__customer(in_to_date integer, in_from_date date, in_id date)

Returns: SET OF pnl_line

Language: SQL

WITH gl (id) AS
 ( SELECT id FROM ap WHERE approved is true AND entity_credit_account = $1
UNION ALL
   SELECT id FROM ar WHERE approved is true AND entity_credit_account = $1
)
SELECT a.id, a.accno, a.description, a.category, 
       ah.id, ah.accno, ah.description,
       CASE WHEN a.category = 'E' THEN -1 ELSE 1 END * sum(ac.amount)
  FROM account a
  JOIN account_heading ah on a.heading = ah.id
  JOIN acc_trans ac ON a.id = ac.chart_id
  JOIN gl ON ac.trans_id = gl.id
 WHERE ac.approved is true 
          AND ($2 IS NULL OR ac.transdate >= $2) 
          AND ($3 IS NULL OR ac.transdate <= $3)
          AND a.category IN ('I', 'E')
 GROUP BY a.id, a.accno, a.description, a.category, 
          ah.id, ah.accno, ah.description
 ORDER BY a.category DESC, a.accno ASC;

Function: lsmb14.pnl__income_statement_accrual(in_business_units date, in_to_date date, in_from_date integer[])

Returns: SET OF pnl_line

Language: SQL

WITH RECURSIVE bu_tree (id, parent, path) AS (
      SELECT id, null, row(array[id])::tree_record FROM business_unit
       WHERE id = any($3)
      UNION ALL
      SELECT bu.id, parent, row((path).t || bu.id)::tree_record
        FROM business_unit bu
        JOIN bu_tree ON bu.parent_id = bu_tree.id
)
   SELECT a.id, a.accno, a.description, a.category, ah.id, ah.accno,
          ah.description, 
          CASE WHEN a.category = 'E' THEN -1 ELSE 1 END * sum(ac.amount)
     FROM account a
     JOIN account_heading ah on a.heading = ah.id
     JOIN acc_trans ac ON a.id = ac.chart_id AND ac.approved
     JOIN tx_report gl ON ac.trans_id = gl.id
LEFT JOIN (select array_agg(path) as bu_ids, entry_id
             FROM business_unit_ac buac
             JOIN bu_tree ON bu_tree.id = buac.bu_id
        GROUP BY buac.entry_id) bu
          ON (ac.entry_id = bu.entry_id)
    WHERE ac.approved is true 
          AND ($1 IS NULL OR ac.transdate >= $1) 
          AND ($2 IS NULL OR ac.transdate <= $2)
          AND ($3 = '{}' 
              OR $3 is null or in_tree($3, bu_ids))
          AND a.category IN ('I', 'E')
 GROUP BY a.id, a.accno, a.description, a.category, 
          ah.id, ah.accno, ah.description
 ORDER BY a.category DESC, a.accno ASC;

Function: lsmb14.pnl__income_statement_cash(in_business_units date, in_to_date date, in_from_date integer[])

Returns: SET OF pnl_line

Language: SQL

WITH RECURSIVE bu_tree (id, parent, path) AS (
      SELECT id, null, row(array[id])::tree_record FROM business_unit
       WHERE id = any($3)
      UNION ALL
      SELECT bu.id, parent, row((path).t || bu.id)::tree_record
        FROM business_unit bu
        JOIN bu_tree ON bu.parent_id = bu_tree.id
)
   SELECT a.id, a.accno, a.description, a.category, ah.id, ah.accno,
          ah.description, 
          CASE WHEN a.category = 'E' THEN -1 ELSE 1 END 
               * sum(ac.amount * ca.portion)
     FROM account a
     JOIN account_heading ah on a.heading = ah.id
     JOIN acc_trans ac ON a.id = ac.chart_id AND ac.approved
     JOIN tx_report gl ON ac.trans_id = gl.id
     JOIN cash_impact ca ON gl.id = ca.id
LEFT JOIN (select array_agg(path) as bu_ids, entry_id
             FROM business_unit_ac buac
             JOIN bu_tree ON bu_tree.id = buac.bu_id
         GROUP BY entry_id) bu 
          ON (ac.entry_id = bu.entry_id)
    WHERE ac.approved is true 
          AND ($3 = '{}' 
              OR $3 is null or in_tree($3, bu_ids))
          AND ($1 IS NULL OR ac.transdate >= $1) 
          AND ($2 IS NULL OR ac.transdate <= $2)
          AND a.category IN ('I', 'E')
 GROUP BY a.id, a.accno, a.description, a.category, 
          ah.id, ah.accno, ah.description
 ORDER BY a.category DESC, a.accno ASC;

Function: lsmb14.pnl__invoice(in_id integer)

Returns: SET OF pnl_line

Language: SQL

SELECT a.id, a.accno, a.description, a.category, 
       ah.id, ah.accno, ah.description,
       CASE WHEN a.category = 'E' THEN -1 ELSE 1 END * sum(ac.amount)
  FROM account a
  JOIN account_heading ah on a.heading = ah.id
  JOIN acc_trans ac ON a.id = ac.chart_id
 WHERE ac.approved AND ac.trans_id = $1 AND a.category IN ('I', 'E')
 GROUP BY a.id, a.accno, a.description, a.category, 
          ah.id, ah.accno, ah.description
 ORDER BY a.category DESC, a.accno ASC;

Function: lsmb14.pnl__product(in_business_units date, in_parts_id date, in_to_date integer, in_from_date integer[])

Returns: SET OF pnl_line

Language: SQL

WITH RECURSIVE bu_tree (id, parent, path) AS (
      SELECT id, null, row(array[id])::tree_record FROM business_unit
       WHERE id = any($4)
      UNION ALL
      SELECT bu.id, parent, row((path).t || bu.id)::tree_record
        FROM business_unit bu
        JOIN bu_tree ON bu.parent_id = bu_tree.id
)
   SELECT a.id, a.accno, a.description, a.category, ah.id, ah.accno,
          ah.description, 
          sum(ac.amount) * -1 
     FROM account a
     JOIN account_heading ah on a.heading = ah.id
     JOIN acc_trans ac ON ac.chart_id = a.id
     JOIN invoice i ON i.id = ac.invoice_id
     JOIN account_link l ON l.account_id = a.id
     JOIN ar ON ar.id = ac.trans_id
LEFT JOIN (select as_array(bu.path) as bu_ids, entry_id
             from business_unit_inv bui 
             JOIN bu_tree bu ON bui.bu_id = bu.id
         GROUP BY entry_id) bui ON bui.entry_id = i.id
    WHERE i.parts_id = $3
          AND (ac.transdate >= $1 OR $1 IS NULL) 
          AND (ac.transdate <= $2 OR $2 IS NULL)
          AND ar.approved
          AND l.description = 'IC_expense'
          AND ($4 is null or $4 = '{}' OR in_tree($4, bu_ids))
 GROUP BY a.id, a.accno, a.description, a.category, ah.id, ah.accno,
          ah.description
    UNION
   SELECT a.id, a.accno, a.description, a.category, ah.id, ah.accno,
          ah.description, 
          sum(i.sellprice * i.qty * (1 - coalesce(i.discount, 0)))
     FROM parts p
     JOIN invoice i ON i.id = p.id
     JOIN acc_trans ac ON ac.invoice_id = i.id
     JOIN account a ON p.income_accno_id = a.id
     JOIN ar ON ar.id = ac.trans_id
     JOIN account_heading ah on a.heading = ah.id
LEFT JOIN (select as_array(bu.path) as bu_ids, entry_id
             from business_unit_inv bui 
             JOIN bu_tree bu ON bui.bu_id = bu.id
         GROUP BY entry_id) bui ON bui.entry_id = i.id
    WHERE i.parts_id = $3
          AND (ac.transdate >= $1 OR $1 IS NULL) 
          AND (ac.transdate <= $2 OR $2 IS NULL)
          AND ar.approved
          AND ($4 is null or $4 = '{}' OR in_tree($4, bu_ids))
 GROUP BY a.id, a.accno, a.description, a.category, ah.id, ah.accno,
          ah.description

Function: lsmb14.pricegroup__list()

Returns: SET OF pricegroup

Language: SQL

Returns an alphabetically ordered pricegroup list.

SELECT * FROM pricegroup ORDER BY pricegroup;

Function: lsmb14.pricegroups__list()

Returns: SET OF pricegroup

Language: SQL

SELECT * FROM pricegroup;

Function: lsmb14.pricelist__delete(credit_id integer, entry_id integer)

Returns: boolean

Language: SQL

delete from partscustomer where entry_id = $1 and credit_id = $2;
delete from partsvendor where entry_id = $1 and credit_id = $2;
select true;

Function: lsmb14.pricelist__save(in_entry_id integer, in_curr integer, in_validto numeric, in_validfrom numeric, in_partnumber smallint, in_lead_time text, in_price date, in_pricebreak date, in_credit_id bpchar, in_parts_id integer)

Returns: eca__pricematrix

Language: PLPGSQL

DECLARE 
   retval eca__pricematrix;
   t_insert bool;
   t_entity_class int;

BEGIN

t_insert := false;

SELECT entity_class INTO t_entity_class FROM entity_credit_account 
  WHERE id = in_credit_id;

IF t_entity_class = 1 THEN -- VENDOR
    UPDATE partsvendor
       SET lastcost = in_price,
           leadtime = in_lead_time,
           partnumber = in_partnumber,
           curr = in_curr
     WHERE credit_id = in_credit_id AND entry_id = in_entry_id;

    IF NOT FOUND THEN
        INSERT INTO partsvendor
               (parts_id, credit_id, lastcost, leadtime, partnumber, curr)
        VALUES (in_parts_id, in_credit_id, in_price, in_leadtime::int2, 
               in_partnumber, in_curr);
    END IF;

    SELECT pv.parts_id, p.partnumber, p.description, pv.credit_id, NULL, NULL,
           pv.lastcost, pv.leadtime::int, pv.partnumber, NULL, NULL, pv.curr, 
           pv.entry_id
      INTO retval
      FROM partsvendor pv
      JOIN parts p ON p.id = pv.parts_id
     WHERE parts_id = in_parts_id and credit_id = in_credit_id;

    RETURN retval;

ELSIF t_entity_class = 2 THEN -- CUSTOMER
    UPDATE partscustomer
       SET pricebreak = in_pricebreak,
           sellprice  = in_price,
           validfrom  = in_validfrom,
           validto    = in_validto,
           curr       = in_curr
     WHERE entry_id = in_entry_id and credit_id = in_credit_id;

    IF NOT FOUND THEN
        INSERT INTO partscustomer
               (parts_id, credit_id, sellprice, validfrom, validto, curr)
        VALUES (in_parts_id, in_credit_id, in_price, in_validfrom, in_validto, 
                in_curr);

        t_insert := true;
    END IF;

    SELECT pc.parts_id, p.partnumber, p.description, pc.credit_id, 
           pc.pricebreak, pc.sellprice, NULL, NULL, NULL, pc.validfrom, 
           pc.validto, pc.curr, pc.entry_id
      INTO retval
      FROM partscustomer pc
      JOIN parts p on pc.parts_id = p.id
     WHERE entry_id = CASE WHEN t_insert 
                           THEN currval('partscustomer_entry_id_seq') 
                           ELSE in_entry_id 
                      END;
                           
    RETURN retval;

ELSE

RAISE EXCEPTION 'No valid entity credit account found';

END IF;
END;

Function: lsmb14.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: lsmb14.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;
        t_amount numeric;
    BEGIN
        SELECT CASE WHEN a.category in ('A', 'E') THEN in_amount * -1
                    ELSE in_amount
               END into t_amount
          FROM cr_report r JOIN account a ON r.chart_id = a.id
         WHERE r.id = in_report_id; 

	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
                -- could this be changed to update, if not found insert?
		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, t_amount, 0, in_date, t_uid,
				in_type);
		ELSIF in_count = 1 THEN
			UPDATE cr_report_line
			SET their_balance = t_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 = t_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 = t_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 = t_amount, 
					trans_type = in_type,
					clear_time = in_date,
					cleared = true
				WHERE t_scn = scn AND report_id = in_report_id
                                	AND our_value = t_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 = t_amount
				ORDER BY id ASC limit 1;

				UPDATE cr_report_line
                                SET their_balance = t_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 = t_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, t_amount, 0, in_date, t_uid,
			in_type);
		ELSIF in_count = 1 THEN -- perfect match
			UPDATE cr_report_line SET their_balance = t_amount,
					trans_type = in_type,
					clear_time = in_date,
					cleared = true
			WHERE report_id = in_report_id AND our_balance = t_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 = t_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 = t_amount,
					trans_type = in_type,
					clear_time = in_date,
					cleared = true
			WHERE id = lid;
			
		END IF;
	END IF;
        return 1; 
        
    END;    

Function: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.reconciliation__new_report_id(in_recon_fx integer, in_end_date numeric, in_total date, in_chart_id boolean)

Returns: integer

Language: SQL

Inserts creates a new report and returns the id.


    INSERT INTO cr_report(chart_id, their_total, end_date, recon_fx) 
    values ($1, $2, $3, $4);
    SELECT currval('cr_report_id_seq')::int;


Function: lsmb14.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;
        t_recon_fx BOOL;
    BEGIN
                SELECT recon_fx INTO t_recon_fx FROM cr_report WHERE id = in_report_id;
 
		INSERT INTO cr_report_line (report_id, scn, their_balance, 
			our_balance, "user", voucher_id, ledger_id, post_date)
		SELECT in_report_id, 
		       CASE WHEN ac.source IS NULL OR ac.source = '' 
                            THEN gl.ref
                            ELSE ac.source END,
		       0, 
		       sum(amount / CASE WHEN t_recon_fx IS NOT TRUE OR gl.table = 'gl'
                                         THEN 1
                                         WHEN t_recon_fx and gl.table = 'ap' 
                                         THEN ex.sell
                                         WHEN t_recon_fx and gl.table = 'ar' 
                                         THEN ex.buy
                                    END) 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, curr, 
                             transdate, 'ar' as table 
                        FROM ar where approved
			UNION
		      select id, entity_credit_account::text, curr, 
                             transdate, 'ap' as table 
                        FROM ap WHERE approved
			UNION
		      select id, reference, '', 
                             transdate, '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)))
                LEFT JOIN exchangerate ex ON gl.transdate = ex.transdate
		WHERE ac.cleared IS FALSE
			AND ac.approved IS TRUE
			AND ac.chart_id = in_chart_id
			AND ac.transdate <= in_end_date
                        AND ((t_recon_fx is not true 
                                and ac.fx_transaction is not true) 
                            OR (t_recon_fx is true 
                                AND (gl.table <> 'gl' OR ac.fx_transaction
                                                      IS TRUE))) 
		GROUP BY gl.ref, ac.source, ac.transdate,
			ac.memo, ac.voucher_id, gl.table, 
                        case when gl.table = 'gl' then gl.id else 1 end
		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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.report__cash_summary(in_to_accno date, in_from_accno date, in_date_to text, in_date_from text)

Returns: SET OF cash_summary_item

Language: SQL

SELECT a.id, a.accno, a.is_heading, a.description, t.label, 
       sum(CASE WHEN ac.amount < 0 THEN ac.amount * -1 ELSE NULL END),
       sum(CASE WHEN ac.amount > 0 THEN ac.amount ELSE NULL END)
  FROM (select id, accno, false as is_heading, description FROM account
       UNION
        SELECT id, accno, true, description FROM account_heading) a
  LEFT
  JOIN acc_trans ac ON ac.chart_id = a.id 
  LEFT
  JOIN (select id, case when table_name ilike 'ar' THEN 'rcpt'
                        when table_name ilike 'ap' THEN 'pmt'
                        when table_name ilike 'gl' THEN 'xfer'
                    END AS label
          FROM transactions) t ON t.id = ac.trans_id
 WHERE accno BETWEEN $3 AND $4
        and ac.transdate BETWEEN $1 AND $2
GROUP BY a.id, a.accno, a.is_heading, a.description, t.label
ORDER BY accno;


Function: lsmb14.report__coa()

Returns: SET OF coa_entry

Language: SQL


WITH ac (chart_id, amount) AS (
     SELECT chart_id, CASE WHEN acc_trans.approved and gl.approved THEN amount
                           ELSE 0 
                       END
       FROM acc_trans
       JOIN (select id, approved from ar union all
             select id, approved from ap union all
             select id, approved from gl) gl ON gl.id = acc_trans.trans_id
),
l(account_id, link) AS (
     SELECT account_id, array_to_string(array_agg(description), ':')
       FROM account_link
   GROUP BY account_id
)
SELECT a.id, a.is_heading, a.accno, a.description, a.gifi_accno, 
       CASE WHEN sum(ac.amount) < 0 THEN sum(amount) * -1 ELSE null::numeric
        END,
       CASE WHEN sum(ac.amount) > 0 THEN sum(amount) ELSE null::numeric END,
       count(ac.*), l.link
  FROM (SELECT id,false as is_heading, accno, description, gifi_accno
          FROM account
         UNION
        SELECT id, true, accno, description, null::text 
          FROM account_heading) a

 LEFT JOIN ac ON ac.chart_id = a.id AND not a.is_heading
 LEFT JOIN l ON l.account_id = a.id AND NOT a.is_heading
  GROUP BY a.id, a.is_heading, a.accno, a.description, a.gifi_accno, l.link
  ORDER BY a.accno;


Function: lsmb14.report__general_balance(in_date_to date, in_date_from date)

Returns: SET OF general_balance_line

Language: SQL


SELECT a.id, a.accno, a.description,
      sum(CASE WHEN ac.transdate < $1 THEN abs(amount) ELSE null END),
      sum(CASE WHEN ac.transdate >= $1 AND ac.amount < 0 
               THEN ac.amount * -1 ELSE null END),
      SUM(CASE WHEN ac.transdate >= $1 AND ac.amount > 0
               THEN ac.amount ELSE null END),
      SUM(ABS(ac.amount))
 FROM account a 
 LEFT
 JOIN acc_trans ac ON ac.chart_id = a.id
 LEFT 
 JOIN (select id, approved from ar UNION
       SELECT id, approved from ap UNION
       SELECT id, approved FROM gl) gl ON ac.trans_id = gl.id
WHERE gl.approved and ac.approved
      and ac.transdate <= $2 
GROUP BY a.id, a.accno, a.description
ORDER BY a.accno;


Function: lsmb14.report__gl(in_business_units text, in_to_amount text, in_from_amount bpchar, in_approved text, in_to_date text, in_from_date text, in_description date, in_memo date, in_source boolean, in_category numeric, in_accno numeric, in_reference integer[])

Returns: SET OF gl_report_item

Language: PLPGSQL

DECLARE 
         retval gl_report_item;
         t_balance numeric;
         t_chart_id int;
BEGIN

IF in_from_date IS NULL THEN
   t_balance := 0;
ELSIF in_accno IS NOT NULL THEN
   SELECT id INTO t_chart_id FROM account WHERE accno  = in_accno;
   t_balance := account__obtain_balance(in_from_date , 
                                       (select id from account 
                                         where accno = in_accno));
ELSE
   t_balance := null;
END IF;

FOR retval IN
       WITH RECURSIVE bu_tree (id, path) AS (
            SELECT id, id::text AS path
              FROM business_unit
             WHERE parent_id is null
            UNION
            SELECT bu.id, bu_tree.path || ',' || bu.id
              FROM business_unit bu
              JOIN bu_tree ON bu_tree.id = bu.parent_id
            )
       SELECT g.id, g.type, g.invoice, g.reference, g.description, ac.transdate,
              ac.source, ac.amount, c.accno, c.gifi_accno, 
              g.till, ac.cleared, ac.memo, c.description AS accname, 
              ac.chart_id, ac.entry_id, 
              sum(ac.amount) over (rows unbounded preceding) + t_balance 
                as running_balance,
              compound_array(ARRAY[ARRAY[bac.class_id, bac.bu_id]])
         FROM (select id, 'gl' as type, false as invoice, reference, 
                      description, approved,
                      null::text as till 
                 FROM gl
               UNION
               SELECT ar.id, 'ar', invoice, invnumber, e.name, approved, till
                 FROM ar
                 JOIN entity_credit_account eca ON ar.entity_credit_account
                      = eca.id
                 JOIN entity e ON e.id = eca.entity_id
               UNION
               SELECT ap.id, 'ap', invoice, invnumber, e.name, approved,
                      null as till
                 FROM ap
                 JOIN entity_credit_account eca ON ap.entity_credit_account 
                      = eca.id
                 JOIN entity e ON e.id = eca.entity_id) g
         JOIN acc_trans ac ON ac.trans_id = g.id
         JOIN account c ON ac.chart_id = c.id
    LEFT JOIN business_unit_ac bac ON ac.entry_id = bac.entry_id 
    LEFT JOIN bu_tree ON bac.bu_id = bu_tree.id
        WHERE (g.reference ilike in_reference || '%' or in_reference is null)
              AND (c.accno = in_accno OR in_accno IS NULL)
              AND (ac.source ilike '%' || in_source || '%' 
                   OR in_source is null)
              AND (ac.memo ilike '%' || in_memo || '%' OR in_memo is null)
             AND (in_description IS NULL OR
                  g.description
                  @@
                  plainto_tsquery(get_default_lang()::regconfig, in_description))
              AND (transdate BETWEEN in_from_date AND in_to_date
                   OR (transdate >= in_from_date AND  in_to_date IS NULL)
                   OR (transdate <= in_to_date AND in_from_date IS NULL)
                   OR (in_to_date IS NULL AND in_from_date IS NULL))
              AND (in_approved is false OR (g.approved AND ac.approved))
              AND (in_from_amount IS NULL OR ac.amount >= in_from_amount)
              AND (in_to_amount IS NULL OR ac.amount <= in_to_amount)
              AND (in_category = c.category OR in_category IS NULL)
     GROUP BY g.id, g.type, g.invoice, g.reference, g.description, ac.transdate,
              ac.source, ac.amount, c.accno, c.gifi_accno,
              g.till, ac.cleared, ac.memo, c.description,
              ac.chart_id, ac.entry_id, ac.trans_id
       HAVING in_business_units is null or in_business_units 
                <@ compound_array(string_to_array(bu_tree.path, ',')::int[])
     ORDER BY ac.transdate, ac.trans_id, c.accno
LOOP
   RETURN NEXT retval;
END LOOP;
END;

Function: lsmb14.report__invoice_aging_detail(in_use_duedate integer, in_business_units integer, in_to_date text, in_accno date, in_entity_class integer[], in_entity_id boolean)

Returns: SET OF report_aging_item

Language: PLPGSQL

DECLARE
	item report_aging_item;
BEGIN
	FOR item IN
                  WITH RECURSIVE bu_tree (id, path) AS (
                SELECT id, id::text AS path
                  FROM business_unit
                 WHERE id = any(in_business_units)
                 UNION
                SELECT bu.id, bu_tree.path || ',' || bu.id
                  FROM business_unit bu
                  JOIN bu_tree ON bu_tree.id = bu.parent_id
                       )
		SELECT c.entity_id, c.meta_number, e.name,
		       e.name as contact_name, 
	               a.invnumber, a.transdate, a.till, a.ordnumber, 
		       a.ponumber, a.notes, 
		       CASE WHEN a.age/30 = 0
		                 THEN (a.sign * sum(ac.amount)) 
                            ELSE 0 END
		            as c0, 
		       CASE WHEN a.age/30 = 1
		                 THEN (a.sign * sum(ac.amount))
                            ELSE 0 END
		            as c30, 
		       CASE WHEN a.age/30 = 2
		            THEN (a.sign * sum(ac.amount))
                            ELSE 0 END
		            as c60, 
		       CASE WHEN a.age/30 > 2
		            THEN (a.sign * sum(ac.amount))
                            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,
                   (coalesce(in_to_date, now())::date - a.transdate) as age
		  FROM (select id, invnumber, till, ordnumber, amount, duedate,
                               curr, ponumber, notes, entity_credit_account,
                               -1 AS sign, transdate,
                               CASE WHEN in_use_duedate 
                                    THEN coalesce(in_to_date, now())::date
                                         - duedate
                                    ELSE coalesce(in_to_date, now())::date
                                         - transdate 
                               END as age
                          FROM ar
                         WHERE in_entity_class = 2
                         UNION 
                        SELECT id, invnumber, null, ordnumber, amount, duedate,
                               curr, ponumber, notes, entity_credit_account,
                               1 as sign, transdate,
                               CASE WHEN in_use_duedate 
                                    THEN coalesce(in_to_date, now())::date
                                         - duedate
                                    ELSE coalesce(in_to_date, now())::date
                                         - transdate 
                               END as age
                          FROM ap
                         WHERE in_entity_class = 1) a
                  JOIN acc_trans ac ON ac.trans_id = a.id
                  JOIN account acc ON ac.chart_id = acc.id
                  JOIN account_link acl ON acl.account_id = acc.id
                       AND ((in_entity_class = 1 
                              AND acl.description = 'AP')
                           OR (in_entity_class = 2
                              AND acl.description = 'AR'))
		  JOIN entity_credit_account c 
                       ON a.entity_credit_account = c.id
		  JOIN entity e ON (e.id = c.entity_id)
             LEFT JOIN business_unit_ac buac ON ac.entry_id = buac.entry_id
             LEFT JOIN bu_tree ON buac.bu_id = bu_tree.id
	     LEFT JOIN entity_to_location e2l 
                       ON e.id = e2l.entity_id 
                       AND e2l.location_class = 3
             LEFT JOIN location l ON l.id = e2l.location_id
	     LEFT JOIN country ON (country.id = l.country_id)
                 WHERE (e.id = in_entity_id OR in_entity_id IS NULL)
                       AND (in_accno IS NULL or acc.accno = in_accno)
              GROUP BY c.entity_id, c.meta_number, e.name,
                       l.line_one, l.line_two, l.line_three,
                       l.city, l.state, l.mail_code, country.name,
                       a.invnumber, a.transdate, a.till, a.ordnumber,
                       a.ponumber, a.notes, a.amount, a.sign,
                       a.duedate, a.id, a.curr, a.age
                HAVING in_business_units is null or in_business_units 
                       <@ compound_array(string_to_array(bu_tree.path, 
                                         ',')::int[])
	      ORDER BY entity_id, curr, transdate, invnumber
	LOOP
		return next item;
        END LOOP;
END;

Function: lsmb14.report__invoice_aging_summary(in_use_duedate integer, in_business_units integer, in_to_date text, in_accno date, in_entity_class integer[], in_entity_id boolean)

Returns: SET OF report_aging_item

Language: SQL

SELECT entity_id, account_number, name, contact_name, null::text, null::date, 
       null::text, null::text, null::text, null::text, 
       sum(c0), sum(c30), sum(c60), sum(c90), null::date, null::int, curr,
       null::numeric, null::text[], null::int
  FROM report__invoice_aging_detail($1, $2, $3, $4, $5, $6)
 GROUP BY entity_id, account_number, name, contact_name, curr
 ORDER BY account_number

Function: lsmb14.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: lsmb14.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: lsmb14.session_check(in_token integer, in_session_id text)

Returns: session

Language: PLPGSQL

Returns a session row. If no session exists, it returns null

DECLARE out_row session%ROWTYPE;
BEGIN
	DELETE FROM session
	 WHERE last_used < now() - coalesce((SELECT value FROM defaults
                                    WHERE setting_key = 'session_timeout')::interval,
	                            '90 minutes'::interval);
        UPDATE session 
           SET last_used = now()
         WHERE session_id = in_session_id
               AND token = in_token
	       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

               PERFORM * 
                  FROM defaults
                 WHERE setting_key = 'auto_logout' and value = '1';

                IF FOUND THEN
                    RAISE NOTICE 'auto logout';
                    RETURN NULL;
                ELSE
                    INSERT INTO session (users_id, token)
                    SELECT id, md5(random()::text)
                      FROM users 
                     WHERE username = SESSION_USER;

                    SELECT * INTO out_row FROM SESSION 
                     WHERE users_id = (select id from users
                                             where username = SESSION_USER);
                    RETURN out_row;
               END IF;
	END IF;
	RETURN out_row;
END;

Function: lsmb14.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: lsmb14.setting__set(in_value character varying, in_setting_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_setting_key;
        IF NOT FOUND THEN
             INSERT INTO defaults (setting_key, value) 
                  VALUES (in_setting_key, in_value);
        END IF;
	RETURN TRUE;
END;

Function: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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 entity_credit_account.id,
                     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, entity_credit_account.id
	LOOP
		RETURN NEXT out_row;
	END LOOP;
END;

Function: lsmb14.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 entity_credit_account.id,
                     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, entity_credit_account.id
    LOOP
		RETURN NEXT out_row;
	END LOOP;
END;

Function: lsmb14.tg_enforce_perms_eclass()

Returns: trigger

Language: PLPGSQL

DECLARE
   r_eclass entity_class;
   roll_pfx text;
BEGIN
IF TG_OP = 'DELETE' THEN
   RETURN OLD;
ELSE 
   SELECT value INTO roll_pfx FROM defaults WHERE setting_key = 'roll_prefix';
   SELECT * INTO r_eclass from entity_class WHERE id = NEW.entity_class;
   IF pg_has_role(SESSION_USER, coalesce(roll_pfx, 
                                         'lsmb_' || current_database() || '__')
                                || 'contact_class_' || lower(regexp_replace(
                                                        r_eclass.class, 
                                                        ' ', 
                                                        '_')), 'USAGE')
   THEN
      RETURN NEW;
   ELSE
      RAISE EXCEPTION 'Access Denied for class';
   END IF;
END IF;
END;

Function: lsmb14.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: lsmb14.to_args(text[])

Returns: text[]

Language: INTERNAL

Turns a setof ARRAY[key,value] into an ARRAY[key||'='||value, key||'='||value,...]

aggregate_dummy

Function: lsmb14.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, approved) 
		VALUES (new.id, TG_RELNAME, new.approved);
	ELSEIF tg_op = 'UPDATE' THEN
		IF new.id = old.id AND new.approved = old.approved THEN
			return new;
		ELSE
			UPDATE transactions SET id = new.id, 
                                                approved = new.approved
                         WHERE id = old.id;
		END IF;
	ELSE 
		DELETE FROM transactions WHERE id = old.id;
	END IF;
	RETURN new;
END;

Function: lsmb14.trigger_parts_short()

Returns: trigger

Language: PLPGSQL

BEGIN
  IF NEW.onhand >= NEW.rop THEN
    NOTIFY parts_short;
  END IF;
  RETURN NEW;
END;

Function: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.user__get_all_users()

Returns: SET OF user_listable

Language: SQL

    
    select * from user_listable;
    

Function: lsmb14.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: lsmb14.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: lsmb14.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: lsmb14.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.invoice, 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.invoice, 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, false, 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, false, 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, false, 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: lsmb14.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: lsmb14.warehouse__list_all()

Returns: SET OF warehouse

Language: SQL

SELECT * FROM warehouse order by description;

Schema public


Table: public.ac_tax_form

Mapping journal_line to country_tax_form for reporting purposes.

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

Index - Schema public


Table: public.acc_trans

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

public.acc_trans Structure
F-Key Name Type Description
public.transactions.id trans_id integer NOT NULL
public.account.id chart_id integer NOT NULL
amount numeric NOT NULL
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
memo text
invoice_id integer
approved boolean DEFAULT true
cleared_on date
reconciled_on date
public.voucher.id voucher_id integer
entry_id serial PRIMARY KEY

Tables referencing this one via Foreign Key Constraints:

ac_transdate_year_idx date_part('YEAR'::text, transdate) acc_trans_voucher_id_idx voucher_id

Index - Schema public


Table: public.account

This table stores the main account info.

public.account Structure
F-Key Name Type Description
id serial UNIQUE NOT NULL
accno text PRIMARY KEY
description text
is_temp boolean NOT NULL DEFAULT false

Only affects equity accounts. If set, close at end of year.
category character(1) NOT NULL
gifi_accno text
public.account_heading.id heading integer NOT NULL
contra boolean NOT NULL DEFAULT false
tax boolean NOT NULL DEFAULT false
obsolete boolean NOT NULL DEFAULT false

Tables referencing this one via Foreign Key Constraints:

Index - Schema public


Table: public.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.

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

Index - Schema public


Table: public.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.

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

Tables referencing this one via Foreign Key Constraints:

Index - Schema public


Table: public.account_link

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

Index - Schema public


Table: public.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.

public.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:

Index - Schema public


Table: public.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. HV TODO drop entity_id

public.ap Structure
F-Key Name Type Description
public.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
public.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
public.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.
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.
public.entity_credit_account.id entity_credit_account integer NOT NULL

reference for the vendor account used.

 

public.ap Constraints
Name Constraint
ap_check CHECK ((((amount IS NULL) AND (curr IS NULL)) OR ((amount IS NOT NULL) AND (curr IS NOT NULL))))

Index - Schema public


Table: public.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. HV TODO drop entity_id

public.ar Structure
F-Key Name Type Description
public.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
public.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
public.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.
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.
public.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
is_return boolean DEFAULT false

 

public.ar Constraints
Name Constraint
ar_check CHECK ((((amount IS NULL) AND (curr IS NULL)) OR ((amount IS NOT NULL) AND (curr IS NOT NULL))))

Index - Schema public


Table: public.assembly

Holds mapping for parts that are members of assemblies.

public.assembly Structure
F-Key Name Type Description
public.parts.id id integer PRIMARY KEY

This is the id of the assembly the part is being mapped to.
public.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: public.asset_class

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

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

Tables referencing this one via Foreign Key Constraints:

Index - Schema public


Table: public.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.

public.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
public.asset_unit_class.id unit_class integer NOT NULL

Tables referencing this one via Foreign Key Constraints:

Index - Schema public


Table: public.asset_disposal_method

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

 

public.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:

Index - Schema public


Table: public.asset_item

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

public.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
public.warehouse.id location_id integer
public.business_unit.id department_id integer
public.eca_invoice.journal_id invoice_id integer
public.account.id asset_account_id integer
public.account.id dep_account_id integer
public.account.id exp_account_id integer
public.asset_item.id obsolete_by integer UNIQUE#1
public.asset_class.id asset_class_id integer

Tables referencing this one via Foreign Key Constraints:

Index - Schema public


Table: public.asset_note

public.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"()
public.asset_item.id ref_key integer NOT NULL
subject text

Table public.asset_note Inherits note,

 

public.asset_note Constraints
Name Constraint
asset_note_note_class_check CHECK ((note_class = 4))

Index - Schema public


Table: public.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.

public.asset_report Structure
F-Key Name Type Description
id serial PRIMARY KEY
report_date date
public.journal_entry.id gl_id bigint UNIQUE
public.asset_class.id asset_class bigint
public.asset_report_class.id report_class integer
public.entity.id entered_by bigint NOT NULL DEFAULT person__get_my_entity_id()
public.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:

Index - Schema public


Table: public.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.

public.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:

Index - Schema public


Table: public.asset_report_line

public.asset_report_line Structure
F-Key Name Type Description
public.asset_item.id asset_id bigint PRIMARY KEY
public.asset_report.id report_id bigint PRIMARY KEY
amount numeric
public.business_unit.id department_id integer

In case assets are moved between departments, we have to store this here.
public.warehouse.id warehouse_id integer

Index - Schema public


Table: public.asset_rl_to_disposal_method

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

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

Index - Schema public


Table: public.asset_unit_class

public.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:

Index - Schema public


Table: public.audittrail

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

public.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()
public.person.entity_id person_id integer NOT NULL
entry_id bigserial PRIMARY KEY
audittrail_trans_id_key trans_id

Index - Schema public


Table: public.batch

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

public.batch Structure
F-Key Name Type Description
id serial PRIMARY KEY
public.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
public.entity_employee.entity_id approved_by integer
public.entity_employee.entity_id created_by integer
public.session.session_id locked_by integer
created_on date DEFAULT now()

 

public.batch Constraints
Name Constraint
batch_control_code_check CHECK ((length(control_code) > 0))

Tables referencing this one via Foreign Key Constraints:

Index - Schema public


Table: public.batch_class

These values are hard-coded. Please coordinate before adding standard values. Values from 900 to 999 are reserved for local use.

public.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:

Index - Schema public


Table: public.bu_class_to_module

public.bu_class_to_module Structure
F-Key Name Type Description
public.business_unit_class.id bu_class_id integer PRIMARY KEY
public.lsmb_module.id module_id integer PRIMARY KEY

Index - Schema public


Table: public.budget_info

public.budget_info Structure
F-Key Name Type Description
id serial UNIQUE NOT NULL
start_date date NOT NULL
end_date date NOT NULL
reference text PRIMARY KEY
description text NOT NULL
public.entity.id entered_by integer NOT NULL DEFAULT person__get_my_entity_id()
public.entity.id approved_by integer
public.entity.id obsolete_by integer
entered_at timestamp without time zone NOT NULL DEFAULT now()
approved_at timestamp without time zone
obsolete_at timestamp without time zone

 

public.budget_info Constraints
Name Constraint
budget_info_check CHECK ((start_date < end_date))

Tables referencing this one via Foreign Key Constraints:

Index - Schema public


Table: public.budget_line

public.budget_line Structure
F-Key Name Type Description
public.budget_info.id budget_id integer PRIMARY KEY
public.account.id account_id integer PRIMARY KEY
description text
amount numeric NOT NULL

Index - Schema public


Table: public.budget_note

public.budget_note Structure
F-Key Name Type Description
id integer PRIMARY KEY DEFAULT nextval('note_id_seq'::regclass)
note_class integer NOT NULL DEFAULT 6
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"()
public.budget_info.id ref_key integer NOT NULL
subject text

Table public.budget_note Inherits note,

 

public.budget_note Constraints
Name Constraint
budget_note_note_class_check CHECK ((note_class = 6))

Index - Schema public


Table: public.budget_to_business_unit

public.budget_to_business_unit Structure
F-Key Name Type Description
public.budget_info.id budget_id integer UNIQUE PRIMARY KEY
public.business_unit.id bu_id integer NOT NULL
public.business_unit_class.id bu_class integer PRIMARY KEY

Index - Schema public


Table: public.business

Groups of Customers assigned joint discounts.

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

Index - Schema public


Table: public.business_unit

Tracks Projects, Departments, Funds, Etc.

public.business_unit Structure
F-Key Name Type Description
id serial UNIQUE#1 PRIMARY KEY
public.business_unit_class.id class_id integer UNIQUE#2 UNIQUE#1 NOT NULL
control_code text UNIQUE#2
description text
start_date date
end_date date
public.business_unit.id parent_id integer
public.entity_credit_account.id credit_id integer

Tables referencing this one via Foreign Key Constraints:

Index - Schema public


Table: public.business_unit_ac

public.business_unit_ac Structure
F-Key Name Type Description
public.acc_trans.entry_id entry_id integer PRIMARY KEY
public.business_unit_class.id public.business_unit.class_id#1 class_id integer PRIMARY KEY
public.business_unit.id#1 bu_id integer PRIMARY KEY

Index - Schema public


Table: public.business_unit_class

Consolidates projects and departments, and allows this to be extended for funds accounting and other purposes.

public.business_unit_class Structure
F-Key Name Type Description
id serial UNIQUE NOT NULL
label text PRIMARY KEY
active boolean NOT NULL DEFAULT false
ordering integer

Tables referencing this one via Foreign Key Constraints:

Index - Schema public


Table: public.business_unit_inv

public.business_unit_inv Structure
F-Key Name Type Description
public.invoice.id entry_id integer PRIMARY KEY
public.business_unit_class.id public.business_unit.class_id#1 class_id integer PRIMARY KEY
public.business_unit.id#1 bu_id integer PRIMARY KEY

Index - Schema public


Table: public.business_unit_jl

public.business_unit_jl Structure
F-Key Name Type Description
public.journal_line.id entry_id integer PRIMARY KEY
public.business_unit_class.id bu_class integer PRIMARY KEY
public.business_unit.id bu_id integer NOT NULL

Index - Schema public


Table: public.business_unit_oitem

public.business_unit_oitem Structure
F-Key Name Type Description
public.orderitems.id entry_id integer PRIMARY KEY
public.business_unit_class.id public.business_unit.class_id#1 class_id integer PRIMARY KEY
public.business_unit.id#1 bu_id integer PRIMARY KEY

Index - Schema public


Table: public.business_unit_translation

Translation information for projects, departments, etc.

public.business_unit_translation Structure
F-Key Name Type Description
public.business_unit.id trans_id integer PRIMARY KEY
language_code character varying(6) PRIMARY KEY
description text

Table public.business_unit_translation Inherits translation,

Index - Schema public


View: public.cash_impact

This view is used by cash basis reports to determine the fraction of a transaction to be counted.

public.cash_impact Structure
F-Key Name Type Description
id integer
portion numeric
rel text
transdate date
(
SELECT gl.id
     , 1::numeric AS portion
     ,'gl'::text AS rel
     , gl.transdate 
  FROM gl 
UNION ALLSELECT gl.id
     , CASE WHEN 
     (gl.amount = 
           (0)::numeric
     ) THEN 
     (0)::numeric WHEN 
     (gl.transdate = ac.transdate) THEN 
     (
           (1)::numeric + 
           (sum
                 (ac.amount) / gl.amount
           )
     ) ELSE 
     (
           (1)::numeric - 
           (
                 (gl.amount - sum
                       (ac.amount)
                 ) / gl.amount
           )
     ) END AS portion
     ,'ar'::text AS rel
     , ac.transdate 
  FROM (
           (ar gl 
              JOIN acc_trans ac 
                ON (
                       (ac.trans_id = gl.id)
                 )
           )
        JOIN account_link al 
          ON (
                 (
                       (ac.chart_id = al.account_id)
                     AND (al.description = 'AR'::text)
                 )
           )
     )
GROUP BY gl.id
     , gl.amount
     , ac.transdate
)
UNION ALLSELECT gl.id
, CASE WHEN 
(gl.amount = 
     (0)::numeric
) THEN 
(0)::numeric WHEN 
(gl.transdate = ac.transdate) THEN 
(
     (1)::numeric - 
     (sum
           (ac.amount) / gl.amount
     )
) ELSE 
(
     (1)::numeric - 
     (
           (gl.amount + sum
                 (ac.amount)
           ) / gl.amount
     )
) END AS portion
,'ap'::text AS rel
, ac.transdate 
FROM (
     (ap gl 
        JOIN acc_trans ac 
          ON (
                 (ac.trans_id = gl.id)
           )
     )
  JOIN account_link al 
    ON (
           (
                 (ac.chart_id = al.account_id)
               AND (al.description = 'AP'::text)
           )
     )
)
GROUP BY gl.id
, gl.amount
, ac.transdate;

Index - Schema public


View: public.chart

Compatibility chart for 1.2 and earlier.

public.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'::text AS charttype
, NULL::bpchar AS category
, NULL::text AS link
, NULL::integer AS account_heading
, NULL::text AS gifi_accno
, false AS contra
, false AS tax 
FROM account_heading 
UNIONSELECT c.id
, c.accno
, c.description
,'A'::text 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: public.company

public.company Structure
F-Key Name Type Description
id serial UNIQUE NOT NULL
public.entity.id entity_id integer PRIMARY KEY
legal_name text PRIMARY KEY
tax_id text

In the US this would be a EIN.
sales_tax_id text
license_number text
sic_code character varying
created date NOT NULL DEFAULT ('now'::text)::date

 

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

Tables referencing this one via Foreign Key Constraints:

Index - Schema public


Table: public.contact_class

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

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

 

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

Tables referencing this one via Foreign Key Constraints:

Index - Schema public


Table: public.country

public.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

 

public.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:

Index - Schema public


Table: public.country_tax_form

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

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

Tables referencing this one via Foreign Key Constraints:

Index - Schema public


Table: public.cr_coa_to_account

Provides name mapping for the cash reconciliation screen.

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

Index - Schema public


Table: public.cr_report

This table holds header data for cash reports.

public.cr_report Structure
F-Key Name Type Description
id bigserial PRIMARY KEY
public.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()
public.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
public.entity.id deleted_by integer
public.entity.id approved_by integer
approved_username text
recon_fx boolean DEFAULT false

 

public.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:

Index - Schema public


Table: public.cr_report_line

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

public.cr_report_line Structure
F-Key Name Type Description
id bigserial PRIMARY KEY
public.cr_report.id report_id integer NOT NULL
scn text

This is the check number. Maps to journal_entry.reference
their_balance numeric
our_balance numeric
errorcode integer
public.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
public.acc_trans.entry_id ledger_id integer
voucher_id integer
overlook boolean NOT NULL DEFAULT false
cleared boolean NOT NULL DEFAULT false

Index - Schema public


Table: public.custom_field_catalog

Deprecated, use only with old code.

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

Index - Schema public


Table: public.custom_table_catalog

Deprecated, use only with old code.

public.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:

Index - Schema public


Table: public.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.

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

Index - Schema public


Table: public.eca_invoice

Replaces the rest of the ar and ap tables. Also tracks payments and receipts.

public.eca_invoice Structure
F-Key Name Type Description
order_id integer

Link to order it was created from
public.journal_entry.id journal_id integer PRIMARY KEY
on_hold boolean DEFAULT false

On hold invoices can not be paid, and overpayments that are on hold cannot be used to pay invoices.
reverse boolean DEFAULT false

When this is set to true, the invoice is shown with opposite normal numbers, i.e. negatives appear as positives, and positives appear as negatives.
public.entity_credit_account.id credit_id integer NOT NULL
due date NOT NULL
public.language.code language_code character(6)
force_closed boolean NOT NULL DEFAULT false

When this is set to true, the invoice does not show up on outstanding reports and cannot be paid. Overpayments where this is set to true do not appear on outstanding reports and cannot be paid.
order_number text

This is the order number of the other party. So for a sales invoice, this would be a purchase order, and for a vendor invoice, this would be a sales order.

Tables referencing this one via Foreign Key Constraints:

Index - Schema public


Table: public.eca_note

Notes for entity_credit_account entries.

public.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"()
public.entity_credit_account.id ref_key integer NOT NULL

references entity_credit_account.id
subject text

Table public.eca_note Inherits note,

 

public.eca_note Constraints
Name Constraint
eca_note_note_class_check CHECK ((note_class = 3))

Index - Schema public


Table: public.eca_tax

Mapping customers and vendors to taxes.

public.eca_tax Structure
F-Key Name Type Description
public.entity_credit_account.id eca_id integer PRIMARY KEY
public.account.id chart_id integer PRIMARY KEY

Index - Schema public


Table: public.eca_to_contact

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

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

 

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

Index - Schema public


Table: public.eca_to_location

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

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

Index - Schema public


Table: public.employee_class

public.employee_class Structure
F-Key Name Type Description
label text PRIMARY KEY
id serial UNIQUE NOT NULL

Tables referencing this one via Foreign Key Constraints:

Index - Schema public


View: public.employee_search

public.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


Table: public.employee_to_ec

public.employee_to_ec Structure
F-Key Name Type Description
public.entity_employee.entity_id employee_id integer PRIMARY KEY
public.employee_class.id ec_id integer

Index - Schema public


View: public.employees

public.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: public.entity

The primary entity table to map to all contacts

public.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
public.entity_class.id public.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)
public.country.id country_id integer NOT NULL

 

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

Tables referencing this one via Foreign Key Constraints:

Index - Schema public


Table: public.entity_bank_account

This stores bank account information for both companies and persons.

public.entity_bank_account Structure
F-Key Name Type Description
id serial UNIQUE NOT NULL
public.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:

Index - Schema public


Table: public.entity_class

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

public.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
public.country.id country_id integer
active boolean NOT NULL DEFAULT true

 

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

Tables referencing this one via Foreign Key Constraints:

entity_class_idx lower(class)

Index - Schema public


Table: public.entity_credit_account

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

public.entity_credit_account Structure
F-Key Name Type Description
id serial UNIQUE NOT NULL
public.entity.id entity_id integer PRIMARY KEY
public.entity_class.id entity_class integer PRIMARY KEY
pay_to_name text
discount numeric
description text
discount_terms integer
public.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
public.language.code language_code character varying(6) DEFAULT 'en'::character varying
public.pricegroup.id pricegroup_id integer
curr character(3)
startdate date DEFAULT ('now'::text)::date
enddate date
threshold numeric
public.entity_employee.entity_id employee_id integer
public.person.id primary_contact integer
public.account.id ar_ap_account_id integer
public.account.id cash_account_id integer
public.entity_bank_account.id bank_account integer
public.country_tax_form.id taxform_id integer

 

public.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:

Index - Schema public


Table: public.entity_employee

This contains employee-specific extensions to person/entity.

public.entity_employee Structure
F-Key Name Type Description
public.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
public.entity.id manager_id integer
employeenumber character varying(32)
dob date

Tables referencing this one via Foreign Key Constraints:

Index - Schema public


Table: public.entity_note

public.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"()
public.entity.id ref_key integer NOT NULL
subject text
public.entity.id entity_id integer

Table public.entity_note Inherits note,

 

public.entity_note Constraints
Name Constraint
entity_note_note_class_check CHECK ((note_class = 1))
entity_note_id_idx id entity_note_vectors_idx vector

Index - Schema public


Table: public.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.

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

 

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

Index - Schema public


Table: public.entity_to_contact

This table stores contact information for entities

public.entity_to_contact Structure
F-Key Name Type Description
public.entity.id entity_id integer PRIMARY KEY
public.contact_class.id contact_class_id integer PRIMARY KEY
contact text PRIMARY KEY
description text

 

public.entity_to_contact Constraints
Name Constraint
entity_to_contact_contact_check CHECK ((contact ~ '[[:alnum:]_]'::text))

Index - Schema public


Table: public.entity_to_location

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

public.entity_to_location Structure
F-Key Name Type Description
public.location.id location_id integer PRIMARY KEY
public.location_class.id location_class integer PRIMARY KEY
public.entity.id entity_id integer PRIMARY KEY

Index - Schema public


Table: public.exchangerate

When you receive money in a foreign currency, it is worth to you in your local currency whatever you can get for it when you sell the acquired currency (sell rate). When you have to pay someone in a foreign currency, the equivalent amount is the amount you have to spend to acquire the foreign currency (buy rate).

public.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: public.file_base

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

public.file_base Structure
F-Key Name Type Description
content bytea NOT NULL
public.mime_type.id mime_type_id integer NOT NULL
file_name text PRIMARY KEY
description text
public.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.
public.file_class.id file_class integer PRIMARY KEY

Index - Schema public


Table: public.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.

public.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:

Index - Schema public


Table: public.file_eca

File attachments primarily attached to customer and vendor agreements.

public.file_eca 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)
public.entity_credit_account.id ref_key integer PRIMARY KEY
file_class integer PRIMARY KEY

Table public.file_eca Inherits file_base,

 

public.file_eca Constraints
Name Constraint
file_eca_file_class_check CHECK ((file_class = 5))

Index - Schema public


Table: public.file_entity

File attachments primarily attached to entities.

public.file_entity 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)
public.entity.id ref_key integer PRIMARY KEY
file_class integer PRIMARY KEY

Table public.file_entity Inherits file_base,

 

public.file_entity Constraints
Name Constraint
file_entity_file_class_check CHECK ((file_class = 4))

Index - Schema public


View: public.file_links

public.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: public.file_order

File attachments primarily attached to orders and quotations.

public.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)
public.oe.id ref_key integer PRIMARY KEY
file_class integer PRIMARY KEY

Table public.file_order Inherits file_base,

 

public.file_order Constraints
Name Constraint
file_order_file_class_check CHECK ((file_class = 2))

Tables referencing this one via Foreign Key Constraints:

Index - Schema public


View: public.file_order_links

public.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: public.file_order_to_order

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

public.file_order_to_order Structure
F-Key Name Type Description
public.file_order.id file_id integer PRIMARY KEY
source_class integer PRIMARY KEY
public.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 public.file_order_to_order Inherits file_secondary_attachment,

 

public.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: public.file_order_to_tx

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

public.file_order_to_tx Structure
F-Key Name Type Description
public.file_order.id file_id integer PRIMARY KEY
source_class integer PRIMARY KEY
public.journal_entry.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 public.file_order_to_tx Inherits file_secondary_attachment,

 

public.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: public.file_part

File attachments primarily attached to goods and services.

public.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)
public.parts.id ref_key integer PRIMARY KEY
file_class integer PRIMARY KEY

Table public.file_part Inherits file_base,

 

public.file_part Constraints
Name Constraint
file_part_file_class_check CHECK ((file_class = 3))

Index - Schema public


Table: public.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.

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

Index - Schema public


Table: public.file_transaction

File attachments primarily attached to AR/AP/GL.

public.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)
public.transactions.id ref_key integer PRIMARY KEY
file_class integer PRIMARY KEY

Table public.file_transaction Inherits file_base,

 

public.file_transaction Constraints
Name Constraint
file_transaction_file_class_check CHECK ((file_class = 1))

Tables referencing this one via Foreign Key Constraints:

Index - Schema public


View: public.file_tx_links

public.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'::text 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: public.file_tx_to_order

Secondary links from journal entries to orders.

public.file_tx_to_order Structure
F-Key Name Type Description
public.file_transaction.id file_id integer PRIMARY KEY
source_class integer PRIMARY KEY
public.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 public.file_tx_to_order Inherits file_secondary_attachment,

 

public.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: public.file_view_catalog

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

Index - Schema public


Table: public.gifi

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

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

Index - Schema public


Table: public.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.

public.gl Structure
F-Key Name Type Description
public.transactions.id id integer PRIMARY KEY DEFAULT nextval('id'::regclass)
reference text
description text
transdate date DEFAULT ('now'::text)::date
public.person.id person_id integer

the person_id of the employee who created the entry.
notes text
approved boolean DEFAULT true

Tables referencing this one via Foreign Key Constraints:

Index - Schema public


Table: public.inventory

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

public.inventory Structure
F-Key Name Type Description
public.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: public.inventory_report

public.inventory_report Structure
F-Key Name Type Description
id serial PRIMARY KEY
transdate date NOT NULL
source text
ar_trans_id integer
ap_trans_id integer

Tables referencing this one via Foreign Key Constraints:

Index - Schema public


Table: public.inventory_report_line

public.inventory_report_line Structure
F-Key Name Type Description
public.inventory_report.id report_id integer PRIMARY KEY
public.parts.id parts_id integer PRIMARY KEY
counted numeric
expected numeric

Index - Schema public


Table: public.invoice

Line items of invoices with goods/services attached.

public.invoice Structure
F-Key Name Type Description
id serial PRIMARY KEY
public.transactions.id trans_id integer
public.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
deliverydate date
serialnumber text
notes text

Tables referencing this one via Foreign Key Constraints:

invoice_id_key id invoice_trans_id_key trans_id

Index - Schema public


Table: public.invoice_note

public.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"()
public.invoice.id ref_key integer NOT NULL
subject text

Table public.invoice_note Inherits note,

invoice_note_id_idx id invoice_note_vectors_idx vector

Index - Schema public


Table: public.invoice_tax_form

Maping invoice to country_tax_form.

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

Index - Schema public


Table: public.jcitems

Time and materials cards. Materials cards not implemented.

public.jcitems Structure
F-Key Name Type Description
id serial PRIMARY KEY
public.business_unit.id business_unit_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
public.person.id person_id integer NOT NULL
notes text
total numeric NOT NULL
non_billable numeric NOT NULL
jcitems_id_key id

Index - Schema public


Table: public.jctype

public.jctype Structure
F-Key Name Type Description
id integer UNIQUE NOT NULL
label text PRIMARY KEY
description text NOT NULL
is_service boolean DEFAULT true
is_timecard boolean DEFAULT true

Index - Schema public


Table: public.job

public.job Structure
F-Key Name Type Description
public.business_unit.id bu_id integer PRIMARY KEY
parts_id integer

Job costing/manufacturing here not implemented.
production numeric
completed numeric

Index - Schema public


Table: public.journal_entry

This tale records the header information for each transaction. It replaces parts of the following tables: acc_trans, ar, ap, gl, transactions. Note now all ar/ap transactions are also journal entries.

public.journal_entry Structure
F-Key Name Type Description
id serial PRIMARY KEY
reference text NOT NULL

Invoice number or journal entry number.
description text
public.session.session_id locked_by integer
public.journal_type.id journal integer
post_date date NOT NULL DEFAULT now()
effective_start date NOT NULL

For transactions whose effects are spread out over a period of time, this is the effective start date for the transaction. To be used by add-ons for automating adjustments.
effective_end date NOT NULL

For transactions whose effects are spread out over a period of time, this is the effective end date for the transaction. To be used by add-ons for automating adjustments.
currency character(3) NOT NULL
approved boolean DEFAULT false
is_template boolean DEFAULT false

Set true for template transactions. Templates can never be approved but can be copied into new transactions and are useful for recurrances.
public.entity.id entered_by integer NOT NULL
public.entity.id approved_by integer

 

public.journal_entry Constraints
Name Constraint
journal_entry_check CHECK (((is_template IS FALSE) OR (approved IS FALSE)))

Tables referencing this one via Foreign Key Constraints:

Index - Schema public


Table: public.journal_line

Replaces acc_trans as the main account transaction line table.

public.journal_line Structure
F-Key Name Type Description
id serial PRIMARY KEY
public.account.id account_id integer NOT NULL
public.journal_entry.id journal_id integer NOT NULL
amount numeric NOT NULL
cleared boolean NOT NULL DEFAULT false

Still needed both for legacy data and in case reconciliation data must eventually be purged.
public.cr_report.id reconciliation_report integer
public.account_link_description.description line_type text

 

public.journal_line Constraints
Name Constraint
journal_line_amount_check CHECK ((amount <> 'NaN'::numeric))

Tables referencing this one via Foreign Key Constraints:

Index - Schema public


Table: public.journal_note

This stores notes attached to journal entries, including payments and invoices.

public.journal_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"()
public.journal_entry.id ref_key integer NOT NULL
subject text
internal_only boolean NOT NULL DEFAULT false

When set to true, does not show up in notes list for invoice templates

Table public.journal_note Inherits note,

 

public.journal_note Constraints
Name Constraint
journal_note_note_class_check CHECK ((note_class = 5))

Index - Schema public


Table: public.journal_type

This table describes the journal entry type of the transaction. The following values are hard coded by default: 1: General journal 2: Sales (AR) 3: Purchases (AP) 4: Receipts 5: Payments

public.journal_type Structure
F-Key Name Type Description
id serial UNIQUE NOT NULL
name text PRIMARY KEY

Tables referencing this one via Foreign Key Constraints:

Index - Schema public


Table: public.language

Languages for manual translations and so forth.

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

Tables referencing this one via Foreign Key Constraints:

Index - Schema public


Table: public.location

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

public.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
public.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

 

public.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:

Index - Schema public


Table: public.location_class

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

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

 

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

Tables referencing this one via Foreign Key Constraints:

Index - Schema public


Table: public.lsmb_group

public.lsmb_group Structure
F-Key Name Type Description
role_name text PRIMARY KEY

Tables referencing this one via Foreign Key Constraints:

Index - Schema public


Table: public.lsmb_group_grants

public.lsmb_group_grants Structure
F-Key Name Type Description
public.lsmb_group.role_name group_name text PRIMARY KEY
granted_role text PRIMARY KEY

Index - Schema public


Table: public.lsmb_module

This stores categories functionality into modules. Addons may add rows here, but the id should be hardcoded. As always 900-1000 will be reserved for internal use, and negative numbers will be reserved for testing.

public.lsmb_module Structure
F-Key Name Type Description
id integer UNIQUE NOT NULL
label text PRIMARY KEY

Tables referencing this one via Foreign Key Constraints:

Index - Schema public


Table: public.lsmb_roles

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

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

Index - Schema public


Table: public.makemodel

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

public.makemodel Structure
F-Key Name Type Description
parts_id integer PRIMARY KEY
barcode text
make text PRIMARY KEY
model text PRIMARY KEY
makemodel_make_key lower(make) makemodel_model_key lower(model) makemodel_parts_id_key parts_id

Index - Schema public


Table: public.menu_acl

Provides access control list entries for menu nodes.

public.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'.
public.menu_node.id node_id integer PRIMARY KEY

 

public.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: public.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.

public.menu_attribute Structure
F-Key Name Type Description
public.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: public.menu_friendly

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

public.menu_friendly Structure
F-Key Name Type Description
level integer
path text
label text
id integer
position integer
WITH RECURSIVE tree
(path
     , id
     , parent
     , level
     , positions
) AS 
(
SELECT (menu_node.id)::text AS path
     , menu_node.id
     , menu_node.parent
     , 0 AS level
     , (menu_node."position")::text AS "position"
  FROM menu_node 
 WHERE (menu_node.parent IS NULL)
 UNIONSELECT (
           (t.path || 
               ','::text
           ) || 
           (n.id)::text
     )
     , n.id
     , n.parent
     , (t.level + 1)
     , (
           (t.positions || 
               ','::text
           ) || n."position"
     )
  FROM (menu_node n 
        JOIN tree t 
          ON (
                 (t.id = n.parent)
           )
     )
)
SELECT t.level
, t.path
, (repeat
     (' '::text
           , (2 * t.level)
     ) || 
     (n.label)::text
) AS label
, n.id
, n."position"
FROM (tree t 
  JOIN menu_node n 
 USING (id)
)
ORDER BY (string_to_array
     (t.positions
           ,','::text
     )
)::integer[];

Index - Schema public


Table: public.menu_node

This table stores the tree structure of the menu.

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

Tables referencing this one via Foreign Key Constraints:

Index - Schema public


Table: public.mime_type

This is a lookup table for storing MIME types.

public.mime_type Structure
F-Key Name Type Description
id serial UNIQUE NOT NULL
mime_type text PRIMARY KEY
invoice_include boolean DEFAULT false

Tables referencing this one via Foreign Key Constraints:

Index - Schema public


Table: public.new_shipto

Tracks ship_to information for orders and invoices.

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

Index - Schema public


Table: public.note

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

public.note Structure
F-Key Name Type Description
id serial PRIMARY KEY
public.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: public.note_class

Coordinate with others before adding entries.

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

 

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

Tables referencing this one via Foreign Key Constraints:

Index - Schema public


Table: public.oe

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

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

Tables referencing this one via Foreign Key Constraints:

oe_id_key id oe_ordnumber_key ordnumber oe_transdate_key transdate

Index - Schema public


Table: public.oe_class

Hardwired classifications for orders and quotations. Coordinate before adding.

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

 

public.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:

Index - Schema public


Table: public.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.

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

Index - Schema public


Table: public.orderitems

Line items for sales/purchase orders and quotations.

public.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)
reqdate date
ship numeric
serialnumber text
notes text

Tables referencing this one via Foreign Key Constraints:

orderitems_id_key id orderitems_trans_id_key trans_id

Index - Schema public


View: public.overpayments

public.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
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
, 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
, cmp.legal_name
, eca.id
, eca.entity_id
, eca.discount
, eca.meta_number;

Index - Schema public


Table: public.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.

public.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.
public.account.id inventory_accno_id integer
public.account.id income_accno_id integer
public.account.id expense_accno_id integer
public.account.id returns_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
avgcost numeric

Tables referencing this one via Foreign Key Constraints:

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

Index - Schema public


Table: public.parts_translation

Translation information for parts.

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

Table public.parts_translation Inherits translation,

Index - Schema public


Table: public.partscustomer

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

public.partscustomer Structure
F-Key Name Type Description
parts_id integer
public.entity_credit_account.id credit_id integer
public.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: public.partsgroup

Groups of parts for Point of Sale screen.

public.partsgroup Structure
F-Key Name Type Description
id serial PRIMARY KEY
partsgroup text
public.partsgroup.id parent integer

Tables referencing this one via Foreign Key Constraints:

partsgroup_id_key id

Index - Schema public


Table: public.partsgroup_translation

Translation information for partsgroups.

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

Table public.partsgroup_translation Inherits translation,

Index - Schema public


Table: public.partstax

Mapping of parts to taxes.

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

Index - Schema public


Table: public.partsvendor

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

public.partsvendor Structure
F-Key Name Type Description
public.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: public.payment

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

public.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
public.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
public.entity_credit_account.id entity_credit_id integer
public.person.id employee_id integer
currency character(3)
notes text

Tables referencing this one via Foreign Key Constraints:

payment_id_idx id

Index - Schema public


Table: public.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.

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

Index - Schema public


Table: public.payment_map

This maps the payment journal entry to the invoices it pays. A couple notes here: 1) There is no requirement tht the payment "invoice" be linked to the same entity_credit_account as the paid invoice. People can pay eachothers invoices if LedgerSMB supports this at an app level. 2) This now means that payments are first class transactions.

public.payment_map Structure
F-Key Name Type Description
public.journal_line.id line_id integer PRIMARY KEY
public.eca_invoice.journal_id pays integer NOT NULL

Index - Schema public


Table: public.payment_type

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

Index - Schema public


Table: public.payroll_deduction

public.payroll_deduction Structure
F-Key Name Type Description
entry_id serial UNIQUE NOT NULL
public.entity.id entity_id integer PRIMARY KEY
public.payroll_deduction_type.id type_id integer PRIMARY KEY
rate numeric NOT NULL

Index - Schema public


Table: public.payroll_deduction_class

public.payroll_deduction_class Structure
F-Key Name Type Description
id integer UNIQUE#1 NOT NULL
public.country.id country_id integer UNIQUE#1 PRIMARY KEY
label text PRIMARY KEY

Tables referencing this one via Foreign Key Constraints:

Index - Schema public


Table: public.payroll_deduction_type

public.payroll_deduction_type Structure
F-Key Name Type Description
id serial UNIQUE NOT NULL
public.account.id account_id integer NOT NULL
public.payroll_deduction_class.id#1 pdc_id integer NOT NULL
public.payroll_deduction_class.country_id#1 country_id integer NOT NULL
label text NOT NULL
unit text NOT NULL
default_amount numeric
calc_percent boolean NOT NULL

Tables referencing this one via Foreign Key Constraints:

Index - Schema public


Table: public.payroll_employee_class

public.payroll_employee_class Structure
F-Key Name Type Description
id serial UNIQUE NOT NULL
label text PRIMARY KEY

Tables referencing this one via Foreign Key Constraints:

Index - Schema public


Table: public.payroll_employee_class_to_income_type

public.payroll_employee_class_to_income_type Structure
F-Key Name Type Description
public.payroll_employee_class.id ec_id integer PRIMARY KEY
public.payroll_income_type.id it_id integer PRIMARY KEY

Index - Schema public


Table: public.payroll_income_category

public.payroll_income_category Structure
F-Key Name Type Description
id serial UNIQUE NOT NULL
label text

Index - Schema public


Table: public.payroll_income_class

public.payroll_income_class Structure
F-Key Name Type Description
id integer UNIQUE#1 NOT NULL
public.country.id country_id integer UNIQUE#1 PRIMARY KEY
label text PRIMARY KEY

Tables referencing this one via Foreign Key Constraints:

Index - Schema public


Table: public.payroll_income_type

public.payroll_income_type Structure
F-Key Name Type Description
id serial UNIQUE NOT NULL
public.account.id account_id integer NOT NULL
public.payroll_income_class.id#1 pic_id integer NOT NULL
public.payroll_income_class.country_id#1 country_id integer NOT NULL
label text NOT NULL
unit text NOT NULL
default_amount numeric

Tables referencing this one via Foreign Key Constraints:

Index - Schema public


Table: public.payroll_paid_timeoff

public.payroll_paid_timeoff Structure
F-Key Name Type Description
public.entity.id employee_id integer NOT NULL
public.payroll_pto_class.id pto_class_id integer NOT NULL
public.payroll_report.id report_id integer NOT NULL
amount numeric NOT NULL

Index - Schema public


Table: public.payroll_pto_class

public.payroll_pto_class Structure
F-Key Name Type Description
id serial UNIQUE NOT NULL
label text PRIMARY KEY

Tables referencing this one via Foreign Key Constraints:

Index - Schema public


Table: public.payroll_report

public.payroll_report Structure
F-Key Name Type Description
id serial PRIMARY KEY
public.payroll_employee_class.id ec_id integer NOT NULL
payment_date date NOT NULL
public.entity_employee.entity_id created_by integer
public.entity_employee.entity_id approved_by integer

Tables referencing this one via Foreign Key Constraints:

Index - Schema public


Table: public.payroll_report_line

public.payroll_report_line Structure
F-Key Name Type Description
id serial UNIQUE NOT NULL
public.payroll_report.id report_id integer PRIMARY KEY
public.entity.id employee_id integer PRIMARY KEY
public.payroll_income_type.id it_id integer PRIMARY KEY
qty numeric NOT NULL
rate numeric NOT NULL
description text

Index - Schema public


Table: public.payroll_wage

public.payroll_wage Structure
F-Key Name Type Description
entry_id serial UNIQUE NOT NULL
public.entity.id entity_id integer PRIMARY KEY
public.payroll_income_type.id type_id integer PRIMARY KEY
rate numeric NOT NULL

Index - Schema public


View: public.periods

public.periods Structure
F-Key Name Type Description
id text
label text
date_to date
date_from date
SELECT'ytd'::text AS id
,'Year to Date'::text AS label
, (now
     ()
)::date AS date_to
, (
     (
           (date_part
                 ('year'::text
                       , now
                       ()
                 )
           )::text || '-01-01'::text
     )
)::date AS date_from 
UNIONSELECT'last_year'::text AS id
,'Last Year'::text 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: public.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.

public.person Structure
F-Key Name Type Description
id serial PRIMARY KEY
public.entity.id entity_id integer UNIQUE NOT NULL
public.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

 

public.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:

Index - Schema public


Table: public.person_to_company

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

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

Index - Schema public


Table: public.pricegroup

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

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

Tables referencing this one via Foreign Key Constraints:

pricegroup_id_key id pricegroup_pricegroup_key pricegroup

Index - Schema public


View: public.recon_payee

public.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
, ac.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: public.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.

public.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: public.recurringemail

Email to be sent out when recurring transaction is posted.

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

Index - Schema public


Table: public.recurringprint

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

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

Index - Schema public


View: public.role_view

public.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
rolreplication 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.rolreplication
, 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: public.salutation

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

Tables referencing this one via Foreign Key Constraints:

Index - Schema public


Table: public.session

This table is used to track sessions on a database level across page requests (discretionary locks,open forms for anti-xsrf measures). 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.

public.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
public.users.id users_id integer NOT NULL
notify_pasword interval NOT NULL DEFAULT '7 days'::interval

 

public.session Constraints
Name Constraint
session_token_check CHECK ((length((token)::text) = 32))

Tables referencing this one via Foreign Key Constraints:

Index - Schema public


Table: public.sic

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

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

Index - Schema public


Table: public.status

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

public.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: public.tax

Information on tax rates.

public.tax Structure
F-Key Name Type Description
public.account.id public.account.id chart_id integer PRIMARY KEY
rate numeric
minvalue numeric
maxvalue 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).
public.taxmodule.taxmodule_id taxmodule_id integer NOT NULL DEFAULT 1

Index - Schema public


Table: public.tax_extended

This stores extended information for manual tax calculations.

public.tax_extended Structure
F-Key Name Type Description
tax_basis numeric
rate numeric
public.journal_line.id entry_id integer PRIMARY KEY

Index - Schema public


Table: public.taxcategory

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

Tables referencing this one via Foreign Key Constraints:

Index - Schema public


Table: public.taxmodule

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

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

Tables referencing this one via Foreign Key Constraints:

Index - Schema public


Table: public.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.

public.transactions Structure
F-Key Name Type Description
id integer PRIMARY KEY
table_name text
public.session.session_id locked_by integer

This should only be used in pessimistic locking measures as required by large batch work flows.
approved boolean
public.entity.id approved_by integer
approved_at timestamp without time zone

Tables referencing this one via Foreign Key Constraints:

transactions_locked_by_i locked_by

Index - Schema public


Table: public.translation

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

public.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


Table: public.trial_balance

public.trial_balance Structure
F-Key Name Type Description
id serial PRIMARY KEY
date_from date
date_to date
description text NOT NULL
public.trial_balance__yearend_types.type yearend text NOT NULL

Tables referencing this one via Foreign Key Constraints:

Index - Schema public


Table: public.trial_balance__account_to_report

public.trial_balance__account_to_report Structure
F-Key Name Type Description
public.trial_balance.id report_id integer NOT NULL
public.account.id account_id integer NOT NULL

Index - Schema public


Table: public.trial_balance__heading_to_report

public.trial_balance__heading_to_report Structure
F-Key Name Type Description
public.trial_balance.id report_id integer NOT NULL
public.account_heading.id heading_id integer NOT NULL

Index - Schema public


Table: public.trial_balance__yearend_types

public.trial_balance__yearend_types Structure
F-Key Name Type Description
type text PRIMARY KEY

Tables referencing this one via Foreign Key Constraints:

Index - Schema public


View: public.tx_report

This view provides join and approval information for transactions.

public.tx_report Structure
F-Key Name Type Description
id integer
reference text
entity_credit_account integer
table text
approved boolean
(
SELECT gl.id
     , gl.reference
     , NULL::integer AS entity_credit_account
     ,'gl'::text AS "table"
     , gl.approved 
  FROM gl 
UNION ALLSELECT ap.id
     , ap.invnumber AS reference
     , ap.entity_credit_account
     ,'ap'::text AS "table"
     , ap.approved 
  FROM ap
)
UNION ALLSELECT ar.id
, ar.invnumber AS reference
, ar.entity_credit_account
,'ar'::text AS "table"
, ar.approved 
FROM ar;

Index - Schema public


View: public.user_listable

public.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: public.user_preference

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

public.user_preference Structure
F-Key Name Type Description
public.users.id id integer PRIMARY KEY
public.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: public.users

public.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
public.entity.id entity_id integer NOT NULL

Tables referencing this one via Foreign Key Constraints:

Index - Schema public


Table: public.voucher

Mapping transactions to batches for batch approval.

public.voucher Structure
F-Key Name Type Description
public.transactions.id trans_id integer NOT NULL
public.batch.id batch_id integer NOT NULL
id serial PRIMARY KEY

This is simply a surrogate key for easy reference.
public.batch_class.id batch_class integer NOT NULL

This is the authoritative class of the voucher.

Tables referencing this one via Foreign Key Constraints:

Index - Schema public


Table: public.warehouse

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

Tables referencing this one via Foreign Key Constraints:

Index - Schema public


Table: public.yearend

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

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

Index - Schema public


Function: public._entity_location_save(in_country_id 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 entity_to_location
	WHERE entity_id = in_entity_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_id) 
	INTO l_id;

	INSERT INTO entity_to_location
		(entity_id, location_class, location_id)
	VALUES  (in_entity_id, in_location_class, l_id);

	RETURN l_id;    
    END;


Function: public.account__delete(in_id integer)

Returns: boolean

Language: PLPGSQL

This deletes an account with the id specified. If the account has transactions associated with it, it will fail and raise a foreign key constraint.

BEGIN
DELETE FROM account_link WHERE account_id = in_id;
DELETE FROM account WHERE id = in_id;
RETURN FOUND;
END;

Function: public.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: public.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: public.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: public.account__is_recon(in_accno text)

Returns: boolean

Language: SQL

Returns true if account is set up for reconciliation, false otherwise. Note that returns false on invalid account number too

 SELECT count(*) > 0 
     FROM cr_coa_to_account c2a
     JOIN account ON account.id = c2a.chart_id 
    WHERE accno = $1; 

Function: public.account__list_by_heading()

Returns: SET OF account

Language: SQL

SELECT * FROM account ORDER BY heading;

Function: public.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: public.account__save(in_is_temp integer, in_obsolete text, in_link text, in_tax bpchar, in_contra text, in_heading integer, in_gifi_accno boolean, in_category boolean, in_description text[], in_accno boolean, in_id boolean)

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,
                obsolete = in_obsolete,
                tax = t_tax,
                is_temp = in_is_temp
	WHERE id = in_id;

	IF FOUND THEN
		t_id := in_id;
	ELSE
                -- can't obsolete on insert, but this can be changed if users
                -- request it --CT
		INSERT INTO account (accno, description, category, gifi_accno,
			heading, contra, tax, is_temp)
		VALUES (in_accno, in_description, in_category, in_gifi_accno,
			t_heading_id, in_contra, in_tax, in_is_temp);

		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: public.account__save_tax(in_old_validto integer, in_taxmodule_id date, in_pass numeric, in_taxnumber numeric, in_maxvalue numeric, in_minvalue 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,
               minvalue = in_minvalue,
               maxvalue = in_maxvalue,
               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, minvalue, maxvalue, taxnumber,
                        pass, taxmodule_id)
         VALUES (in_chart_id, in_validto, in_rate, in_minvalue, in_maxvalue, 
                in_taxnumber, in_pass, in_taxmodule_id);

         RETURN TRUE;

END;

Function: public.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: public.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: public.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: public.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: public.account_heading_list()

Returns: SET OF account_heading

Language: SQL

Lists all existing account headings.

SELECT * FROM account_heading order by accno;

Function: public.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: public.add_custom_field(field_datatype character varying, new_field_name character varying, table_name character varying)

Returns: boolean

Language: PLPGSQL

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: public.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: public.admin__add_group_to_role(in_role_name text, in_group_name text)

Returns: boolean

Language: PLPGSQL

This function inserts the arguments into lsmb_group_grants for future reference and issues the db-level grant. It then returns true if there are no exceptions.

 
BEGIN
   PERFORM * FROM lsmb_group_grants 
     WHERE group_name = in_group_name AND
           granted_role = in_role_name;

 IF NOT FOUND THEN
   INSERT INTO lsmb_group_grants(group_name, granted_role) 
   VALUES (in_group_name, in_role_name);
 END IF;

   EXECUTE 'GRANT ' || quote_ident(in_role_name) || ' TO ' ||
           quote_literal('lsmb_' || t_dbname || '__' || in_group_name);
   RETURN TRUE;
END;

Function: public.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: public.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 '|| quote_ident('lsmb_' || t_dbname || '__' || in_group_name);
        execute stmt;
        INSERT INTO lsmb_group (role_name) 
             values (quote_literal('lsmb_' || t_dbname || '__' || in_group_name));
        return 1;
    END;
    

Function: public.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: public.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: public.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: public.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: public.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: public.admin__get_user(in_entity_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 entity_id = in_entity_id;
        return next a_user;
        return;
    
    END;    

Function: public.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: public.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: public.admin__list_group_grants(in_group_name text)

Returns: SET OF lsmb_group_grants

Language: SQL

SELECT * FROM lsmb_group_grants WHERE group_name = $1
ORDER BY granted_role;

Function: public.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: public.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: public.admin__remove_group_from_role(in_role_name text, in_group_name text)

Returns: boolean

Language: PLPGSQL

Returns true if the grant record was found and deleted, false otherwise. Issues db-level revoke in all cases.

BEGIN

   EXECUTE 'REVOKE ' || quote_ident(in_role_name) || ' FROM ' ||
           quote_literal('lsmb_' || t_dbname || '__' || in_group_name);

   DELETE FROM lsmb_group_grants 
    WHERE group_name = in_group_name AND granted_role = in_role_name;

   RETURN FOUND;

END;


Function: public.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: public.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 and in_import 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: public.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: public.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: public.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: public.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: public.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: public.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: public.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: public.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: public.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: public.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: public.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: public.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: public.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: public.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: public.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: public.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: public.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: public.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: public.asset_dep_straight_line_yr_d(in_report_id integer[], in_report_date date, in_asset_ids integer)

Returns: boolean

Language: SQL

     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, -- years
                  ai.usable_life - 
                  get_fractional_year(coalesce(max(report_date),
                                         start_depreciation,
                                         purchase_date),
                                       coalesce(start_depreciation,
                                         purchase_date)),
                  get_fractional_year(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: public.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: public.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: public.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: public.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: public.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: public.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.id IS NULL OR 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: public.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: public.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: public.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: public.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: public.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: public.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: public.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: public.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: public.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: public.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: public.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: public.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: public.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: public.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: public.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: public.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: public.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
	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: public.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: public.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: public.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: public.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: public.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: public.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 batch WHERE id = in_batch_id;
	DELETE FROM transactions WHERE id = ANY(t_transaction_ids);

	RETURN 1;
END;

Function: public.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: public.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: public.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: public.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: public.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: public.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: public.budget__approve(in_id integer)

Returns: budget_info_ext

Language: SQL

UPDATE budget_info 
   set approved_at = now(), approved_by = person__get_my_entity_id()
 WHERE id = $1;

SELECT budget__get_info($1);

Function: public.budget__get_business_units(in_id integer)

Returns: SET OF business_unit

Language: SQL

 select bu.*
     FROM business_unit bu
     JOIN budget_to_business_unit b2bu ON b2bu.bu_id = bu.id
     JOIN budget_info bi ON bi.id = b2bu.budget_id
    WHERE bi.id = $1
 ORDER BY bu.class_id;

Function: public.budget__get_details(in_id integer)

Returns: SET OF budget_line

Language: SQL

This retrieves the budget lines associated with a budget.

  SELECT * FROM budget_line where budget_id = $1;

Function: public.budget__get_info(in_id integer)

Returns: budget_info_ext

Language: SQL

Selects the budget info.

 
select bi.id, bi.start_date, bi.end_date, bi.reference, bi.description, 
       bi.entered_by, bi.approved_by, bi.obsolete_by, bi.entered_at, 
       bi.approved_at, bi.obsolete_at, 
       ee.name, ae.name, oe.name
  from budget_info bi
  JOIN entity ee ON bi.entered_by = ee.id
  LEFT JOIN entity ae ON bi.approved_by = ae.id
  LEFT JOIN entity oe ON bi.obsolete_by = oe.id
 where bi.id = $1; 

Function: public.budget__get_notes(in_id integer)

Returns: SET OF budget_note

Language: SQL

Returns all notes associated with a budget, by default in the order they were created.

 
SELECT * FROM budget_note WHERE ref_key = $1
 ORDER BY created;

Function: public.budget__mark_obsolete(in_id integer)

Returns: budget_info_ext

Language: SQL

Marks a budget as obsolete

UPDATE budget_info 
   set obsolete_by = person__get_my_entity_id(), obsolete_at = now()
 WHERE id = $1 and approved_by is not null;
SELECT budget__get_info($1)

Function: public.budget__reject(in_id integer)

Returns: boolean

Language: PLPGSQL

Deletes unapproved budgets only.

BEGIN

DELETE FROM budget_line 
 WHERE budget_id IN (SELECT id from budget_info 
                      WHERE id = in_id AND approved_by IS NULL);

DELETE FROM budget_to_project
 WHERE budget_id IN (SELECT id from budget_info 
                      WHERE id = in_id AND approved_by IS NULL);

DELETE FROM budget_to_department
 WHERE budget_id IN (SELECT id from budget_info 
                      WHERE id = in_id AND approved_by IS NULL);

DELETE FROM budget_info WHERE id = in_id AND approved_by IS NULL;

RETURN FOUND;
END;

Function: public.budget__save_details(in_details integer, in_id text[])

Returns: budget_info_ext

Language: PLPGSQL

This saves the line items for the budget. in_details is an array n long where each entry is {int account_id, text description, numeric amount}. The in_id parameter is the budget_id.

DECLARE
   loop_count int;
   retval budget_info_ext;
BEGIN
    FOR loop_count in   
        array_lower(in_details, 1) ..
        array_upper(in_details, 1)
    LOOP
        INSERT INTO budget_line 
                    (budget_id, 
                     account_id, 
                     description, 
                     amount)
             VALUES (in_id, 
                     in_details[loop_count][1]::int, 
                     in_details[loop_count][2], 
                     in_details[loop_count][3]::numeric);
    END LOOP;
    retval := budget__get_info(in_id);
    return retval;
END;

Function: public.budget__save_info(in_business_units integer, in_description date, in_reference date, in_end_date text, in_start_date text, in_id integer[])

Returns: budget_info_ext

Language: PLPGSQL

Saves the extended budget info passed through to the function. See the comment on type budget_info_ext for more information.

DECLARE 
   retval budget_info_ext;
   t_id int;
BEGIN

   PERFORM * FROM budget_info WHERE id = in_id and approved_by is not null;
   IF FOUND THEN
       RAISE EXCEPTION 'report approved';
   END IF;

  UPDATE budget_info
     SET start_date = in_start_date,
         end_date = in_end_date,
         reference = in_reference,
         description = in_description
   WHERE id = in_id and approved_by is null;
  IF FOUND THEN
      t_id := in_id;
  ELSE
       INSERT INTO budget_info (start_date, end_date, reference, description)
            VALUES (in_start_date, in_end_date, in_reference, in_description);
       t_id = currval('budget_info_id_seq');

       INSERT INTO budget_to_business_unit(budget_id, bu_id, bu_class)
       SELECT t_id, id, class_id
         FROM business_unit
        WHERE id = ANY(in_business_units);
  END IF;
  retval := budget__get_info(t_id);
  return retval;
END;

Function: public.budget__save_note(in_note integer, in_subject text, in_id text)

Returns: budget_note

Language: SQL

Saves a note attached to a budget.

INSERT INTO budget_note (subject, note, ref_key) 
     values ($2, $3, $1);

SELECT * FROM budget_note WHERE id = currval('note_id_seq'::regclass);

Function: public.budget__search(in_is_obsolete date, in_is_approved date, in_business_units date, in_obsolete_by text, in_approved_by text, in_entered_by integer, in_description integer, in_reference integer, in_includes_date integer[], in_end_date boolean, in_start_date boolean)

Returns: SET OF budget_info_ext

Language: SQL

This is a general search for budgets

select bi.id, bi.start_date, bi.end_date, bi.reference, bi.description, 
       bi.entered_by, bi.approved_by, bi.obsolete_by, bi.entered_at, 
       bi.approved_at, bi.obsolete_at, 
       ee.name, ae.name, oe.name
  from budget_info bi 
  JOIN entity ee ON bi.entered_by = ee.id
  LEFT JOIN entity ae ON bi.approved_by = ae.id
  LEFT JOIN entity oe ON bi.obsolete_by = oe.id
 WHERE (start_date = $1 or $1 is null) AND ($2 = end_date or $2 is null) 
       AND ($3 BETWEEN start_date AND end_date or $2 is null)
       AND ($4 ilike reference || '%' or $4 is null) 
       AND (bi.description @@ plainto_tsquery($5) or $5 is null) 
       AND ($6 = entered_by or $6 is null) 
       AND ($7 = approved_by or $7 is null) 
       AND ($8 = obsolete_by or $8 is null) 
       AND ($10 IS NULL OR ($10 = (approved_by IS NOT NULL)))
       AND ($11 IS NULL OR ($11 = (obsolete_by IS NOT NULL)))
 ORDER BY reference;

Function: public.budget__variance_report(in_id integer)

Returns: SET OF budget_variance_report

Language: SQL

Retrieves a variance report for budget with an id of in_id.

   WITH agg_account (amount, id, transdate)
        AS ( SELECT ac.amount *
                    CASE WHEN a.contra THEN -1 ELSE 1 END *
                    CASE WHEN a.category IN ('A', 'E') THEN -1 ELSE 1 END
                    AS amount,
                    ac.chart_id, ac.transdate
               FROM acc_trans ac
               JOIN account a ON ac.chart_id = a.id
           )
   SELECT act.accno, act.description, act.id, b.description, b.amount,
          coalesce(sum(a.amount), 0), 
          b.amount - coalesce(sum(a.amount), 0) AS variance
     FROM budget_info bi
     JOIN budget_line b ON bi.id = b.budget_id
     JOIN account act ON act.id = b.account_id
LEFT JOIN agg_account a ON a.transdate BETWEEN bi.start_date and bi.end_date
                           AND a.id = b.account_id
    WHERE bi.id = $1
 GROUP BY act.accno, act.description, act.id, b.description, b.amount
 ORDER BY act.accno;

Function: public.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: public.business_unit__get(in_id integer)

Returns: business_unit

Language: SQL

 SELECT * FROM business_unit where id = $1; 

Function: public.business_unit__get_tree_for(in_id integer)

Returns: SET OF business_unit_short

Language: SQL

This function returns tree-related records with the root of the tree being the business unit of in_id.

WITH RECURSIVE tree  (id, control_code, description,  start_date, end_date, 
                      parent_id, path, level)
AS (
   SELECT id, control_code, description, start_date, end_date, parent_id, 
          ARRAY[parent_id] AS path, 1 as level
     FROM business_unit WHERE $1 = id
    UNION
   SELECT t.id, t.control_code, t.description, t.start_date, t.end_date, 
          t.parent_id,   
          t.path || bu.id AS path, t.level + 1 as level
     FROM business_unit bu JOIN tree t ON t.parent_id = bu.id
)
SELECT * FROM tree ORDER BY path;

Function: public.business_unit__list_by_class(in_strict_credit integer, in_credit_id date, in_active_on integer, in_business_unit_class_id boolean)

Returns: SET OF business_unit

Language: PLPGSQL

This function retUrns a list of all units (projects, departments, funds, etc) active on the in_active_on date, where in_credit_id matches the credit id of the customer or vendor requested, and where in_business_uni_class_id is the class id of the class of business units (1 for department, 2 for project, etc). With the exception of in_business_unit_class_id, the null matches all records.

BEGIN
RETURN QUERY SELECT * FROM business_unit 
              WHERE (in_active_on BETWEEN coalesce(start_date, in_active_on) 
                                      AND coalesce(end_date, in_active_on) 
                      OR in_active_on IS NULL)
                    AND (in_credit_id = credit_id
                        OR (credit_id IS NULL and in_strict_credit IS NOT TRUE)
                        OR (in_credit_id IS NULL))
                    AND class_id = in_business_unit_class_id
           ORDER BY control_code;
END;

Function: public.business_unit__list_classes(in_module boolean, in_active text)

Returns: SET OF business_unit_class

Language: SQL

This function lists all business unit clases. If in_active is true, then only active classes are listed. If it is false then only inactive classes are listed. If it is null, then all classes are listed.


SELECT bc.* 
  FROM business_unit_class bc
 WHERE     (active = $1 OR $1 IS NULL)
       AND (id IN (select bu_class_id 
                     FROM bu_class_to_module bcm
                     JOIN lsmb_module mod ON mod.id = bcm.module_id
                    WHERE lower(label) = lower($2))
            OR $2 is null)
ORDER BY ordering;


Function: public.business_unit__save(in_credit_id integer, in_parent_id integer, in_end_date text, in_start_date text, in_description date, in_control_code date, in_class_id integer, in_id integer)

Returns: business_unit

Language: PLPGSQL

DECLARE retval business_unit;
        t_id int;

BEGIN

UPDATE business_unit
   SET class_id = in_class_id,
       control_code = in_control_code,
       description = in_description,
       start_date = in_start_date,
       end_date = in_end_date,
       credit_id = in_credit_id
 WHERE id = in_id;


IF FOUND THEN
   t_id := in_id;
ELSE
   INSERT INTO business_unit 
          (class_id, control_code, description, start_date, end_date, parent_id,
           credit_id)
   VALUES (in_class_id, in_control_code, in_description, in_start_date, 
           in_end_date, in_parent_id, in_credit_id);
    t_id := currval('business_unit_id_seq');
END IF;

SELECT * INTO retval FROM business_unit WHERE id = t_id;

RETURN retval;
END;

Function: public.business_unit_class__get_modules(in_id integer)

Returns: SET OF lsmb_module

Language: SQL

 SELECT * FROM lsmb_module 
    WHERE id IN (select module_id from bu_class_to_module where bu_class_id = $1)
 ORDER BY id;

Function: public.business_unit_class__save(in_ordering integer, in_active text, in_label boolean, in_id integer)

Returns: business_unit_class

Language: PLPGSQL

DECLARE retval business_unit_class;
        t_id int;
BEGIN

t_id := in_id;
UPDATE business_unit_class
   SET label = in_label,
       active = in_active,
       ordering = in_ordering
 WHERE id = in_id;

IF NOT FOUND THEN

   INSERT INTO business_unit_class (label, active, ordering)
   VALUES (in_label, in_active, in_ordering);

   t_id := currval('business_unit_class_id_seq');

END IF;

SELECT * INTO retval FROM business_unit_class WHERE id = t_id;

RETURN retval;

END;


Function: public.business_unit_class__save_modules(in_mod_ids integer, in_id integer[])

Returns: boolean

Language: SQL

DELETE FROM bu_class_to_module WHERE bu_class_id = $1;

INSERT INTO bu_class_to_module (bu_class_id, module_id)
SELECT $1, unnest
  FROM unnest($2);

SELECT true;

Function: public.business_unit_get(in_id integer)

Returns: business_unit

Language: SQL

 SELECT * FROM business_unit WHERE id = $1; 

Function: public.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: public.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: public.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: public.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: public.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: public.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))
                       AND not obsolete
              ORDER BY accno
	LOOP
		RETURN next out_row;
	END LOOP;
END;

Function: public.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: public.cogs__add_for_ap(in_lastcost integer, in_qty numeric, in_parts_id numeric)

Returns: numeric

Language: PLPGSQL

DECLARE t_alloc numeric := 0;
        t_cogs numeric := 0;
        t_inv invoice;
        t_cp account_checkpoint;
        t_ar ar;
        t_avail numeric;
BEGIN


IF in_qty > 0 THEN
   return cogs__reverse_ap(in_parts_id, in_qty * -1) * in_lastcost;
END IF;

SELECT * INTO t_cp FROM account_checkpoint ORDER BY end_date DESC LIMIT 1;

FOR t_inv IN
    SELECT i.*
      FROM invoice i
      JOIN ar a ON a.id = i.trans_id
     WHERE qty + allocated > 0 and parts_id  = in_parts_id
  ORDER BY a.transdate, a.id, i.id
LOOP
   t_avail := t_inv.qty + t_inv.allocated;
   SELECT * INTO t_ar FROM ar WHERE id = t_inv.trans_id;
   IF t_alloc < in_qty THEN
       RAISE EXCEPTION 'TOO MANY ALLOCATED';
   ELSIF t_alloc = in_qty THEN
       return t_alloc;
   ELSIF (in_qty + t_alloc) * -1 <=  t_avail  THEN
       UPDATE invoice SET allocated = allocated + (in_qty + t_alloc)
        WHERE id = t_inv.id;

       INSERT INTO acc_trans 
              (chart_id, transdate, amount, invoice_id, approved, trans_id)
       SELECT expense_accno_id, 
              CASE WHEN t_ar.transdate > t_cp.end_date THEN t_ar.transdate
                   ELSE t_cp.end_date + '1 day'::interval
               END, (in_qty + t_alloc) * in_lastcost, t_inv.id, true,
              t_inv.trans_id
         FROM parts 
       WHERE  id = t_inv.parts_id AND inventory_accno_id IS NOT NULL
              AND expense_accno_id IS NOT NULL
       UNION
       SELECT income_accno_id,
              CASE WHEN t_ar.transdate > t_cp.end_date THEN t_ar.transdate
                   ELSE t_cp.end_date + '1 day'::interval
               END, -1 * (in_qty + t_alloc) * in_lastcost, t_inv.id, true,
              t_inv.trans_id
         FROM parts 
       WHERE  id = t_inv.parts_id AND inventory_accno_id IS NOT NULL
              AND expense_accno_id IS NOT NULL;
                                    
       t_cogs := t_cogs + (in_qty + t_alloc) * in_lastcost;
       return in_qty * -1;
   ELSE
       UPDATE invoice SET allocated = qty * -1
        WHERE id = t_inv.id;
       t_cogs := t_cogs + t_avail * in_lastcost;

       INSERT INTO acc_trans
              (chart_id, transdate, amount, invoice_id, approved, trans_id)
       SELECT expense_accno_id,
              CASE WHEN t_ar.transdate > t_cp.end_date THEN t_ar.transdate
                   ELSE t_cp.end_date + '1 day'::interval
               END,  -1 * t_avail * in_lastcost, 
              t_inv.id, true, t_inv.trans_id
         FROM parts
       WHERE  id = t_inv.parts_id AND inventory_accno_id IS NOT NULL
              AND expense_accno_id IS NOT NULL
       UNION
       SELECT income_accno_id,
              CASE WHEN t_ar.transdate > t_cp.end_date THEN t_ar.transdate
                   ELSE t_cp.end_date + '1 day'::interval
               END, -t_avail * in_lastcost, t_inv.id, true, t_inv.trans_id
         FROM parts
       WHERE  id = t_inv.parts_id AND inventory_accno_id IS NOT NULL
              AND expense_accno_id IS NOT NULL;
       t_alloc := t_alloc + t_avail;
       t_cogs := t_cogs + t_avail * in_lastcost;
   END IF;


END LOOP;

RETURN t_alloc;
END;

Function: public.cogs__add_for_ap_line(in_invoice_id integer)

Returns: numeric

Language: PLPGSQL

DECLARE retval numeric;
        r_cogs numeric[];
        t_inv invoice;
        t_adj numeric;
        t_ap  ap;
BEGIN

SELECT * INTO t_inv FROM invoice 
 WHERE id = in_invoice_id;

IF t_inv.qty + t_inv.allocated = 0 THEN
   return 0;
END IF;

SELECT * INTO t_ap FROM ap WHERE id = t_inv.trans_id;

IF t_inv.qty < 0 THEN -- normal COGS

    SELECT cogs__add_for_ap(i.parts_id, i.qty + i.allocated, i.sellprice) 
      INTO retval
      FROM invoice i
      JOIN parts p ON p.id = i.parts_id
     WHERE i.id = $1;

    UPDATE invoice 
       SET allocated = allocated + retval
     WHERE id = $1;
ELSE -- reversal

   r_cogs := cogs__reverse_ap(t_inv.parts_id, t_inv.qty + t_inv.allocated);

   UPDATE invoice
      SET allocated = allocated + r_cogs[1]
    WHERE id = in_invoice_id;

   t_adj := t_inv.sellprice * r_cogs[1] + r_cogs[2];

   INSERT INTO acc_trans 
          (chart_id, trans_id, approved,  amount, transdate, invoice_id)
   SELECT p.inventory_accno_id, t_inv.trans_id, true, t_adj, t_ap.transdate, 
          in_invoice_id
     FROM parts p
    WHERE id = t_inv.parts_id
    UNION
   SELECT p.expense_accno_id, t_inv.trans_id, true, t_adj * -1, t_ap.transdate,
          in_invoice_id
     FROM parts p
    WHERE id = t_inv.parts_id;
   retval := r_cogs[1];
   raise notice 'cogs reversal returned %', r_cogs;

END IF;

RETURN retval;

END;


Function: public.cogs__add_for_ar(in_qty integer, in_parts_id numeric)

Returns: numeric[]

Language: PLPGSQL

This function accepts a parts_id and a quantity, and iterates through AP records in order, calculating COGS on a FIFO basis and returning it to the application to attach to the current transaction. Return values are an array of {allocated, cogs}.

DECLARE t_alloc numeric := 0;
        t_cogs numeric := 0;
        t_inv invoice;
        t_avail numeric;
BEGIN


FOR t_inv IN
    SELECT i.*
      FROM invoice i
      JOIN (select id, approved, transdate from ap
             union
            select id, approved, transdate from gl) a ON a.id = i.trans_id
     WHERE qty + allocated < 0 AND i.parts_id = in_parts_id
  ORDER BY a.transdate asc, a.id asc, i.id asc
LOOP
   t_avail := (t_inv.qty + t_inv.allocated) * -1;
   IF t_alloc > in_qty THEN
       RAISE EXCEPTION 'TOO MANY ALLOCATED';
   ELSIF t_alloc = in_qty THEN
       return ARRAY[t_alloc, t_cogs];
   ELSIF (in_qty + t_alloc) <= t_avail THEN
       UPDATE invoice SET allocated = allocated + (in_qty - t_alloc)
        WHERE id = t_inv.id;
       t_cogs := t_cogs + (in_qty - t_alloc) * t_inv.sellprice;
       t_alloc := in_qty;
       return ARRAY[t_alloc, t_cogs];
   ELSE
       UPDATE invoice SET allocated = qty * -1
        WHERE id = t_inv.id;
       t_cogs := t_cogs + (t_avail * t_inv.sellprice);
       t_alloc := t_alloc + t_avail;
   END IF;
END LOOP;

RETURN ARRAY[t_alloc, t_cogs];

END;

Function: public.cogs__add_for_ar_line(in_invoice_id integer)

Returns: numeric

Language: PLPGSQL

DECLARE 
   t_cogs numeric[];
   t_inv invoice;
   t_part parts;
   t_ar ar;
   t_transdate date;
BEGIN

SELECT * INTO t_inv FROM invoice WHERE id = in_invoice_id;
SELECT * INTO t_part FROM parts WHERE id = t_inv.parts_id;
SELECT * INTO t_ar FROM ar WHERE id = t_inv.trans_id;

IF t_inv.qty + t_inv.allocated = 0 THEN
   return 0;
END IF;

IF t_inv.qty > 0 THEN 
   t_cogs := cogs__add_for_ar(t_inv.parts_id, t_inv.qty + t_inv.allocated);
ELSE
   t_cogs := cogs__reverse_ar(t_inv.parts_id, t_inv.qty + t_inv.allocated);
END IF;


UPDATE invoice set allocated = allocated - t_cogs[1]
 WHERE id = in_invoice_id;

SELECT CASE WHEN t_ar.transdate > max(end_date) THEN t_ar.transdate
            ELSE max(end_date) + '1 day'::interval
        END INTO t_transdate
  from account_checkpoint td; 
INSERT INTO acc_trans 
       (trans_id, chart_id, approved, amount, transdate,  invoice_id)
VALUES (t_inv.trans_id, CASE WHEN t_inv.qty < 0 AND t_ar.is_return 
                           THEN t_part.returns_accno_id
                           ELSE t_part.expense_accno_id
                      END, TRUE, t_cogs[2] * -1, t_transdate, t_inv.id),
       (t_inv.trans_id, t_part.inventory_accno_id, TRUE, t_cogs[2], 
       t_transdate, t_inv.id);

RETURN t_cogs[1];

END;


Function: public.cogs__reverse_ap(in_qty integer, in_parts_id numeric)

Returns: numeric[]

Language: PLPGSQL

This function iterates through invoice rows attached to ap transactions and allocates them on a first-in first-out basis. The sort of pseudo-"COGS" value is returned to the application for further handling.

DECLARE t_alloc numeric :=0;
        t_inv invoice;
        t_cogs numeric :=0;
        retval numeric[];
BEGIN
RAISE NOTICE 'reversing AP: parts_id %, qty %', in_parts_id, in_qty;

FOR t_inv IN
    SELECT i.*
      FROM invoice i
      JOIN ap a ON a.id = i.trans_id
     WHERE qty + allocated < 0 AND parts_id = in_parts_id
  ORDER BY a.transdate, a.id, i.id
LOOP
   RAISE NOTICE 'id %, avail %, allocated %, requesting %', t_inv.id, t_inv.qty + t_inv.allocated, t_alloc, in_qty - t_alloc;
   IF t_alloc > in_qty THEN
       RAISE EXCEPTION 'TOO MANY ALLOCATED';
   ELSIF t_alloc = in_qty THEN
       return ARRAY[t_alloc, t_cogs];
   ELSIF (in_qty - t_alloc) <= -1 * (t_inv.qty + t_inv.allocated) THEN
       raise notice 'partial reversal';
       UPDATE invoice SET allocated = allocated + (in_qty - t_alloc)
        WHERE id = t_inv.id;
       return ARRAY[in_qty * -1, t_cogs + (in_qty - t_alloc) * t_inv.sellprice];
   ELSE
       raise notice 'total reversal';
       UPDATE invoice SET allocated = qty * -1
        WHERE id = t_inv.id;
       t_alloc := t_alloc - (t_inv.qty + t_inv.allocated);
       t_cogs := t_cogs - (t_inv.qty + t_inv.allocated) * t_inv.sellprice;
   END IF;
END LOOP;

RETURN ARRAY[t_alloc, t_cogs];

RAISE EXCEPTION 'TOO FEW TO ALLOCATE';
END;

Function: public.cogs__reverse_ar(in_qty integer, in_parts_id numeric)

Returns: numeric[]

Language: PLPGSQL

This function accepts a part id and quantity to reverse. It then iterates backwards over AP related records, calculating COGS. This does not save COGS but rather returns it to the application to save. Return values are an array of {allocated, cogs}.

DECLARE t_alloc numeric := 0;
        t_cogs numeric := 0;
        t_inv invoice;
        t_avail numeric;
BEGIN

FOR t_inv IN
    SELECT i.*
      FROM invoice i
      JOIN (select id, approved, transdate from ap 
            union
            select id, approved, transdate from gl) a ON a.id = i.trans_id
     WHERE allocated > 0 and a.approved and parts_id = in_parts_id
  ORDER BY a.transdate DESC, a.id DESC, i.id DESC
LOOP
   t_avail := t_inv.allocated;
   IF t_alloc < in_qty THEN
       RAISE EXCEPTION 'TOO MANY ALLOCATED';
   ELSIF t_alloc = in_qty THEN
       RETURN ARRAY[t_alloc, t_cogs];
   ELSIF (in_qty - t_alloc) * -1 <=  t_inv.allocated THEN
       raise notice 'partial reversal';
       UPDATE invoice SET allocated = allocated + (in_qty - t_alloc)
        WHERE id = t_inv.id;
       t_cogs := t_cogs +  (in_qty - t_alloc) * t_inv.sellprice;
       return ARRAY[t_alloc + (in_qty - t_alloc), t_cogs];
   ELSE
       raise notice 'full reversal';
       UPDATE invoice SET allocated = 0
        WHERE id = t_inv.id;
       t_alloc := t_alloc + t_inv.allocated * -1;
       t_cogs := t_cogs + -1 * (t_inv.allocated) * t_inv.sellprice;
   END IF;
END LOOP;

RAISE EXCEPTION 'TOO FEW TO REVERSE';

END;

Function: public.company__get(in_entity_id integer)

Returns: company_entity

Language: SQL

Returns all attributes for the company attached to the entity.

	SELECT c.entity_id, e.entity_class, c.legal_name, c.tax_id, c.sales_tax_id,
               c.license_number, c.sic_code, e.control_code, e.country_id 
          FROM company c
          JOIN entity e ON e.id = c.entity_id
         WHERE entity_id = $1;

Function: public.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: public.company__get_by_cc(in_control_code text)

Returns: company_entity

Language: SQL

Returns the entity/company row attached to the control code.

        SELECT c.entity_id, e.entity_class, c.legal_name, c.tax_id, c.sales_tax_id,
               c.license_number, c.sic_code, e.control_code, e.country_id
          FROM company c
          JOIN entity e ON e.id = c.entity_id
         WHERE e.control_code = $1;

Function: public.company__next_id()

Returns: bigint

Language: SQL

    
    select nextval('company_id_seq');
    

Function: public.company__save(in_license_number integer, in_sales_tax_id text, in_country_id integer, in_sic_code text, in_entity_id text, in_tax_id integer, in_legal_name text, in_entity_class integer, in_control_code text, in_id text)

Returns: company

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;
        t_retval COMPANY;
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_legal_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_legal_name, in_entity_class, t_control_code,in_country_id);
		t_entity_id := currval('entity_id_seq');
	END IF;

	UPDATE company
	SET legal_name = in_legal_name,
		tax_id = in_tax_id,
		sic_code = in_sic_code,
                sales_tax_id = in_sales_tax_id,
                license_number = in_license_number
	WHERE id = t_company_id;


	IF NOT FOUND THEN
		INSERT INTO company(entity_id, legal_name, tax_id, sic_code,
                                    sales_tax_id, license_number)
		VALUES (t_entity_id, in_legal_name, in_tax_id, in_sic_code, 
                        in_sales_tax_id, in_license_number);

	END IF;
        SELECT * INTO t_retval FROM company WHERE entity_id = t_entity_id;
        RETURN t_retval;	
END;

Function: public.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 (select legal_name, tax_id, entity_id 
                FROM company
               UNION ALL
              SELECT last_name || ', ' || first_name, null, entity_id 
                FROM person) 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: public.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: public.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: public.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: public.contact__search(in_notes integer, in_control_code text, in_name_part text[], in_business_id text, in_active_date_to text, in_active_date_from text, in_country text, in_mail_code text, in_state text, in_city date, in_address date, in_meta_number integer, in_contact_info text, in_contact text, in_entity_class text)

Returns: SET OF contact_search_result

Language: PLPGSQL

DECLARE
	out_row contact_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, 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 control_code like in_control_code || '%'
                      union
                      select * from entity where in_control_code is null) e
		JOIN (SELECT legal_name, sic_code, entity_id 
                        FROM company 
                       WHERE legal_name @@ plainto_tsquery(in_name_part)
                      UNION ALL
                      SELECT legal_name, sic_code, entity_id
                        FROM company
                       WHERE in_name_part IS NULL
                      UNION ALL
                     SELECT coalesce(first_name, '') || ' ' 
                            || coalesce(middle_name, '') || ' ' 
                            || coalesce(last_name, ''), null, entity_id
                       FROM person
                      WHERE coalesce(first_name, '') || ' ' 
                            || coalesce(middle_name, '') || ' '
                            || coalesce(last_name, '') 
                             @@ plainto_tsquery(in_name_part)
                      UNION ALL
                     SELECT coalesce(first_name, '') || ' ' 
                            || coalesce(middle_name, '') || ' ' 
                            || coalesce(last_name, ''), null, entity_id
                       FROM person
                       WHERE in_name_part IS NULL) c ON (e.id = c.entity_id)
		LEFT JOIN entity_credit_account ec ON (ec.entity_id = e.id)
		LEFT JOIN business b ON (ec.business_id = b.id)
		WHERE coalesce(ec.entity_class,e.entity_class) = in_entity_class
			AND (c.entity_id IN (select entity_id 
                                               FROM entity_to_contact
                                              WHERE contact ILIKE 
                                                            ANY(t_contact_info))
				                    OR '' ILIKE 
                                                          ALL(t_contact_info)
                                                    OR t_contact_info IS NULL)
			
			AND ((in_address IS NULL AND in_city IS NULL 
					AND in_state IS NULL 
					AND in_country IS NULL)
				OR (c.entity_id IN 
				(select entity_id FROM entity_to_location
				WHERE location_id IN 
					(SELECT id FROM location
					WHERE (line_one @@ plainto_tsquery(
                                                              in_address)
                                               OR
					       line_two @@ plainto_tsquery(
                                                              in_address)
                                               OR
					       line_three @@ plainto_tsquery(
                                                              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_active_date_to, 
						ec.startdate)
				OR (ec.startdate IS NULL))
			AND (ec.enddate >= coalesce(in_active_date_from, ec.enddate)
				OR (ec.enddate IS NULL))
	 		AND (ec.meta_number like in_meta_number || '%'
			     OR in_meta_number IS NULL)
                        AND (in_notes IS NULL OR e.id in (
                                     SELECT entity_id from entity_note
                                      WHERE note @@ plainto_tsquery(in_notes))
                                  OR ec.id IN (select ref_key FROM eca_note
                                     WHERE note @@ plainto_tsquery(in_notes)))
	LOOP
		RETURN NEXT out_row;
	END LOOP;
END;

Function: public.contact_class__list()

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: public.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: public.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: public.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 = 2 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: public.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: public.date1()

Returns: date

Language: SQL

SELECT (extract('YEAR' from now())|| '-12-01')::date;

Function: public.date2()

Returns: date

Language: SQL

SELECT ((extract('YEAR' from now())|| '-12-01')::date
        + '1 year'::interval)::date;

Function: public.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: public.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: public.deduction__list_for_entity(in_entity_id integer)

Returns: SET OF payroll_deduction

Language: SQL

SELECT * FROM payroll_deduction WHERE entity_id = $1;

Function: public.deduction__list_types(in_country_id integer)

Returns: SET OF payroll_deduction_type

Language: SQL

 
SELECT * FROM payroll_deduction_type where country_id = $1

Function: public.deduction__save(in_type_id numeric, in_entity_id integer, in_rate integer)

Returns: SET OF payroll_deduction

Language: PLPGSQL

 
BEGIN

UPDATE payroll_deduction
   SET rate = in_rate
 WHERE entity_id = in_entity_id and in_type_id;


IF NOT FOUND THEN
    INSERT INTO payroll_deduction (entity_id, type_id, rate)
    VALUES (in_entity_id, in_type_id, in_rate);
END IF;
  
RETURN QUERY SELECT * FROM payroll_deduction
             WHERE entity_id = in_entity_id and in_type_id;
END;

Function: public.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: public.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: public.del_yearend()

Returns: trigger

Language: PLPGSQL

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

Function: public.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.invoice, 
                       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, false as invoice,
                                approved from gl
			WHERE lower(in_type) = 'gl'
			UNION
			SELECT id, transdate, invnumber as reference, 
				(SELECT name FROM eca__get_entity(entity_credit_account)),
				invoice, approved from ap
			WHERE lower(in_type) = 'ap'
			UNION
			SELECT id, transdate, invnumber as reference,
				description, 
				invoice, 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 and charttype = 'A')
           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, trans.invoice
		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: public.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
                PERFORM cogs__add_for_ar_line(id) FROM invoice 
                  WHERE trans_id = in_id;
		UPDATE ar set approved = true where id = in_id;
	ELSIF (t_table = 'ap') THEN
                PERFORM cogs__add_for_ap_line(id) FROM invoice 
                  WHERE trans_id = in_id;
		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: public.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: public.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: public.eca__delete_contact(in_contact integer, in_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_class_id
       and contact= in_contact;
RETURN FOUND;

END;


Function: public.eca__delete_location(in_location_class integer, in_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_id 
       AND location_class = in_location_class;

RETURN FOUND;

END;

Function: public.eca__delete_pricematrix(in_entry_id integer, in_credit_id integer)

Returns: boolean

Language: PLPGSQL

DECLARE retval bool;

BEGIN

retval := false;

DELETE FROM partsvendor 
 WHERE entry_id = in_entry_id 
       AND credit_id = in_credit_id;

retval := FOUND;

DELETE FROM partscustomer
 WHERE entry_id = in_entry_id
       AND credit_id = in_credit_id;

RETURN FOUND or retval;

END;

Function: public.eca__get_by_meta_number(in_entity_class text, in_meta_number integer)

Returns: entity_credit_account

Language: SQL

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

Function: public.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: public.eca__get_pricematrix(in_credit_id integer)

Returns: SET OF eca__pricematrix

Language: SQL

This returns the pricematrix for the customer or vendor (entity_credit_account identified by in_id), orderd by partnumber, validfrom


SELECT pc.parts_id, p.partnumber, p.description, pc.credit_id, pc.pricebreak,
       pc.sellprice, NULL, NULL::int, NULL, pc.validfrom, pc.validto, pc.curr,
       pc.entry_id
  FROM partscustomer pc
  JOIN parts p on pc.parts_id = p.id
  JOIN entity_credit_account eca ON pc.credit_id = eca.id
 WHERE pc.credit_id = $1 AND eca.entity_class = 2
 UNION
SELECT pv.parts_id, p.partnumber, p.description, pv.credit_id, NULL, NULL,
       pv.lastcost, pv.leadtime::int, pv.partnumber, NULL, NULL, pv.curr, 
       pv.entry_id
  FROM partsvendor pv
  JOIN parts p on pv.parts_id = p.id
  JOIN entity_credit_account eca ON pv.credit_id = eca.id
 WHERE pv.credit_id = $1 and eca.entity_class = 1
 ORDER BY partnumber, validfrom


Function: public.eca__get_pricematrix_by_pricegroup(in_credit_id integer)

Returns: SET OF eca__pricematrix

Language: SQL

SELECT pc.parts_id, p.partnumber, p.description, pc.credit_id, pc.pricebreak,
       pc.sellprice, NULL::numeric, NULL::int, NULL::text, pc.validfrom, 
       pc.validto, pc.curr, pc.entry_id
  FROM partscustomer pc
  JOIN parts p on pc.parts_id = p.id
  JOIN entity_credit_account eca ON pc.pricegroup_id = eca.pricegroup_id
 WHERE eca.id = $1 AND eca.entity_class = 2

Function: public.eca__get_taxes(in_id integer)

Returns: SET OF eca_tax

Language: SQL

Returns a set of taxable account id's.

select * from eca_tax where eca_id = $1;

Function: public.eca__history(in_inc_closed text, in_inc_open text, in_entity_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, null::int as project_id, null::text as projectnumber,
            i.serialnumber, 
            case when $16 = 1 then ex.buy else ex.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, notes
             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, notes
             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, notes
           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, notes
           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, notes
           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, notes
           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 id, trans_id, parts_id, qty, description, unit, discount,
                   deliverydate, serialnumber, sellprice
             FROM  invoice where $13 = 'i'
            union 
            select id, trans_id, parts_id, qty, description, unit, discount,
                   reqdate, serialnumber, 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 entity ee ON (a.person_id = ee.id)
LEFT JOIN person ep ON (ep.entity_id = ee.id)
    -- 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)
--          and (a.notes @@ plainto_tsquery($9) or $9 is null)
 ORDER BY eca.meta_number;

Function: public.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: public.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: public.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: public.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: public.eca__location_save(in_old_location_class integer, 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_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_id;
           
         IF FOUND THEN
            SELECT location_save(
                in_id, 
                in_line_one, 
                in_line_two, 
                in_line_three, 
                in_city,
                in_state, 
                in_mail_code, 
                in_country_id
            )
        	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_id
            )
        	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: public.eca__save(in_discount_account_id integer, in_taxform_id integer, in_pay_to_name integer, in_cash_account_id text, in_ar_ap_account_id numeric, in_threshold boolean, in_enddate numeric, in_startdate integer, in_curr integer, in_pricegroup_id character varying, in_language_code integer, in_business_id character varying, in_meta_number integer, in_terms bpchar, in_discount_terms date, in_creditlimit date, in_taxincluded numeric, in_discount integer, in_description integer, in_entity_id text, in_entity_class integer, in_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,
                discount_account_id = in_discount_account_id,
                meta_number = t_meta_number,
                business_id = in_business_id,
                language_code = in_language_code,
                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_id;
        
         IF FOUND THEN
            RETURN in_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,
                discount_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_code,
                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,
                in_discount_account_id
            );
            RETURN currval('entity_credit_account_id_seq');
       END IF;

    END;
    

Function: public.eca__save_contact(in_old_contact_class integer, in_old_contact integer, in_contact text, in_description text, in_class_id 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_class_id
         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_class_id, in_description, in_contact);
        
    END IF;

	RETURN 1;
END;

Function: public.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: public.eca__save_pricematrix(in_entry_id integer, in_curr integer, in_validto numeric, in_validfrom numeric, in_partnumber smallint, in_lead_time text, in_price date, in_pricebreak date, in_credit_id bpchar, in_parts_id integer)

Returns: eca__pricematrix

Language: PLPGSQL

DECLARE 
   retval eca__pricematrix;
   t_insert bool;

BEGIN

t_insert := false;

PERFORM * FROM entity_credit_account 
  WHERE id = in_credit_id AND entity_class = 1;

IF FOUND THEN -- VENDOR
    UPDATE partsvendor
       SET lastcost = in_price,
           leadtime = in_lead_time,
           partnumber = in_partnumber,
           curr = in_curr
     WHERE credit_id = in_credit_id AND entry_id = in_entry_id;

    IF NOT FOUND THEN
        INSERT INTO partsvendor
               (parts_id, credit_id, lastcost, leadtime, partnumber, curr)
        VALUES (in_parts_id, in_credit_id, in_price, in_leadtime::int2, 
               in_partnumber, in_curr);
    END IF;

    SELECT pv.parts_id, p.partnumber, p.description, pv.credit_id, NULL, NULL,
           pv.lastcost, pv.leadtime::int, pv.partnumber, NULL, NULL, pv.curr, 
           pv.entry_id
      INTO retval
      FROM partsvendor pv
      JOIN parts p ON p.id = pv.parts_id
     WHERE parts_id = in_parts_id and credit_id = in_credit_id;

    RETURN retval;
END IF;

PERFORM * FROM entity_credit_account
  WHERE id = in_credit_id AND entity_class = 2;

IF FOUND THEN -- CUSTOMER
    UPDATE partscustomer
       SET pricebreak = in_pricebreak,
           sellprice  = in_price,
           validfrom  = in_validfrom,
           validto    = in_validto,
           curr       = in_curr
     WHERE entry_id = in_entry_id and credit_id = in_credit_id;

    IF NOT FOUND THEN
        INSERT INTO partscustomer
               (parts_id, credit_id, sellprice, validfrom, validto, curr)
        VALUES (in_parts_id, in_credit_id, in_price, in_validfrom, in_validto, 
                in_curr);

        t_insert := true;
    END IF;

    SELECT pc.parts_id, p.partnumber, p.description, pc.credit_id, 
           pc.pricebreak, pc.sellprice, NULL, NULL, NULL, pc.validfrom, 
           pc.validto, pc.curr, pc.entry_id
      INTO retval
      FROM partscustomer pc
      JOIN parts p on pc.parts_id = p.id
     WHERE entry_id = CASE WHEN t_insert 
                           THEN currval('partscustomer_entry_id_seq') 
                           ELSE in_entry_id 
                      END;
                           
    RETURN retval;

END IF;

RAISE EXCEPTION 'No valid entity credit account found';
   
END;

Function: public.eca__set_taxes(in_tax_ids integer, in_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
     IF in_tax_ids = '{}' THEN
         RETURN NULL;
     END IF;
     SELECT * FROM entity_credit_account into eca WHERE id = in_id;

     DELETE FROM eca_tax WHERE eca_id = in_id;
     FOR iter in array_lower(in_tax_ids, 1) .. array_upper(in_tax_ids, 1)
     LOOP
          INSERT INTO eca_tax (eca_id, chart_id)
          values (in_id, in_tax_ids[iter]);
     END LOOP;
     RETURN TRUE;
end;

Function: public.eca_bu_trigger()

Returns: trigger

Language: PLPGSQL

BEGIN
  IF TG_OP = 'INSERT' THEN
      INSERT INTO business_unit(class_id, description, credit_id)
      VALUES (7 - NEW.entity_class, NEW.meta_number, NEW.id);
  ELSIF TG_OP = 'UPDATE' THEN
      IF new.meta_number <> old.meta_number THEN
         UPDATE business_unit SET description = new.meta_number
          WHERE class_id = 7 - NEW.entity_class
                AND credit_id = new.id;
      END IF;
  ELSIF TG_OP = 'DELETE'THEN
      DELETE FROM business_unit WHERE class_id = 7 - NEW.entity_class
                  AND credit_id = old_id;
      RETURN OLD;
  END IF;
  RETURN NEW;
END;

Function: public.employee__all_managers()

Returns: SET OF employee_result

Language: SQL

   SELECT p.entity_id, e.control_code, p.id, s.salutation, s.id,
          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, e.country_id
     FROM person p
     JOIN entity_employee ee on (ee.entity_id = p.entity_id)
     JOIN entity e ON (p.entity_id = e.id)
LEFT JOIN salutation s on (p.salutation_id = s.id)
LEFT JOIN person mp ON ee.manager_id = p.entity_id
    WHERE ee.role = 'manager'
 ORDER BY ee.employeenumber;

Function: public.employee__all_salespeople()

Returns: SET OF employee_result

Language: SQL

   SELECT p.entity_id, e.control_code, p.id, s.salutation, s.id,
          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, e.country_id
     FROM person p
     JOIN entity_employee ee on (ee.entity_id = p.entity_id)
     JOIN entity e ON (p.entity_id = e.id)
LEFT JOIN salutation s on (p.salutation_id = s.id)
LEFT JOIN person mp ON ee.manager_id = p.entity_id
    WHERE ee.sales
 ORDER BY ee.employeenumber;

Function: public.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, e.control_code, p.id, s.salutation, s.id,
          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, e.country_id
     FROM person p
     JOIN entity_employee ee on (ee.entity_id = p.entity_id)
     JOIN entity e ON (p.entity_id = e.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: public.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: public.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: public.employee__save(in_employeenumber 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_employeenumber
	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_employeenumber, 
                                in_entity_id);
		RETURN in_entity_id;
	END IF;
        RETURN out_id;
END;

Function: public.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, e.control_code, p.id, s.salutation, s.id, 
          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, e.country_id
     FROM person p
     JOIN entity e ON p.entity_id = e.id
     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: public.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: public.employee_set_location(in_location integer, in_employee integer)

Returns: void

Language: SQL


    INSERT INTO entity_to_location (entity_id,location_id) 
    SELECT entity_id, $2
      FROM person WHERE id = $1;

Function: public.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: public.entity__delete_contact(in_contact integer, in_class_id integer, in_entity_id text)

Returns: boolean

Language: PLPGSQL

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

BEGIN

DELETE FROM entity_to_contact
 WHERE entity_id = in_entity_id
       and contact_class_id = in_class_id
       and contact= in_contact;
RETURN FOUND;

END;


Function: public.entity__delete_location(in_location_class integer, in_id integer, in_entity_id integer)

Returns: boolean

Language: PLPGSQL

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

BEGIN

DELETE FROM entity_to_location
 WHERE entity_id = in_entity_id AND location_id = in_id 
       AND location_class = in_location_class;

RETURN FOUND;

END;

Function: public.entity__get(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: public.entity__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: public.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
             LEFT JOIN defaults ON setting_key = 'roll_prefix'
		WHERE active and pg_has_role(SESSION_USER, 
                                     coalesce(defaults.value, 
                                     'lsmb_' || current_database() || '__') ||
                                     'contact_class_' ||
                                     lower(regexp_replace(class, ' ', '_')), 
                                     'USAGE')
		ORDER BY id
	LOOP
		RETURN NEXT out_row;
	END LOOP;
END;

Function: public.entity__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 entity_to_contact c
		JOIN contact_class cl ON (c.contact_class_id = cl.id)
		WHERE c.entity_id = in_entity_id
	LOOP
		return next out_row;
	END LOOP;
END;

Function: public.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  ec.id, e.id, ec.entity_class, ec.discount, 
                        ec.discount_terms,
			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.discount_account_id,
			ec.threshold, e.control_code, ec.id, ec.pay_to_name,
                        ec.taxform_id
		FROM entity e 
		JOIN entity_credit_account ec ON (e.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: public.entity__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 entity_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.entity_id = in_entity_id
		ORDER BY lc.id, l.id, c.name
	LOOP
		RETURN NEXT out_row;
	END LOOP;
END;

Function: public.entity__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: public.entity__location_save(in_created integer, in_country_id integer, in_mail_code integer, in_state text, in_city text, in_line_two text, in_line_one text, in_location_class text, in_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_id,
        in_location_class, in_line_one, in_line_two, 
        '', in_city , in_state, in_mail_code, in_country_id);
    END;


Function: public.entity__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: public.entity__save_contact(in_old_class_id integer, in_old_contact integer, in_contact text, in_description text, in_class_id text, in_entity_id integer)

Returns: integer

Language: PLPGSQL

Saves company contact information. The return value is meaningless.

DECLARE out_id int;
BEGIN
        DELETE FROM entity_to_contact 
         WHERE entity_id = in_entity_id AND contact = in_old_contact 
               AND contact_class_id = in_old_class_id;

	INSERT INTO entity_to_contact 
               (entity_id, contact_class_id, description, contact)
	VALUES (in_entity_id, in_class_id, in_description, in_contact);

	RETURN 1;
END;

Function: public.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: public.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: public.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: public.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: public.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: public.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: public.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: public.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: public.eoy_reopen_books(in_end_date date)

Returns: boolean

Language: PLPGSQL

Removes checkpoints and reverses yearend transactions 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: public.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')
                      OR acc.category = 'Q' AND acc.is_temp)
	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: public.file__attach_to_eca(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 entity credit accounts.

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_eca
                   (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: public.file__attach_to_entity(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 contact or entity. 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 entities

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_entity
                   (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: public.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_order
                   (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: public.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: public.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: public.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: public.file__get_for_template(in_file_class integer, in_ref_key integer)

Returns: SET OF file_list_item

Language: SQL

 

SELECT m.mime_type, CASE WHEN f.file_class = 3 THEN ref_key ||'-'|| f.file_name
                         ELSE f.file_name END, 
       f.description, f.uploaded_by, e.name, 
       f.uploaded_at, f.id, f.ref_key, f.file_class,  f.content
  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
       AND m.invoice_include 
       OR f.id IN (SELECT max(fb.id) 
                   FROM file_base fb
                   JOIN mime_type m ON fb.mime_type_id = m.id
                        AND m.mime_type ilike 'image%'
                   JOIN invoice i ON i.trans_id = $1
                        AND i.parts_id = fb.ref_key
                  WHERE fb.file_class = 3)

Function: public.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: public.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: public.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: public.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: public.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: public.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: public.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: public.get_default_lang()

Returns: text

Language: SQL

 SELECT coalesce((select description FROM language 
    WHERE code = (SELECT substring(value, 1, 2) FROM defaults
                   WHERE setting_key = 'default_language')), 'english');

Function: public.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: public.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: public.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: public.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,tablename,reference, action, person_id)
values (t_row.id,TG_RELNAME,t_reference, TG_OP, person__get_my_entity_id());

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

Function: public.in_tree(in_search_array integer, in_node_id public.tree_record[])

Returns: boolean

Language: SQL

SELECT CASE WHEN count(*) > 0 THEN true ELSE false END
  FROM unnest($2) r
 WHERE t @> array[$1];

Function: public.in_tree(in_search_array integer[], in_node_id public.tree_record[])

Returns: boolean

Language: SQL

SELECT bool_and(in_tree(e, $2))
  FROM unnest($1) e;

Function: public.inventory_adj__details(in_id integer)

Returns: SET OF inventory_adjustment_line

Language: SQL

 

   SELECT l.parts_id, p.partnumber, p.description, l.counted, l.expected, 
          l.counted - l.expected
     FROM inventory_report_line l
     JOIN parts p ON l.parts_id = p.id
    WHERE l.report_id = $1;


Function: public.inventory_adj__get(in_id integer)

Returns: SET OF inventory_adjustment_info

Language: SQL


   SELECT r.id, r.transdate, r.source, r.ar_trans_id, r.ap_trans_id,
          ar.invnumber, ap.invnumber
     FROM inventory_report r
     JOIN inventory_report_line l ON l.report_id = r.id 
LEFT JOIN ar ON ar.id = r.ar_trans_id
LEFT JOIN ap ON ap.id = r.ap_trans_id
    WHERE r.id = $1;


Function: public.inventory_adj__search(in_source date, in_partnumber date, in_to_date text, in_from_date text)

Returns: SET OF inventory_adjustment_info

Language: SQL


   SELECT r.id, r.transdate, r.source, r.ar_trans_id, r.ap_trans_id,
          ar.invnumber, ap.invnumber
     FROM inventory_report r
     JOIN inventory_report_line l ON l.report_id = r.id 
     JOIN parts p ON l.parts_id = p.id
LEFT JOIN ar ON ar.id = r.ar_trans_id
LEFT JOIN ap ON ap.id = r.ap_trans_id
    WHERE ($1 is null or $1 <= r.transdate) AND
          ($2 is null OR $2 >= r.transdate) AND
          ($3 IS NULL OR plainto_tsquery($3) @@ tsvector(p.partnumber)) AND
          ($4 IS NULL OR source LIKE $4 || '%');
 

Function: public.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: public.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: public.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: public.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: public.je_get_default_lines()

Returns: character varying

Language: SQL

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

Function: public.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: public.journal__add(in_is_template text, in_approved text, in_transaction_date integer, in_entry_type date, in_description boolean, in_source boolean)

Returns: journal_entry

Language: PLPGSQL

DECLARE retval journal_entry;
BEGIN
	INSERT INTO journal_entry (source, description, entry_type, transaction_date,
			approved, is_template)
	VALUES (in_source, in_description, in_entry_type, in_transaction_date,
			coalesce(in_approved, false), 
			coalesce(in_is_template, false));

	SELECT * INTO retval FROM journal_entry WHERE id = currval('journal_id_seq');
	RETURN retval;
END;

Function: public.journal__add_line(in_business_units integer, in_memo integer, in_cleared numeric, in_amount boolean, in_journal_id text, in_account_id integer[])

Returns: journal_line

Language: PLPGSQL

DECLARE retval journal_line;
BEGIN
	INSERT INTO journal_line(account_id, journal_id, amount, cleared, memo)
	VALUES (in_account_id, in_journal_id, in_amount, 
		coalesce(in_cleared, false), in_memo);

        INSERT INTO business_unit_jl(entry_id, bu_class, bu_id)
        SELECT currval('journal_line_line_id_seq'), business_unit_class, bu
          FROM business_unit
         WHERE id = any(in_business_units);

	SELECT * INTO retval FROM journal_line where line_id = currval('journal_line_line_id_seq');
	return retval;
END;

Function: public.journal__get_entry(in_id integer)

Returns: journal_entry

Language: SQL

SELECT * FROM journal_entry where id = $1;

Function: public.journal__get_invoice(in_id integer)

Returns: eca_invoice

Language: SQL

SELECT * FROM eca_invoice where journal_id = $1;

Function: public.journal__lines(in_id integer)

Returns: SET OF journal_line

Language: SQL

select * from journal_line where journal_id = $1;

Function: public.journal__make_invoice(in_language_code integer, in_credit_id integer, in_reverse boolean, in_on_hold boolean, in_journal_id integer, in_order_id character varying)

Returns: eca_invoice

Language: PLPGSQL

DECLARE retval eca_invoice;
BEGIN	
	INSERT INTO eca_invoice (order_id, journal_id, on_hold, reverse,
		credit_id, language_code)
	VALUES (in_order_id, in_journal_id, coalesce(in_on_hold, false), 
		in_reverse, in_credit_id, in_language_code);

	SELECT * INTO retval FROM eca_invoice WHERE journal_id = in_journal_id;

	RETURN retval;
END;

Function: public.journal__search(in_entity_class text, in_meta_number text, in_is_template integer, in_department_id date, in_approved boolean, in_transaction_date integer, in_entry_type boolean, in_description text, in_source integer)

Returns: SET OF journal_search_result

Language: PLPGSQL

DECLARE retval journal_search_result;
BEGIN
	FOR retval IN 
		SELECT j.id, j.source, j.description, j.entry_type, 
			j.transaction_date, j.approved, 
			j.is_template, eca.meta_number, 
			e.name, ec.class
		FROM journal_entry j
		LEFT JOIN eca_invoice i ON (i.journal_id = j.id)
		LEFT JOIN entity_credit_account eca ON (eca.id = credit_id)
		LEFT JOIN entity e ON (eca.entity_id = e.id)
		LEFT JOIN entity_class ec ON (eca.entity_class = ec.id)
		WHERE (in_source IS NULL OR in_source = j.source) AND
			(in_description IS NULL 
				or in_description = j.description) AND
			(in_entry_type is null or in_entry_type = j.entry_type)
			and (in_transaction_date is null 
				or in_transaction_date = j.transaction_date) and
			j.approved = coalesce(in_approved, true) and
			j.is_template = coalesce(in_is_template, false) and
			(in_department_id is null 
				or j.department_id = in_department_id) and
			(in_meta_number is null 
				or eca.meta_number = in_meta_number) and
			(in_entity_class is null
				or eca.entity_class = in_entity_class)
	LOOP
		RETURN NEXT retval;
	END LOOP;
END;

Function: public.journal__validate_entry(in_id integer)

Returns: boolean

Language: SQL

	SELECT sum(amount) = 0 FROM journal_line WHERE journal_id = $1;

Function: public.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: public.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: public.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: public.location__deactivate(in_id integer)

Returns: location

Language: SQL


UPDATE location set active = false, inactive_date = now()
 WHERE id = $1;

SELECT * FROM location WHERE id = 1;


Function: public.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: public.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: public.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: public.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: public.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: public.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: public.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: public.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: public.lsmb__decompose_timestamp(in_timestamp timestamp with time zone)

Returns: lsmb_date_fields

Language: SQL

SELECT extract('century' from $1) as century, 
       extract('decade' from $1) as decade,
       extract('year' from $1) as year,
       extract('month' from $1) as month,
       extract('day' from $1) as day,
       extract('hour' from $1) as hour,
       extract('minute' from $1) as minute,
       extract('second' from $1) as second,
       extract('quarter' from $1) as quarter,
       extract('doy' from $1) as doy,
       extract('dow' from $1) as dow,
       extract('week' from $1) as week,
       extract('epoch' from $1) as epoch,
       $1::date as as_date,
       $1::time as as_time;

Function: public.lsmb_module__get(in_id integer)

Returns: lsmb_module

Language: SQL

Retrieves a single module's info by id.

 SELECT * FROM lsmb_module where id = $1; 

Function: public.lsmb_module__list()

Returns: SET OF lsmb_module

Language: SQL

Returns a list of all defined modules, ordered by id.

 SELECT * FROM lsmb_module ORDER BY id 

Function: public.menu_children(in_parent_id integer)

Returns: SET OF menu_item

Language: SQL

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.

SELECT * FROM menu_generate() where parent = $1;

Function: public.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 
               WITH RECURSIVE tree (path, id, parent, level, positions)
                               AS (select id::text as path, id, parent, 
                                           0 as level, position::text
                                      FROM menu_node where parent is null
                                     UNION 
                                    select path || ',' || n.id::text, n.id, 
                                           n.parent,
                                           t.level + 1, 
                                           t.positions || ',' || n.position
                                      FROM menu_node n
                                      JOIN tree t ON t.id = n.parent) 
		SELECT n.position, n.id, c.level, n.label, c.path, n.parent,
                       to_args(array[ma.attribute, ma.value])
		FROM tree c
		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 acl
                          LEFT JOIN pg_roles pr on pr.rolname = acl.role_name
                               WHERE CASE WHEN role_name 
                                                           ilike 'public'
                                                      THEN true
                                                      WHEN rolname IS NULL
                                                      THEN FALSE
                                                      ELSE pg_has_role(rolname,
                                                                       'USAGE')
                                      END
                            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 tree cc
                                 JOIN menu_node cn USING(id)
                                WHERE cn.id IN 
                                      (select node_id 
                                         FROM menu_acl acl
                                    LEFT JOIN pg_roles pr 
                                              on pr.rolname = acl.role_name
                                        WHERE CASE WHEN rolname 
                                                           ilike 'public'
                                                      THEN true
                                                      WHEN rolname IS NULL
                                                      THEN FALSE
                                                      ELSE pg_has_role(rolname,
                                                                       'USAGE')
                                                END
                                     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::text 
                                           like c.path::text || ',%')
            GROUP BY n.position, n.id, c.level, n.label, c.path, c.positions,
                     n.parent
            ORDER BY string_to_array(c.positions, ',')::int[]
	LOOP
		RETURN NEXT item;
	END LOOP;
END;

Function: public.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: public.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: public.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: public.order__combine(in_ids integer[])

Returns: SET OF oe

Language: PLPGSQL


DECLARE retval oe;
        ordercount int;
        ids int[];
        loop_info record;
        settings text[];
        my_person_id int;
BEGIN

SELECT id INTO my_person_id 
  FROM person 
 WHERE entity_id = person__get_my_entity_id();

settings := ARRAY['sonumber', 'ponumber', 'sqnumber', 'rfqnumber'];
ids := array[]::int[];

-- This approach of looping through insert/select operations will break down 
-- if overly complex order consolidation jobs are run (think, hundreds of 
-- combined orders in the *output*
--
-- The tradeoff is that if we address the huge complex runs here, then we have
-- the possibility of having to lock the whole table which poses other issues.
-- For that reason, I am going with this approach for now. --CT

FOR loop_info IN
       SELECT max(id) as id, taxincluded, entity_credit_account, oe_class_id,
              curr
         FROM oe WHERE id = any(in_ids)
     GROUP BY taxincluded, entity_credit_account, oe_class_id, curr
LOOP

INSERT INTO oe
       (ordnumber, transdate,   amount,        netamount,
        reqdate,   taxincluded, shippingpoint, notes,
        curr,      person_id,   closed,        quotation, 
        quonumber, intnotes,    shipvia,       language_code,
        ponumber,  terms,       oe_class_id,   entity_credit_account)
SELECT CASE WHEN oe_class_id IN (1, 2) 
            THEN setting_increment(settings[oe_class_id])
            ELSE NULL
        END,          now()::date,        sum(amount),        sum(netamount),
        min(reqdate), taxincluded,        min(shippingpoint), '',
        curr,         my_person_id, false, false,
        CASE WHEN oe_class_id IN (3, 4)
            THEN setting_increment(settings[oe_class_id])
            ELSE NULL
        END,          NULL,      NULL,          NULL,
        null,       min(terms),  oe_class_id,  entity_credit_account
  FROM oe 
 WHERE id = any (in_ids)
       AND taxincluded = loop_info.taxincluded
       AND entity_credit_account = loop_info.entity_credit_account
       AND oe_class_id = loop_info.oe_class_id
 GROUP BY curr, taxincluded, oe_class_id, entity_credit_account;


INSERT INTO orderitems
       (trans_id,      parts_id,        description,         qty,
        sellprice,     precision,       discount,            unit,
        reqdate,       ship,            serialnumber,        notes)
SELECT currval('oe_id_seq'), oi.parts_id, oi.description,     oi.qty,
       oi.sellprice,   oi.precision,    oi.discount,         oi.unit,
       oi.reqdate,     oi.ship,         oi.serialnumber,     oi.notes
  FROM orderitems oi
  JOIN oe ON oi.trans_id = oe.id
 WHERE oe.id = any (in_ids)
       AND taxincluded = loop_info.taxincluded
       AND entity_credit_account = loop_info.entity_credit_account
       AND oe_class_id = loop_info.oe_class_id;

ids := ids || currval('oe_id_seq')::int;

END LOOP;

UPDATE oe SET closed = true WHERE id = any(in_ids);

FOR retval IN select * from oe WHERE id =any(ids)
LOOP
   RETURN NEXT retval;
END LOOP;

END;

Function: public.order__search(in_buisness_units integer, in_shippable text, in_date_to text, in_date_from text, in_description text, in_shipvia boolean, in_closed boolean, in_open text, in_ordnumber text, in_ponumber date, in_legal_name date, in_meta_number boolean, in_oe_class_id integer[])

Returns: SET OF order_search_line

Language: PLPGSQL


DECLARE retval order_search_line;

BEGIN

FOR retval IN
       SELECT o.id, 
              CASE WHEN oe_class_id IN (1, 2) THEN o.ordnumber
                   WHEN oe_class_id IN (3, 4) THEN o.quonumber
                   ELSE NULL
               END as ordnumber, o.transdate, o.reqdate,
              o.amount, c.legal_name AS name, o.netamount, 
              o.entity_credit_account, o.closed, o.quonumber, o.shippingpoint,
              CASE WHEN ct.entity_class = 2 THEN ex.buy ELSE ex.sell END
              AS exchangerate, o.shipvia, pe.first_name || ' ' || pe.last_name 
              AS employee, pm.first_name || ' ' || pm.last_name AS manager, 
              o.curr, o.ponumber, ct.meta_number, c.entity_id
         FROM oe o
         JOIN entity_credit_account ct ON (o.entity_credit_account = ct.id)
         JOIN company c ON (c.entity_id = ct.entity_id)
    LEFT JOIN person pe ON (o.person_id = pe.id)
    LEFT JOIN entity_employee e ON (pe.entity_id = e.entity_id)
    LEFT JOIN person pm ON (e.manager_id = pm.id)
    LEFT JOIN entity_employee m ON (pm.entity_id = m.entity_id)
    LEFT JOIN exchangerate ex 
              ON (ex.curr = o.curr AND ex.transdate = o.transdate)
        WHERE o.oe_class_id = in_oe_class_id
             AND (in_meta_number IS NULL 
                   or ct.meta_number ILIKE in_meta_number || '%')
             AND (in_legal_name IS NULL OR
                     c.legal_name @@ plainto_tsquery(in_legal_name))
             AND (in_ponumber IS NULL OR o.ponumber ILIKE in_ponumber || '%')
            AND (in_ordnumber IS NULL 
                     OR o.ordnumber ILIKE in_ordnumber || '%')
             AND (in_open is true or o.closed is false)
             AND (in_closed is true or o.closed is true)
             AND (in_shipvia IS NULL 
                      OR o.shipvia @@ plainto_tsquery(in_shipvia))
             AND (in_description IS NULL AND in_shippable IS NULL OR
                     EXISTS (SELECT 1 
                               FROM orderitems oi 
                               JOIN parts p ON p.id = oi.parts_id
                              WHERE trans_id = o.id
                                    AND (in_description IS NULL OR 
                                        oi.description 
                                        @@ plainto_tsquery(in_description))
                                    AND (in_shippable IS NULL OR
                                         p.assembly OR 
                                         p.inventory_accno_id IS NOT NULL))
                 )
             AND (in_date_from IS NULL OR o.transdate >= in_date_from)
             AND (in_date_to IS NULL OR o.transdate <= in_date_to)
                                    
LOOP
   RETURN NEXT retval;
END LOOP;
END;

Function: public.parse_date(in_date date)

Returns: date

Language: SQL

Simple way to cast a Perl string to a date format of known type.

 select $1; 

Function: public.parts__get_by_id(in_id integer)

Returns: parts

Language: SQL

SELECT * FROM parts WHERE id = $1;

Function: public.parts__get_by_partnumber(in_partnumber text)

Returns: parts

Language: SQL

SELECT * FROM PARTS WHERE partnumber = $1 and obsolete is not true; 

Function: public.parts__search_lite(in_description text, in_partnumber text)

Returns: SET OF parts

Language: SQL

SELECT * 
  FROM parts 
 WHERE ($1 IS NULL OR (partnumber like $1 || '%'))
       AND ($2 IS NULL 
            OR (description
                @@
                plainto_tsquery(get_default_lang()::regconfig, $2)))
       AND not obsolete
ORDER BY partnumber;

Function: public.payment__reverse(in_currency text, in_exchangerate date, in_voucher_id integer, in_batch_id text, in_account_class date, in_date_reversed integer, in_cash_accno integer, in_credit_id integer, in_date_paid numeric, in_source bpchar)

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;
        t_currs text[];
        t_rev_fx numeric;
        t_fxgain_id int;
        t_fxloss_id int;
        t_paid_fx numeric;
BEGIN
        SELECT * INTO t_rev_fx FROM currency_get_exchangerate(
              in_currency, in_date_reversed, in_account_class);

        SELECT * INTO t_paid_fx FROM currency_get_exchangerate(
              in_currency, in_date_paid, in_account_class);

       select value::int INTO t_fxgain_id FROM setting_get('fxgain_accno_id');
       select value::int INTO t_fxloss_id FROM setting_get('fxloss_accno_id');

       SELECT string_to_array(value, ':') into t_currs
          from defaults
         where setting_key = 'curr';

        IF in_currency IS NULL OR in_currency = t_currs[1] THEN
                t_rev_fx := 1;
                t_paid_fx := 1;
        ELSIF t_rev_fx IS NULL THEN
                t_rev_fx := in_exchangerate;
                PERFORM payments_set_exchangerate(in_account_class,
                                                  in_exchangerate,
                                                  in_currency,
                                                  in_date_reversed);
        ELSIF t_rev_fx <> in_exchangerate THEN
                RAISE EXCEPTION 'Exchange rate different than on file';
        END IF;
        IF t_rev_fx IS NULL THEN
            RAISE EXCEPTION 'No exchangerate provided and not default currency';
        END IF;


        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, arap.curr, arap.fxrate
		FROM acc_trans a
		JOIN (select id, curr, entity_credit_account, 
                             CASE WHEN curr = t_currs[1] THEN 1
                                   ELSE buy END as fxrate
			FROM ar 
                   LEFT JOIN exchangerate USING (transdate, curr)
                       WHERE in_account_class = 2
			UNION
			SELECT id, curr, entity_credit_account, 
                               CASE WHEN curr = t_currs[1] THEN 1
                                    ELSE sell END as fxrate
			FROM ap
                   LEFT JOIN exchangerate USING (transdate, curr)
                       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 account ch ON (a.chart_id = ch.id)
		WHERE a.source IS NOT DISTINCT FROM in_source
			AND a.transdate = in_date_paid
			AND in_credit_id = arap.entity_credit_account
			AND in_cash_accno = ch.accno
                        and in_voucher_id IS NOT DISTINCT FROM voucher_id
	LOOP
                IF pay_row.curr = t_currs[1] THEN
                   pay_row.fxrate = 1;
                END IF;

		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 / t_paid_fx * -1 * t_rev_fx, 
			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),
                 (pay_row.trans_id, pay_row.ar_ap_account_id, 
                        pay_row.amount / t_paid_fx * pay_row.fxrate,
			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),
                 (pay_row.trans_id, 
                  case when pay_row.fxrate > t_rev_fx 
                       THEN t_fxloss_id ELSE t_fxgain_id END, 
                  pay_row.amount / t_paid_fx * (t_rev_fx - pay_row.fxrate),
                  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);

                   
	END LOOP;
	RETURN 1;
END;

Function: public.payment__search(in_currency text, in_entity_class date, in_cash_accno date, in_credit_id integer, in_date_to text, in_date_from integer, in_source bpchar)

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, curr
			FROM ar WHERE in_entity_class = 2
			UNION
			SELECT entity_credit_account, id, curr
			FROM ap WHERE in_entity_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 (in_currency IS NULL OR in_currency = arap.curr)
			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: public.payment_bulk_post(in_currency 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

This posts the payments for large batch workflows. Note that in_transactions is a two-dimensional numeric array. Of each sub-array, the first element is the (integer) transaction id, and the second is the amount for that transaction.

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;
        t_cash_sign int;
BEGIN

        SELECT * INTO t_exchangerate FROM currency_get_exchangerate(
              in_currency, in_payment_date, in_account_class);

        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_currency IS NULL OR in_currency = t_currs[1]) THEN
                t_exchangerate := 1;
        ELSIF t_exchangerate IS NULL THEN
                t_exchangerate := in_exchangerate;
                PERFORM payments_set_exchangerate(in_account_class,
                                                  in_exchangerate, 
                                                  in_currency,
                                                  in_payment_date);
        ELSIF t_exchangerate <> in_exchangerate THEN
                RAISE EXCEPTION 'Exchange rate different than on file';
        END IF;
        IF t_exchangerate IS NULL THEN
            RAISE EXCEPTION 'No exchangerate provided and not default currency';
        END IF;

        CREATE TEMPORARY TABLE bulk_payments_in
           (id int, amount numeric, fxrate numeric, gain_loss_accno int);

        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
            -- Fill the bulk payments table
            INSERT INTO bulk_payments_in(id, amount)
            VALUES (in_transactions[out_count][1],
                    in_transactions[out_count][2]);
        END LOOP;

        IF in_account_class = 1 THEN
            t_cash_sign := 1;
        ELSE
            t_cash_sign := -1;
        END IF;

        IF (in_currency IS NULL OR in_currency = t_currs[1]) THEN
            UPDATE bulk_payments_in
               SET fxrate = 1;
        ELSE
            UPDATE bulk_payments_in
               SET fxrate =
                (SELECT CASE WHEN in_account_class = 1 THEN sell
                             ELSE buy
                        END
                   FROM exchangerate e
                   JOIN (SELECT transdate, id, curr FROM ar
                         UNION
                         SELECT transdate, id, curr FROM ap) a
                     ON (e.transdate = a.transdate
                         AND e.curr = a.curr)
                   WHERE a.id = bulk_payments_in.id);
            UPDATE bulk_payments_in
               SET gain_loss_accno =
                (SELECT value::int FROM defaults
                  WHERE setting_key = 'fxgain_accno_id')
             WHERE ((t_exchangerate - bulk_payments_in.fxrate) * t_cash_sign) < 0;
            UPDATE bulk_payments_in
               SET gain_loss_accno = (SELECT value::int FROM defaults
                  WHERE setting_key = 'fxloss_accno_id')
             WHERE ((t_exchangerate - bulk_payments_in.fxrate) * t_cash_sign) > 0;
            -- explicitly leave zero gain/loss accno_id entries at NULL
            -- so we have an easy check for which 
        END IF;

        -- Insert cash side
        INSERT INTO acc_trans
             (trans_id, chart_id, amount, approved,
              voucher_id, transdate, source)
           SELECT id, t_cash_id, amount * t_cash_sign * t_exchangerate/fxrate,
                  CASE WHEN t_voucher_id IS NULL THEN true
                       ELSE false END,
                  t_voucher_id, in_payment_date, in_source
             FROM bulk_payments_in  where amount <> 0;

        -- early payment discounts
        INSERT INTO acc_trans
               (trans_id, chart_id, amount, approved,
               voucher_id, transdate, source)
        SELECT bpi.id, eca.discount_account_id, 
               amount * t_cash_sign * t_exchangerate/fxrate 
               / (1 - discount::numeric/100) 
               * (discount::numeric/100),
               CASE WHEN t_voucher_id IS NULL THEN true
                       ELSE false END,
               t_voucher_id, in_payment_date, in_source
          FROM bulk_payments_in bpi
          JOIN (select entity_credit_account, id, transdate FROM ar 
                 WHERE in_account_class = 2
                 UNION
                SELECT entity_credit_account, id, transdate FROM ap
                 WHERE in_account_class = 1) gl ON gl.id = bpi.id
          JOIN entity_credit_account eca ON gl.entity_credit_account = eca.id
         WHERE bpi.amount <> 0 
               AND extract('days' from age(gl.transdate)) < eca.discount_terms;

        INSERT INTO acc_trans
               (trans_id, chart_id, amount, approved,
               voucher_id, transdate, source)
        SELECT bpi.id, t_ar_ap_id, 
               amount * t_cash_sign * -1 * t_exchangerate/fxrate 
               / (1 - discount::numeric/100) 
               * (discount::numeric/100),
               CASE WHEN t_voucher_id IS NULL THEN true
                       ELSE false END,
               t_voucher_id, in_payment_date, in_source
          FROM bulk_payments_in bpi
          JOIN (select entity_credit_account, id, transdate FROM ar 
                 WHERE in_account_class = 2
                 UNION
                SELECT entity_credit_account, id, transdate FROM ap
                 WHERE in_account_class = 1) gl ON gl.id = bpi.id
          JOIN entity_credit_account eca ON gl.entity_credit_account = eca.id
         WHERE bpi.amount <> 0 
               AND extract('days' from age(gl.transdate)) < eca.discount_terms;

        -- Insert ar/ap side
        INSERT INTO acc_trans
             (trans_id, chart_id, amount, approved,
              voucher_id, transdate, source)
           SELECT id, t_ar_ap_id,
                  amount * -1 * t_cash_sign,
                  CASE WHEN t_voucher_id IS NULL THEN true
                       ELSE false END,
                  t_voucher_id, in_payment_date, in_source
             FROM bulk_payments_in where amount <> 0;

        -- Insert fx gain/loss effects, if applicable
        INSERT INTO acc_trans
             (trans_id, chart_id, amount, approved,
              voucher_id, transdate, source)
           SELECT id, gain_loss_accno,
                  amount * t_cash_sign * (1 - t_exchangerate/fxrate),
                  CASE WHEN t_voucher_id IS NULL THEN true
                       ELSE false END,
                  t_voucher_id, in_payment_date, in_source
             FROM bulk_payments_in
            WHERE amount <> 0 AND gain_loss_accno IS NOT NULL;

        DROP TABLE bulk_payments_in;
        perform unlock_all();
        return out_count;
END;

Function: public.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: public.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: public.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: public.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 (coalesce(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, 
                                 curr, 1 as invoice_class,
                                 entity_credit_account, on_hold, v.batch_id,
                                 approved, paid
                            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,
                                 curr, 2 as invoice_class,
                                 entity_credit_account, on_hold, v.batch_id,
                                 approved, paid
                            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 due <> 0
                         AND NOT a.on_hold
                         AND a.curr = in_currency
                         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: public.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: public.payment_get_entity_account_payment_info(in_entity_credit_id integer)

Returns: payment_vc_info

Language: SQL

Returns payment information on the entity credit account as required to for discount calculations and payment processing.

 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.id = $1;

Function: public.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: public.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 acc_trans
                                   JOIN chart ON (acc_trans.chart_id = chart.id)
                                   JOIN ap ON (acc_trans.trans_id = ap.id)
                                   WHERE link = 'AP'
                                   GROUP BY chart_id,
                                         trans_id, entity_credit_account
                                   HAVING SUM(acc_trans.amount) <> 0)
                               WHEN in_account_class = 2 THEN
                                ec.id IN (SELECT entity_credit_account
                                   FROM acc_trans
                                   JOIN chart ON (acc_trans.chart_id = chart.id)
                                   JOIN ar ON (acc_trans.trans_id = ar.id)
                                   WHERE link = 'AR'
                                   GROUP BY chart_id,
                                         trans_id, entity_credit_account
                                   HAVING SUM(acc_trans.amount) <> 0)
                          END
        LOOP
                RETURN NEXT out_entity;
        END LOOP;
END;

Function: public.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: public.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.invoice AS invoice, 
		       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 = 2
		              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 = 2
		              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 = 2
		              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 = 2
		         THEN ex.buy
		         ELSE ex.sell END)
		         END) AS exchangerate
                 --TODO HV prepare drop entity_id from ap,ar
                 --FROM  (SELECT id, invnumber, transdate, amount, entity_id,
                 FROM  (SELECT id, invnumber, invoice, transdate, amount,
		               1 as invoice_class, curr,
		               entity_credit_account, department_id, approved
		          FROM ap
                         UNION
		         --SELECT id, invnumber, transdate, amount, entity_id,
		         SELECT id, invnumber, invoice, transdate, amount,
		               2 AS invoice_class, 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
                        --### short term: ignore fractional cent differences
		        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, a.invoice
	LOOP
		RETURN NEXT payment_inv;
	END LOOP;
END;

Function: public.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: public.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: public.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 entity_to_location ctl ON (ctl.location_id = l.id)
                JOIN entity cp ON (ctl.entity_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: public.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 fx_gain_loss_amount numeric;
BEGIN
      IF array_upper(in_amount, 1) <> array_upper(in_cash_account_id, 1) THEN
          RAISE EXCEPTION 'Wrong number of accounts';
      END IF;
        
        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
	-- ### BUG: we don't have a guarantee that the transaction is
	--          the same currency as in_curr, so, we can't use
	--          current_exchangerate as the basis for fx gain/loss
	--          calculations
        IF (in_curr = default_currency) THEN 
           old_exchangerate := 1;
        ELSIF (in_account_class = 2) THEN
           SELECT buy INTO old_exchangerate 
           FROM exchangerate e
           JOIN ar a ON (a.transdate = e.transdate)
                        AND (a.curr = e.curr)
           WHERE a.id = in_transaction_id[out_count];
        ELSE 
           SELECT sell INTO old_exchangerate 
           FROM exchangerate e
           JOIN ap a ON (a.transdate = e.transdate)
                        AND (a.curr = e.curr)
           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  fx_gain_loss_amount equals zero then we dont need to post
        -- any transaction
       fx_gain_loss_amount := in_amount[out_count]*current_exchangerate - in_amount[out_count]*old_exchangerate;
       IF (in_account_class = 1) THEN
         -- in case of vendor invoices, the invoice amounts have been negated, do the same with the diff
         fx_gain_loss_amount := fx_gain_loss_amount * -1;
       END IF;

       IF (fx_gain_loss_amount < 0) THEN
           INSERT INTO acc_trans (chart_id, amount, trans_id, transdate, approved, source)
            VALUES ((select value::int from defaults WHERE setting_key = 'fxgain_accno_id'),
                    fx_gain_loss_amount, in_transaction_id[out_count], in_datepaid, coalesce(in_approved, true),
                    in_source[out_count]);
        ELSIF (fx_gain_loss_amount > 0) THEN
            INSERT INTO acc_trans (chart_id, amount, trans_id, transdate, approved, source)
            VALUES ((select value::int from defaults WHERE setting_key = 'fxloss_accno_id'),
                    fx_gain_loss_amount, in_transaction_id[out_count], in_datepaid, coalesce(in_approved, true),
                    in_source[out_count]);
        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: public.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: public.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: public.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: public.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_datepaid
              AND curr = in_curr;
IF current_exrate.transdate = in_datepaid THEN
   IF in_account_class = 2 THEN 
      UPDATE exchangerate set buy = in_exchangerate  where transdate = in_datepaid;
   ELSE
      UPDATE exchangerate set sell = in_exchangerate where transdate = in_datepaid;
   END IF;
   RETURN 0; 
ELSE
    IF in_account_class = 2 THEN
     INSERT INTO exchangerate (curr, transdate, buy) values (in_curr, in_datepaid, in_exchangerate);
  ELSE   
     INSERT INTO exchangerate (curr, transdate, sell) values (in_curr, in_datepaid, in_exchangerate);
  END IF;                                       
RETURN 0;
END IF;
END;

Function: public.payroll_deduction_type__search(in_unit integer, in_label integer, in_country_id integer, in_pdc_id text, in_account_id text)

Returns: SET OF payroll_deduction_type

Language: SQL

 
SELECT * 
  FROM payroll_deduction_type 
 where (account_id = $1 OR $1 IS NULL) AND
       (pdc_id = $2 OR $2 IS NULL) AND
       (country_id = $3 OR $3 IS NULL) AND
       ($4 IS NULL OR label LIKE $4 || '%') AND
       (unit = $5 or $5 IS NULL);

Function: public.payroll_income_category__list()

Returns: SET OF payroll_income_category

Language: SQL

SELECT * FROM payroll_income_category order by id;

Function: public.payroll_income_class__for_country(in_country_id integer)

Returns: SET OF payroll_income_class

Language: SQL

 
SELECT * FROM payroll_income_class where country_id = $1
ORDER BY label;

Function: public.payroll_income_type__get(in_id integer)

Returns: payroll_income_type

Language: SQL

SELECT * FROM payroll_income_type WHERE id  = $1;

Function: public.payroll_income_type__save(in_default_amount integer, in_unit integer, in_label integer, in_country_id integer, in_pic_id text, in_account_id text, in_id numeric)

Returns: payroll_income_type

Language: PLPGSQL


   DECLARE retval payroll_income_type;

BEGIN
   UPDATE payroll_income_type
      SET account_id = in_account_id,
          pic_id = in_pic_id,
          country_id = in_country_id,
          label = in_label,
          unit = in_unit,
          default_amount = in_default_amount
    WHERE id = in_id;

   IF FOUND THEN
       retval := payroll_income_type__get(in_id);
       RETURN retval;
   END IF;

   INSERT INTO payroll_income_type
          (account_id, pic_id, country_id, label, unit, default_amount)
   VALUES (in_account_id, in_pic_id, in_country_id, in_label, in_unit, 
           in_default_amount);

   retval := payroll_income_type__get(currval('payroll_income_type_id_seq')::int);
   RETURN retval;

END;

Function: public.payroll_income_type__search(in_unit integer, in_label integer, in_country_id integer, in_pic_id text, in_account_id text)

Returns: SET OF payroll_income_type

Language: SQL

 
SELECT * 
  FROM payroll_income_type 
 where (account_id = $1 OR $1 IS NULL) AND
       (pic_id = $2 OR $2 IS NULL) AND
       (country_id = $3 OR $3 IS NULL) AND
       ($4 IS NULL OR label LIKE $4 || '%') AND
       (unit = $5 or $5 IS NULL);

Function: public.periods_get()

Returns: SET OF periods

Language: SQL

Returns dates for year to date, and last year.

SELECT * FROM periods ORDER BY id

Function: public.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 entity_to_contact
 WHERE person_id = (SELECT entity_id FROM person WHERE id = in_person_id) 
       and contact_class_id = in_contact_class_id
       and contact= in_contact;
RETURN FOUND;

END;


Function: public.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 entity_to_location
 WHERE person_id = (select entity_id from person where id = in_person_id) 
       AND location_id = in_location_id 
       AND location_class = in_location_class;

RETURN FOUND;

END;

Function: public.person__get(in_entity_id integer)

Returns: person_entity

Language: SQL

SELECT e.id, e.control_code, e.name, e.country_id, c.name, 
       p.first_name, p.middle_name, p.last_name
  FROM entity e
  JOIN country c ON c.id = e.country_id
  JOIN person p ON p.entity_id = e.id
 WHERE e.id = $1;

Function: public.person__get_by_cc(in_control_code text)

Returns: person_entity

Language: SQL

SELECT e.id, e.control_code, e.name, e.country_id, c.name, 
       p.first_name, p.middle_name, p.last_name
  FROM entity e
  JOIN country c ON c.id = e.country_id
  JOIN person p ON p.entity_id = e.id
 WHERE e.control_code = $1;

Function: public.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: public.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: public.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 entity_to_contact c
		JOIN contact_class cc ON (c.contact_class_id = cc.id)
		JOIN person p ON (c.person_id = p.entity_id)
		WHERE p.entity_id = in_entity_id
	LOOP
		RETURN NEXT out_row;
	END LOOP;
END;

Function: public.person__list_languages()

Returns: SET OF language

Language: SQL

Returns a list of languages ordered by code

 SELECT * FROM language ORDER BY code ASC 

Function: public.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 entity_to_location ctl ON (ctl.location_id = l.id)
		JOIN person p ON (ctl.person_id = p.entity_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: public.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: public.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: public.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 FOUND THEN
        UPDATE entity 
           SET name = in_first_name || ' ' || in_last_name,
               country_id = in_country_id
         WHERE id = in_entity_id; 
    ELSE
        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: public.person__save_contact(in_old_contact_class integer, in_description integer, in_contact_new text, in_old_contact text, in_contact_class text, in_entity_id integer)

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 entity_to_contact;
BEGIN
    
    SELECT cc.* into v_orig 
      FROM entity_to_contact cc
      JOIN person p ON (p.entity_id = cc.entity_id)
     WHERE p.entity_id = in_entity_id 
    and cc.contact_class_id = in_old_contact_class
    AND cc.contact = in_old_contact;
    
    IF NOT FOUND THEN
    
        -- create
        INSERT INTO entity_to_contact
               (entity_id, contact_class_id, contact, description)
        VALUES (in_entity_id, in_contact_class, in_contact_new, in_description);

        return 1;
    ELSE
        -- edit.
        UPDATE entity_to_contact
           SET contact = in_contact_new, description = in_description
         WHERE contact = in_old_contact
               AND entity_id = in_entity_id
               AND contact_class_id = in_old_contact_class;
        return 0;
    END IF;
    
END;

Function: public.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 entity_to_location
       SET location_class = in_location_class
     WHERE entity_id = in_entity_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 entity_to_location 
    		(entity_id, location_id, location_class)
    	VALUES  (in_entity_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: public.pnl__customer(in_to_date integer, in_from_date date, in_id date)

Returns: SET OF pnl_line

Language: SQL

WITH gl (id) AS
 ( SELECT id FROM ap WHERE approved is true AND entity_credit_account = $1
UNION ALL
   SELECT id FROM ar WHERE approved is true AND entity_credit_account = $1
)
SELECT a.id, a.accno, a.description, a.category, 
       ah.id, ah.accno, ah.description,
       CASE WHEN a.category = 'E' THEN -1 ELSE 1 END * sum(ac.amount)
  FROM account a
  JOIN account_heading ah on a.heading = ah.id
  JOIN acc_trans ac ON a.id = ac.chart_id
  JOIN gl ON ac.trans_id = gl.id
 WHERE ac.approved is true 
          AND ($2 IS NULL OR ac.transdate >= $2) 
          AND ($3 IS NULL OR ac.transdate <= $3)
          AND a.category IN ('I', 'E')
 GROUP BY a.id, a.accno, a.description, a.category, 
          ah.id, ah.accno, ah.description
 ORDER BY a.category DESC, a.accno ASC;

Function: public.pnl__income_statement_accrual(in_business_units date, in_ignore_yearend date, in_to_date text, in_from_date integer[])

Returns: SET OF pnl_line

Language: SQL

WITH RECURSIVE bu_tree (id, parent, path) AS (
      SELECT id, null, row(array[id])::tree_record FROM business_unit
       WHERE id = any($4)
      UNION ALL
      SELECT bu.id, parent, row((path).t || bu.id)::tree_record
        FROM business_unit bu
        JOIN bu_tree ON bu.parent_id = bu_tree.id
)
   SELECT a.id, a.accno, a.description, a.category, ah.id, ah.accno,
          ah.description, 
          CASE WHEN a.category = 'E' THEN -1 ELSE 1 END * sum(ac.amount)
     FROM account a
     JOIN account_heading ah on a.heading = ah.id
     JOIN acc_trans ac ON a.id = ac.chart_id AND ac.approved
     JOIN tx_report gl ON ac.trans_id = gl.id
LEFT JOIN (select array_agg(path) as bu_ids, entry_id
             FROM business_unit_ac buac
             JOIN bu_tree ON bu_tree.id = buac.bu_id
        GROUP BY buac.entry_id) bu
          ON (ac.entry_id = bu.entry_id)
    WHERE ac.approved is true 
          AND ($1 IS NULL OR ac.transdate >= $1) 
          AND ($2 IS NULL OR ac.transdate <= $2)
          AND ($4 = '{}' 
              OR $4 is null or in_tree($4, bu_ids))
          AND a.category IN ('I', 'E')
          AND ($3 = 'none' 
               OR ($3 = 'all' 
                   AND NOT EXISTS (SELECT * FROM yearend WHERE trans_id = gl.id
                   ))
               OR ($3 = 'last'
                   AND NOT EXISTS (SELECT 1 FROM yearend 
                                   HAVING max(trans_id) = gl.id))
              )
 GROUP BY a.id, a.accno, a.description, a.category, 
          ah.id, ah.accno, ah.description
 ORDER BY a.category DESC, a.accno ASC;

Function: public.pnl__income_statement_cash(in_business_units date, in_ignore_yearend date, in_to_date text, in_from_date integer[])

Returns: SET OF pnl_line

Language: SQL

WITH RECURSIVE bu_tree (id, parent, path) AS (
      SELECT id, null, row(array[id])::tree_record FROM business_unit
       WHERE id = any($4)
      UNION ALL
      SELECT bu.id, parent, row((path).t || bu.id)::tree_record
        FROM business_unit bu
        JOIN bu_tree ON bu.parent_id = bu_tree.id
)
   SELECT a.id, a.accno, a.description, a.category, ah.id, ah.accno,
          ah.description, 
          CASE WHEN a.category = 'E' THEN -1 ELSE 1 END 
               * sum(ac.amount * ca.portion)
     FROM account a
     JOIN account_heading ah on a.heading = ah.id
     JOIN acc_trans ac ON a.id = ac.chart_id AND ac.approved
     JOIN tx_report gl ON ac.trans_id = gl.id
     JOIN (SELECT id, sum(portion) as portion
             FROM cash_impact ca 
            WHERE ($1 IS NULL OR ca.transdate >= $1)
                  AND ($2 IS NULL OR ca.transdate <= $2)
           GROUP BY id
          ) ca ON gl.id = ca.id 
LEFT JOIN (select array_agg(path) as bu_ids, entry_id
             FROM business_unit_ac buac
             JOIN bu_tree ON bu_tree.id = buac.bu_id
         GROUP BY entry_id) bu 
          ON (ac.entry_id = bu.entry_id)
    WHERE ac.approved is true 
          AND ($4 = '{}' 
              OR $4 is null or in_tree($4, bu_ids))
          AND a.category IN ('I', 'E')
          AND ($3 = 'none' 
               OR ($3 = 'all' 
                   AND NOT EXISTS (SELECT * FROM yearend WHERE trans_id = gl.id
                   ))
               OR ($3 = 'last'
                   AND NOT EXISTS (SELECT 1 FROM yearend 
                                   HAVING max(trans_id) = gl.id))
              )
 GROUP BY a.id, a.accno, a.description, a.category, 
          ah.id, ah.accno, ah.description
 ORDER BY a.category DESC, a.accno ASC;

Function: public.pnl__invoice(in_id integer)

Returns: SET OF pnl_line

Language: SQL

SELECT a.id, a.accno, a.description, a.category, 
       ah.id, ah.accno, ah.description,
       CASE WHEN a.category = 'E' THEN -1 ELSE 1 END * sum(ac.amount)
  FROM account a
  JOIN account_heading ah on a.heading = ah.id
  JOIN acc_trans ac ON a.id = ac.chart_id
 WHERE ac.approved AND ac.trans_id = $1 AND a.category IN ('I', 'E')
 GROUP BY a.id, a.accno, a.description, a.category, 
          ah.id, ah.accno, ah.description
 ORDER BY a.category DESC, a.accno ASC;

Function: public.pnl__product(in_business_units date, in_parts_id date, in_to_date integer, in_from_date integer[])

Returns: SET OF pnl_line

Language: SQL

WITH RECURSIVE bu_tree (id, parent, path) AS (
      SELECT id, null, row(array[id])::tree_record FROM business_unit
       WHERE id = any($4)
      UNION ALL
      SELECT bu.id, parent, row((path).t || bu.id)::tree_record
        FROM business_unit bu
        JOIN bu_tree ON bu.parent_id = bu_tree.id
)
   SELECT a.id, a.accno, a.description, a.category, ah.id, ah.accno,
          ah.description, 
          sum(ac.amount) * -1 
     FROM account a
     JOIN account_heading ah on a.heading = ah.id
     JOIN acc_trans ac ON ac.chart_id = a.id
     JOIN invoice i ON i.id = ac.invoice_id
     JOIN account_link l ON l.account_id = a.id
     JOIN ar ON ar.id = ac.trans_id 
LEFT JOIN (select as_array(bu.path) as bu_ids, entry_id
             from business_unit_inv bui 
             JOIN bu_tree bu ON bui.bu_id = bu.id
         GROUP BY entry_id) bui ON bui.entry_id = i.id
    WHERE i.parts_id = $3
          AND (ac.transdate >= $1 OR $1 IS NULL) 
          AND (ac.transdate <= $2 OR $2 IS NULL)
          AND ar.approved
          AND l.description = 'IC_expense'
          AND ($4 is null or $4 = '{}' OR in_tree($4, bu_ids))
 GROUP BY a.id, a.accno, a.description, a.category, ah.id, ah.accno,
          ah.description
    UNION
   SELECT a.id, a.accno, a.description, a.category, ah.id, ah.accno,
          ah.description, 
          sum(i.sellprice * i.qty * (1 - coalesce(i.discount, 0)))
     FROM parts p
     JOIN invoice i ON i.id = p.id
     JOIN acc_trans ac ON ac.invoice_id = i.id
     JOIN account a ON p.income_accno_id = a.id
     JOIN ar ON ar.id = ac.trans_id
     JOIN account_heading ah on a.heading = ah.id
LEFT JOIN (select as_array(bu.path) as bu_ids, entry_id
             from business_unit_inv bui 
             JOIN bu_tree bu ON bui.bu_id = bu.id
         GROUP BY entry_id) bui ON bui.entry_id = i.id
    WHERE i.parts_id = $3
          AND (ac.transdate >= $1 OR $1 IS NULL) 
          AND (ac.transdate <= $2 OR $2 IS NULL)
          AND ar.approved
          AND ($4 is null or $4 = '{}' OR in_tree($4, bu_ids))
 GROUP BY a.id, a.accno, a.description, a.category, ah.id, ah.accno,
          ah.description

Function: public.pricegroup__list()

Returns: SET OF pricegroup

Language: SQL

Returns an alphabetically ordered pricegroup list.

SELECT * FROM pricegroup ORDER BY pricegroup;

Function: public.pricegroups__list()

Returns: SET OF pricegroup

Language: SQL

SELECT * FROM pricegroup;

Function: public.pricelist__delete(credit_id integer, entry_id integer)

Returns: boolean

Language: SQL

delete from partscustomer where entry_id = $1 and credit_id = $2;
delete from partsvendor where entry_id = $1 and credit_id = $2;
select true;

Function: public.pricelist__save(in_entry_id integer, in_curr integer, in_validto numeric, in_validfrom numeric, in_partnumber smallint, in_lead_time text, in_price date, in_pricebreak date, in_credit_id bpchar, in_parts_id integer)

Returns: eca__pricematrix

Language: PLPGSQL

DECLARE 
   retval eca__pricematrix;
   t_insert bool;
   t_entity_class int;

BEGIN

t_insert := false;

SELECT entity_class INTO t_entity_class FROM entity_credit_account 
  WHERE id = in_credit_id;

IF t_entity_class = 1 THEN -- VENDOR
    UPDATE partsvendor
       SET lastcost = in_price,
           leadtime = in_lead_time,
           partnumber = in_partnumber,
           curr = in_curr
     WHERE credit_id = in_credit_id AND entry_id = in_entry_id;

    IF NOT FOUND THEN
        INSERT INTO partsvendor
               (parts_id, credit_id, lastcost, leadtime, partnumber, curr)
        VALUES (in_parts_id, in_credit_id, in_price, in_leadtime::int2, 
               in_partnumber, in_curr);
    END IF;

    SELECT pv.parts_id, p.partnumber, p.description, pv.credit_id, NULL, NULL,
           pv.lastcost, pv.leadtime::int, pv.partnumber, NULL, NULL, pv.curr, 
           pv.entry_id
      INTO retval
      FROM partsvendor pv
      JOIN parts p ON p.id = pv.parts_id
     WHERE parts_id = in_parts_id and credit_id = in_credit_id;

    RETURN retval;

ELSIF t_entity_class = 2 THEN -- CUSTOMER
    UPDATE partscustomer
       SET pricebreak = in_pricebreak,
           sellprice  = in_price,
           validfrom  = in_validfrom,
           validto    = in_validto,
           curr       = in_curr
     WHERE entry_id = in_entry_id and credit_id = in_credit_id;

    IF NOT FOUND THEN
        INSERT INTO partscustomer
               (parts_id, credit_id, sellprice, validfrom, validto, curr)
        VALUES (in_parts_id, in_credit_id, in_price, in_validfrom, in_validto, 
                in_curr);

        t_insert := true;
    END IF;

    SELECT pc.parts_id, p.partnumber, p.description, pc.credit_id, 
           pc.pricebreak, pc.sellprice, NULL, NULL, NULL, pc.validfrom, 
           pc.validto, pc.curr, pc.entry_id
      INTO retval
      FROM partscustomer pc
      JOIN parts p on pc.parts_id = p.id
     WHERE entry_id = CASE WHEN t_insert 
                           THEN currval('partscustomer_entry_id_seq') 
                           ELSE in_entry_id 
                      END;
                           
    RETURN retval;

ELSE

RAISE EXCEPTION 'No valid entity credit account found';

END IF;
END;

Function: public.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: public.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;
        t_amount numeric;
    BEGIN
        SELECT CASE WHEN a.category in ('A', 'E') THEN in_amount * -1
                    ELSE in_amount
               END into t_amount
          FROM cr_report r JOIN account a ON r.chart_id = a.id
         WHERE r.id = in_report_id; 

	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
                -- could this be changed to update, if not found insert?
		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, t_amount, 0, in_date, t_uid,
				in_type);
		ELSIF in_count = 1 THEN
			UPDATE cr_report_line
			SET their_balance = t_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 = t_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 = t_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 = t_amount, 
					trans_type = in_type,
					clear_time = in_date,
					cleared = true
				WHERE t_scn = scn AND report_id = in_report_id
                                	AND our_value = t_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 = t_amount
				ORDER BY id ASC limit 1;

				UPDATE cr_report_line
                                SET their_balance = t_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 = t_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, t_amount, 0, in_date, t_uid,
			in_type);
		ELSIF in_count = 1 THEN -- perfect match
			UPDATE cr_report_line SET their_balance = t_amount,
					trans_type = in_type,
					clear_time = in_date,
					cleared = true
			WHERE report_id = in_report_id AND our_balance = t_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 = t_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 = t_amount,
					trans_type = in_type,
					clear_time = in_date,
					cleared = true
			WHERE id = lid;
			
		END IF;
	END IF;
        return 1; 
        
    END;    

Function: public.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: public.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: public.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: public.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: public.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: public.reconciliation__new_report_id(in_recon_fx integer, in_end_date numeric, in_total date, in_chart_id boolean)

Returns: integer

Language: SQL

Inserts creates a new report and returns the id.


    INSERT INTO cr_report(chart_id, their_total, end_date, recon_fx) 
    values ($1, $2, $3, $4);
    SELECT currval('cr_report_id_seq')::int;


Function: public.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;
        t_recon_fx BOOL;
    BEGIN
                SELECT recon_fx INTO t_recon_fx FROM cr_report WHERE id = in_report_id;
 
		INSERT INTO cr_report_line (report_id, scn, their_balance, 
			our_balance, "user", voucher_id, ledger_id, post_date)
		SELECT in_report_id, 
		       CASE WHEN ac.source IS NULL OR ac.source = '' 
                            THEN gl.ref
                            ELSE ac.source END,
		       0, 
		       sum(amount / CASE WHEN t_recon_fx IS NOT TRUE OR gl.table = 'gl'
                                         THEN 1
                                         WHEN t_recon_fx and gl.table = 'ap' 
                                         THEN ex.sell
                                         WHEN t_recon_fx and gl.table = 'ar' 
                                         THEN ex.buy
                                    END) 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, curr, 
                             transdate, 'ar' as table 
                        FROM ar where approved
			UNION
		      select id, entity_credit_account::text, curr, 
                             transdate, 'ap' as table 
                        FROM ap WHERE approved
			UNION
		      select id, reference, '', 
                             transdate, '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)))
                LEFT JOIN exchangerate ex ON gl.transdate = ex.transdate
		WHERE ac.cleared IS FALSE
			AND ac.approved IS TRUE
			AND ac.chart_id = in_chart_id
			AND ac.transdate <= in_end_date
                        AND ((t_recon_fx is not true 
                                and ac.fx_transaction is not true) 
                            OR (t_recon_fx is true 
                                AND (gl.table <> 'gl' OR ac.fx_transaction
                                                      IS TRUE))) 
		GROUP BY gl.ref, ac.source, ac.transdate,
			ac.memo, ac.voucher_id, gl.table, 
                        case when gl.table = 'gl' then gl.id else 1 end
		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: public.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: public.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: public.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: public.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: public.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: public.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: public.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: public.report__aa_outstanding(in_date_to integer, in_date_from integer, in_on_hold text, in_ship_via text, in_business_units integer, in_employee_id integer[], in_meta_number text, in_entity_name boolean, in_account_id date, in_entity_class date)

Returns: SET OF aa_transactions_line

Language: SQL


SELECT null::int as id, null::bool as invoice, entity_id, meta_number, 
       entity_name, null::date as transdate, count(*)::text as invnumber, 
       sum(amount) as amount, sum(netamount) as netamount, sum(tax) as tax, 
       sum(paid) as paid, sum(due) as due, max(last_payment) as last_payment, 
       null::date as duedate, null::text as notes, null::text as till, 
       null::text as salesperson, null::text as manager, 
       null::text as shipping_point, null::text as ship_via, 
       null::text[] as business_units
  FROM report__aa_outstanding_details($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
 GROUP BY meta_number, entity_name, entity_id;


Function: public.report__aa_outstanding_details(in_date_to integer, in_date_from integer, in_on_hold text, in_ship_via text, in_business_units integer, in_employee_id integer[], in_meta_number text, in_entity_name boolean, in_account_id date, in_entity_class date)

Returns: SET OF aa_transactions_line

Language: PLPGSQL

DECLARE retval aa_transactions_line;

BEGIN

FOR retval IN
SELECT a.id, a.invoice, eeca.id, eca.meta_number, eeca.name, a.transdate,
       a.invnumber, a.amount, a.netamount, a.netamount - a.amount as tax, 
       a.amount - p.due as paid, p.due, p.last_payment, a.duedate, a.notes,
       a.till, ee.name, me.name, a.shippingpoint, a.shipvia, 
       '{}' as business_units -- TODO
  FROM (select id, transdate, invnumber, amount, netamount, duedate, notes, 
               till, person_id, entity_credit_account, invoice, shippingpoint,
               shipvia, ordnumber, ponumber, description, on_hold
          FROM ar
         WHERE in_entity_class = 2 and approved
         UNION
        SELECT id, transdate, invnumber, amount, netamount, duedate, notes,
               null, person_id, entity_credit_account, invoice, shippingpoint,
               shipvia, ordnumber, ponumber, description, on_hold
          FROM ap 
         WHERE in_entity_class = 1 and approved) a 
  JOIN (SELECT trans_id, sum(amount) * 
               CASE WHEN in_entity_class = 1 THEN 1 ELSE -1 END AS due,
               max(transdate) as last_payment
          FROM acc_trans ac
          JOIN account_link al ON ac.chart_id = al.account_id
         WHERE approved AND al.description IN ('AR', 'AP')
               AND (in_date_to is null or transdate <= in_date_to)
      GROUP BY trans_id) p ON p.trans_id = a.id
  JOIN entity_credit_account eca ON a.entity_credit_account = eca.id
  JOIN entity eeca ON eca.entity_id = eeca.id
  JOIN entity_employee ON entity_employee.entity_id = a.person_id
  JOIN entity ee ON entity_employee.entity_id = ee.id
  LEFT
  JOIN entity me ON entity_employee.manager_id = me.id
 WHERE (in_account_id IS NULL 
          OR EXISTS (select 1 FROM acc_trans 
                      WHERE trans_id = a.id and chart_id = in_account_id))
       AND (in_entity_name IS NULL 
           OR eeca.name @@ plainto_tsquery(in_entity_name))
       AND (in_meta_number IS NULL 
          OR eca.meta_number ilike in_meta_number || '%')
       AND (in_employee_id IS NULL OR ee.id = in_employee_id)
       AND (in_ship_via IS NULL
          OR a.shipvia @@ plainto_tsquery(in_ship_via))
       AND (in_on_hold IS NULL OR in_on_hold = a.on_hold)
       AND (in_date_from IS NULL OR a.transdate >= in_date_from)
       AND (in_date_to IS NULL OR a.transdate <= in_date_to)
       AND p.due::numeric(100,2) <> 0
LOOP
   RETURN NEXT retval;
END LOOP;
END;

Function: public.report__aa_transactions(in_tax_account_id integer, in_taxable integer, in_on_hold text, in_date_to text, in_date_from integer, in_shipvia integer, in_notes text, in_description text, in_source text, in_ponumber text, in_ordnumber text, in_invnumber text, in_manager_id text, in_employee_id date, in_meta_number date, in_entity_name boolean, in_account_id boolean, in_entity_class integer)

Returns: SET OF aa_transactions_line

Language: PLPGSQL


DECLARE retval aa_transactions_line;

BEGIN

FOR retval IN

SELECT a.id, a.invoice, eeca.id, eca.meta_number, eeca.name,
       a.transdate, a.invnumber, a.amount, a.netamount, 
       a.amount - a.netamount as tax, a.amount - p.due, p.due, p.last_payment, 
       a.duedate, a.notes,
       a.till, eee.name as employee, mee.name as manager, a.shippingpoint, 
       a.shipvia, '{}'
       
  FROM (select id, transdate, invnumber, amount, netamount, duedate, notes, 
               till, person_id, entity_credit_account, invoice, shippingpoint,
               shipvia, ordnumber, ponumber, description, on_hold
          FROM ar
         WHERE in_entity_class = 2 and approved
         UNION
        SELECT id, transdate, invnumber, amount, netamount, duedate, notes,
               null, person_id, entity_credit_account, invoice, shippingpoint,
               shipvia, ordnumber, ponumber, description, on_hold
          FROM ap 
         WHERE in_entity_class = 1 and approved) a 
  JOIN (select sum(amount) * case when in_entity_class = 1 THEN 1 ELSE -1 END
               as due, trans_id, max(transdate) as last_payment
          FROM acc_trans ac
          JOIN account_link l ON ac.chart_id = l.account_id
         WHERE l.description IN ('AR', 'AP')
      GROUP BY ac.trans_id
       ) p ON p.trans_id = a.id
  JOIN entity_employee ee ON ee.entity_id = a.person_id
  JOIN entity eee ON eee.id = ee.entity_id
  JOIN entity_credit_account eca ON a.entity_credit_account = eca.id
  JOIN entity eeca ON eca.entity_id = eeca.id
  LEFT
  JOIN entity mee ON ee.manager_id = mee.id
 WHERE (in_account_id IS NULL OR 
       EXISTS (select * from acc_trans 
                where trans_id = a.id AND chart_id = in_account_id))
       AND (in_entity_name IS NULL 
           OR eeca.name ilike in_entity_name || '%')
       AND (in_meta_number IS NULL OR eca.meta_number ilike in_meta_number)
       AND (in_employee_id = ee.entity_id OR in_employee_id IS NULL)
       AND (in_manager_id = mee.id OR in_manager_id IS NULL)
       AND (a.invnumber ilike in_invnumber || '%' OR in_invnumber IS NULL)
       AND (a.ordnumber ilike in_ordnumber || '%' OR in_ordnumber IS NULL)
       AND (a.ponumber ilike in_ponumber || '%' OR in_ponumber IS NULL)
       AND (in_source IS NULL OR
           EXISTS (
              SELECT * from acc_trans where trans_id = a.id 
                     AND source ilike in_source || '%'
           ))
       AND (in_description IS NULL 
              OR a.description @@ plainto_tsquery(in_description))
       AND (in_notes IS NULL OR a.notes @@ plainto_tsquery(in_notes))
       AND (in_shipvia IS NULL OR a.shipvia @@ plainto_tsquery(in_shipvia))
       AND (in_date_from IS NULL OR a.transdate >= in_date_from)
       AND (in_date_to IS NULL OR a.transdate <= in_date_to)
       AND (in_on_hold IS NULL OR in_on_hold = a.on_hold)
       AND (in_taxable IS NULL 
            OR (in_taxable 
              AND (in_tax_account_id IS NULL 
                 OR EXISTS (SELECT 1 FROM acc_trans 
                             WHERE trans_id = a.id 
                                   AND chart_id = in_tax_account_id)
            ))
            OR (NOT in_taxable
                  AND NOT EXISTS (SELECT 1 
                                    FROM acc_trans ac
                                    JOIN account_link al 
                                      ON al.account_id = ac.chart_id
                                   WHERE ac.trans_id = a.id 
                                         AND al.description ilike '%tax'))
            )

LOOP

  RETURN NEXT retval;

END LOOP;

END;

Function: public.report__cash_summary(in_to_accno date, in_from_accno date, in_date_to text, in_date_from text)

Returns: SET OF cash_summary_item

Language: SQL

SELECT a.id, a.accno, a.is_heading, a.description, t.label, 
       sum(CASE WHEN ac.amount < 0 THEN ac.amount * -1 ELSE NULL END),
       sum(CASE WHEN ac.amount > 0 THEN ac.amount ELSE NULL END)
  FROM (select id, accno, false as is_heading, description FROM account
       UNION
        SELECT id, accno, true, description FROM account_heading) a
  LEFT
  JOIN acc_trans ac ON ac.chart_id = a.id 
  LEFT
  JOIN (select id, case when table_name ilike 'ar' THEN 'rcpt'
                        when table_name ilike 'ap' THEN 'pmt'
                        when table_name ilike 'gl' THEN 'xfer'
                    END AS label
          FROM transactions) t ON t.id = ac.trans_id
 WHERE accno BETWEEN $3 AND $4
        and ac.transdate BETWEEN $1 AND $2
GROUP BY a.id, a.accno, a.is_heading, a.description, t.label
ORDER BY accno;


Function: public.report__coa()

Returns: SET OF coa_entry

Language: SQL


WITH ac (chart_id, amount) AS (
     SELECT chart_id, CASE WHEN acc_trans.approved and gl.approved THEN amount
                           ELSE 0 
                       END
       FROM acc_trans
       JOIN (select id, approved from ar union all
             select id, approved from ap union all
             select id, approved from gl) gl ON gl.id = acc_trans.trans_id
),
l(account_id, link) AS (
     SELECT account_id, array_to_string(array_agg(description), ':')
       FROM account_link
   GROUP BY account_id
)
SELECT a.id, a.is_heading, a.accno, a.description, a.gifi_accno, 
       CASE WHEN sum(ac.amount) < 0 THEN sum(amount) * -1 ELSE null::numeric
        END,
       CASE WHEN sum(ac.amount) > 0 THEN sum(amount) ELSE null::numeric END,
       count(ac.*), l.link
  FROM (SELECT id,false as is_heading, accno, description, gifi_accno
          FROM account
         UNION
        SELECT id, true, accno, description, null::text 
          FROM account_heading) a

 LEFT JOIN ac ON ac.chart_id = a.id AND not a.is_heading
 LEFT JOIN l ON l.account_id = a.id AND NOT a.is_heading
  GROUP BY a.id, a.is_heading, a.accno, a.description, a.gifi_accno, l.link
  ORDER BY a.accno;


Function: public.report__general_balance(in_date_to date, in_date_from date)

Returns: SET OF general_balance_line

Language: SQL


SELECT a.id, a.accno, a.description,
      sum(CASE WHEN ac.transdate < $1 THEN abs(amount) ELSE null END),
      sum(CASE WHEN ac.transdate >= $1 AND ac.amount < 0 
               THEN ac.amount * -1 ELSE null END),
      SUM(CASE WHEN ac.transdate >= $1 AND ac.amount > 0
               THEN ac.amount ELSE null END),
      SUM(ABS(ac.amount))
 FROM account a 
 LEFT
 JOIN acc_trans ac ON ac.chart_id = a.id
 LEFT 
 JOIN (select id, approved from ar UNION
       SELECT id, approved from ap UNION
       SELECT id, approved FROM gl) gl ON ac.trans_id = gl.id
WHERE gl.approved and ac.approved
      and ac.transdate <= $2 
GROUP BY a.id, a.accno, a.description
ORDER BY a.accno;


Function: public.report__gl(in_business_units text, in_to_amount text, in_from_amount bpchar, in_approved text, in_to_date text, in_from_date text, in_description date, in_memo date, in_source boolean, in_category numeric, in_accno numeric, in_reference integer[])

Returns: SET OF gl_report_item

Language: PLPGSQL

DECLARE 
         retval gl_report_item;
         t_balance numeric;
         t_chart_id int;
BEGIN

IF in_from_date IS NULL THEN
   t_balance := 0;
ELSIF in_accno IS NOT NULL THEN
   SELECT id INTO t_chart_id FROM account WHERE accno  = in_accno;
   t_balance := account__obtain_balance(in_from_date , 
                                       (select id from account 
                                         where accno = in_accno));
ELSE
   t_balance := null;
END IF;

FOR retval IN
       WITH RECURSIVE bu_tree (id, path) AS (
            SELECT id, id::text AS path
              FROM business_unit
             WHERE parent_id is null
            UNION
            SELECT bu.id, bu_tree.path || ',' || bu.id
              FROM business_unit bu
              JOIN bu_tree ON bu_tree.id = bu.parent_id
            )
       SELECT g.id, g.type, g.invoice, g.reference, g.description, ac.transdate,
              ac.source, ac.amount, c.accno, c.gifi_accno, 
              g.till, ac.cleared, ac.memo, c.description AS accname, 
              ac.chart_id, ac.entry_id, 
              sum(ac.amount) over (rows unbounded preceding) + t_balance 
                as running_balance,
              compound_array(ARRAY[ARRAY[bac.class_id, bac.bu_id]])
         FROM (select id, 'gl' as type, false as invoice, reference, 
                      description, approved,
                      null::text as till 
                 FROM gl
               UNION
               SELECT ar.id, 'ar', invoice, invnumber, e.name, approved, till
                 FROM ar
                 JOIN entity_credit_account eca ON ar.entity_credit_account
                      = eca.id
                 JOIN entity e ON e.id = eca.entity_id
               UNION
               SELECT ap.id, 'ap', invoice, invnumber, e.name, approved,
                      null as till
                 FROM ap
                 JOIN entity_credit_account eca ON ap.entity_credit_account 
                      = eca.id
                 JOIN entity e ON e.id = eca.entity_id) g
         JOIN acc_trans ac ON ac.trans_id = g.id
         JOIN account c ON ac.chart_id = c.id
    LEFT JOIN business_unit_ac bac ON ac.entry_id = bac.entry_id 
    LEFT JOIN bu_tree ON bac.bu_id = bu_tree.id
        WHERE (g.reference ilike in_reference || '%' or in_reference is null)
              AND (c.accno = in_accno OR in_accno IS NULL)
              AND (ac.source ilike '%' || in_source || '%' 
                   OR in_source is null)
              AND (ac.memo ilike '%' || in_memo || '%' OR in_memo is null)
             AND (in_description IS NULL OR
                  g.description
                  @@
                  plainto_tsquery(get_default_lang()::regconfig, in_description))
              AND (transdate BETWEEN in_from_date AND in_to_date
                   OR (transdate >= in_from_date AND  in_to_date IS NULL)
                   OR (transdate <= in_to_date AND in_from_date IS NULL)
                   OR (in_to_date IS NULL AND in_from_date IS NULL))
              AND (in_approved is false OR (g.approved AND ac.approved))
              AND (in_from_amount IS NULL OR ac.amount >= in_from_amount)
              AND (in_to_amount IS NULL OR ac.amount <= in_to_amount)
              AND (in_category = c.category OR in_category IS NULL)
     GROUP BY g.id, g.type, g.invoice, g.reference, g.description, ac.transdate,
              ac.source, ac.amount, c.accno, c.gifi_accno,
              g.till, ac.cleared, ac.memo, c.description,
              ac.chart_id, ac.entry_id, ac.trans_id
       HAVING in_business_units is null or in_business_units 
                <@ compound_array(string_to_array(bu_tree.path, ',')::int[])
     ORDER BY ac.transdate, ac.trans_id, c.accno
LOOP
   RETURN NEXT retval;
END LOOP;
END;

Function: public.report__invoice_aging_detail(in_use_duedate integer, in_business_units integer, in_to_date text, in_accno date, in_entity_class integer[], in_entity_id boolean)

Returns: SET OF report_aging_item

Language: PLPGSQL

DECLARE
	item report_aging_item;
BEGIN
	FOR item IN
                  WITH RECURSIVE bu_tree (id, path) AS (
                SELECT id, id::text AS path
                  FROM business_unit
                 WHERE id = any(in_business_units)
                 UNION
                SELECT bu.id, bu_tree.path || ',' || bu.id
                  FROM business_unit bu
                  JOIN bu_tree ON bu_tree.id = bu.parent_id
                       )
		SELECT c.entity_id, c.meta_number, e.name,
		       e.name as contact_name, 
	               a.invnumber, a.transdate, a.till, a.ordnumber, 
		       a.ponumber, a.notes, 
		       CASE WHEN a.age/30 = 0
		                 THEN (a.sign * sum(ac.amount)) 
                            ELSE 0 END
		            as c0, 
		       CASE WHEN a.age/30 = 1
		                 THEN (a.sign * sum(ac.amount))
                            ELSE 0 END
		            as c30, 
		       CASE WHEN a.age/30 = 2
		            THEN (a.sign * sum(ac.amount))
                            ELSE 0 END
		            as c60, 
		       CASE WHEN a.age/30 > 2
		            THEN (a.sign * sum(ac.amount))
                            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,
                   (coalesce(in_to_date, now())::date - a.transdate) as age
		  FROM (select id, invnumber, till, ordnumber, amount, duedate,
                               curr, ponumber, notes, entity_credit_account,
                               -1 AS sign, transdate,
                               CASE WHEN in_use_duedate 
                                    THEN coalesce(in_to_date, now())::date
                                         - duedate
                                    ELSE coalesce(in_to_date, now())::date
                                         - transdate 
                               END as age
                          FROM ar
                         WHERE in_entity_class = 2
                         UNION 
                        SELECT id, invnumber, null, ordnumber, amount, duedate,
                               curr, ponumber, notes, entity_credit_account,
                               1 as sign, transdate,
                               CASE WHEN in_use_duedate 
                                    THEN coalesce(in_to_date, now())::date
                                         - duedate
                                    ELSE coalesce(in_to_date, now())::date
                                         - transdate 
                               END as age
                          FROM ap
                         WHERE in_entity_class = 1) a
                  JOIN acc_trans ac ON ac.trans_id = a.id
                  JOIN account acc ON ac.chart_id = acc.id
                  JOIN account_link acl ON acl.account_id = acc.id
                       AND ((in_entity_class = 1 
                              AND acl.description = 'AP')
                           OR (in_entity_class = 2
                              AND acl.description = 'AR'))
		  JOIN entity_credit_account c 
                       ON a.entity_credit_account = c.id
		  JOIN entity e ON (e.id = c.entity_id)
             LEFT JOIN business_unit_ac buac ON ac.entry_id = buac.entry_id
             LEFT JOIN bu_tree ON buac.bu_id = bu_tree.id
	     LEFT JOIN entity_to_location e2l 
                       ON e.id = e2l.entity_id 
                       AND e2l.location_class = 3
             LEFT JOIN location l ON l.id = e2l.location_id
	     LEFT JOIN country ON (country.id = l.country_id)
                 WHERE (e.id = in_entity_id OR in_entity_id IS NULL)
                       AND (in_accno IS NULL or acc.accno = in_accno)
              GROUP BY c.entity_id, c.meta_number, e.name,
                       l.line_one, l.line_two, l.line_three,
                       l.city, l.state, l.mail_code, country.name,
                       a.invnumber, a.transdate, a.till, a.ordnumber,
                       a.ponumber, a.notes, a.amount, a.sign,
                       a.duedate, a.id, a.curr, a.age
                HAVING in_business_units is null or in_business_units 
                       <@ compound_array(string_to_array(bu_tree.path, 
                                         ',')::int[])
	      ORDER BY entity_id, curr, transdate, invnumber
	LOOP
		return next item;
        END LOOP;
END;

Function: public.report__invoice_aging_summary(in_use_duedate integer, in_business_units integer, in_to_date text, in_accno date, in_entity_class integer[], in_entity_id boolean)

Returns: SET OF report_aging_item

Language: SQL

SELECT entity_id, account_number, name, contact_name, null::text, null::date, 
       null::text, null::text, null::text, null::text, 
       sum(c0), sum(c30), sum(c60), sum(c90), null::date, null::int, curr,
       null::numeric, null::text[], null::int
  FROM report__invoice_aging_detail($1, $2, $3, $4, $5, $6)
 GROUP BY entity_id, account_number, name, contact_name, curr
 ORDER BY account_number

Function: public.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: public.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: public.session_check(in_token integer, in_session_id text)

Returns: session

Language: PLPGSQL

Returns a session row. If no session exists, it returns null

DECLARE out_row session%ROWTYPE;
BEGIN
	DELETE FROM session
	 WHERE last_used < now() - coalesce((SELECT value FROM defaults
                                    WHERE setting_key = 'session_timeout')::interval,
	                            '90 minutes'::interval);
        UPDATE session 
           SET last_used = now()
         WHERE session_id = in_session_id
               AND token = in_token
	       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

               PERFORM * 
                  FROM defaults
                 WHERE setting_key = 'auto_logout' and value = '1';

                IF FOUND THEN
                    RAISE NOTICE 'auto logout';
                    RETURN NULL;
                ELSE
                    INSERT INTO session (users_id, token)
                    SELECT id, md5(random()::text)
                      FROM users 
                     WHERE username = SESSION_USER;

                    SELECT * INTO out_row FROM SESSION 
                     WHERE users_id = (select id from users
                                             where username = SESSION_USER);
                    RETURN out_row;
               END IF;
	END IF;
	RETURN out_row;
END;

Function: public.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: public.setting__set(in_value character varying, in_setting_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_setting_key;
        IF NOT FOUND THEN
             INSERT INTO defaults (setting_key, value) 
                  VALUES (in_setting_key, in_value);
        END IF;
	RETURN TRUE;
END;

Function: public.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: public.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: public.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: public.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: public.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: public.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, 
       t.is_accrual
  FROM country_tax_form t
  JOIN country c ON c.id = t.country_id
 ORDER BY c.name, t.form_name;

Function: public.tax_form__save(in_is_accrual integer, in_default_reportable integer, in_form_name text, in_country_id boolean, 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),
               is_accrual = coalesce(in_is_accrual, 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), 
                coalesce(in_is_accrual, false));

	RETURN currval('country_tax_form_id_seq');
END;

Function: public.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 entity_credit_account.id,
                     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, entity_credit_account.id
	LOOP
		RETURN NEXT out_row;
	END LOOP;
END;

Function: public.tax_form_details_report_accrual(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 entity_credit_account.id,
                     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
                          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
                          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
                              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 
                       WHERE transdate BETWEEN in_begin AND in_end
                       UNION 
                      select id, entity_credit_account, invnumber, duedate, 
                             amount, transdate, 'ap' as class
                        FROM ap
                       WHERE transdate BETWEEN in_begin AND in_end
                     ) 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)
		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, entity_credit_account.id
	LOOP
		RETURN NEXT out_row;
	END LOOP;
END;

Function: public.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 entity_credit_account.id,
                     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, entity_credit_account.id
    LOOP
		RETURN NEXT out_row;
	END LOOP;
END;

Function: public.tax_form_summary_report_accrual(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 entity_credit_account.id,
                     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
                          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
                          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
                      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 
                       WHERE transdate BETWEEN in_begin AND in_end
                       UNION 
                      select id, transdate, entity_credit_account, invoice, 
                              amount, 'ap' as class from ap
                       WHERE transdate BETWEEN in_begin AND in_end
                     ) 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 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, entity_credit_account.id
    LOOP
		RETURN NEXT out_row;
	END LOOP;
END;

Function: public.test_get_account_id(in_accno text)

Returns: integer

Language: SQL

 
SELECT id FROM chart WHERE accno = $1; 

Function: public.tg_enforce_perms_eclass()

Returns: trigger

Language: PLPGSQL

DECLARE
   r_eclass entity_class;
   roll_pfx text;
BEGIN
IF TG_OP = 'DELETE' THEN
   RETURN OLD;
ELSE 
   SELECT value INTO roll_pfx FROM defaults WHERE setting_key = 'roll_prefix';
   SELECT * INTO r_eclass from entity_class WHERE id = NEW.entity_class;
   IF pg_has_role(SESSION_USER, coalesce(roll_pfx, 
                                         'lsmb_' || current_database() || '__')
                                || 'contact_class_' || lower(regexp_replace(
                                                        r_eclass.class, 
                                                        ' ', 
                                                        '_')), 'USAGE')
   THEN
      RETURN NEW;
   ELSE
      RAISE EXCEPTION 'Access Denied for class';
   END IF;
END IF;
END;

Function: public.timecard__get(id integer)

Returns: jcitems

Language: SQL

 SELECT * FROM jcitems WHERE id = $1; 

Function: public.timecard__parts(in_service boolean, in_timecard boolean)

Returns: SET OF parts

Language: SQL

SELECT * 
  FROM parts
 WHERE not obsolete
       AND ($1 OR inventory_accno_id IS NULL)
       AND ($2 OR (income_accno_id IS NOT NULL 
             AND inventory_accno_id IS NULL))
 ORDER BY partnumber;

Function: public.timecard__report(in_closed integer[], in_open text, in_date_to integer, in_date_from date, in_person_id date, in_partnumber boolean, in_business_units boolean)

Returns: SET OF timecard_report_line

Language: SQL

WITH RECURSIVE bu_tree (id, path) AS (
     SELECT id, id::text AS path, control_code, description
       FROM business_unit
      WHERE id = any($1)
      UNION
     SELECT bu.id, bu_tree.path || ',' || bu.id, bu.control_code, bu.description
       FROM business_unit bu
       JOIN bu_tree ON bu_tree.id = bu.parent_id
)
SELECT j.id, j.description, j.qty, j.allocated, j.checkedin::time as checkedin,
       j.checkedout::time as checkedout, j.checkedin::date as transdate,
       extract('dow' from j.checkedin) as weekday, 
       extract('week' from j.checkedin) as workweek,
       p.partnumber, bu.control_code as business_unit_code, 
       bu.description AS businessunit_description,
       ee.employeenumber, e.name AS employee, j.parts_id, j.sellprice
  FROM jcitems j
  JOIN parts p ON p.id = j.parts_id
  JOIN person ON person.id = j.person_id
  JOIN entity_employee ee ON ee.entity_id = person.entity_id
  JOIN entity e ON ee.entity_id = e.id
  JOIN bu_tree bu ON bu.id = j.business_unit_id
 WHERE (p.partnumber = $2 OR $2 IS NULL)
       AND (ee.entity_id = $3 OR $3 IS NULL)
       AND (j.checkedin::date <= $4 OR $4 IS NULL)
       AND (j.checkedin::date >= $5 OR $5 IS NULL)
       AND (j.qty > j.allocated AND $6)
       AND (j.qty <= j.allocated AND $7);

Function: public.timecard__save(in_non_billable integer, in_total integer, in_notes integer, in_person_id text, in_checkedout numeric, in_checkedin numeric, in_serialnumber numeric, in_fxsellprice numeric, in_sellprice text, in_allocated timestamp with time zone, in_qty timestamp with time zone, in_description integer, in_parts_id text, in_business_unit_id numeric, in_id numeric)

Returns: jcitems

Language: PLPGSQL

DECLARE retval jcitems;

BEGIN

UPDATE jcitems 
   SET description = in_description,
       qty = in_qty,
       allocated = in_allocated,
       serialnumber = in_serialnumber,
       checkedin = in_checkedin,
       checkedout = in_checkedout,
       person_id = person__get_my_entity_id(),
       notes = in_notes,
       total = in_total,
       non_billable = in_non_billable
 WHERE id = in_id;

IF FOUND THEN
  SELECT * INTO retval WHERE id = in_id;
  return retval;
END IF;

INSERT INTO jcitems 
(business_unit_id, parts_id, description, qty, allocated, sellprice,
  fxsellprice, serialnumber, checkedin, checkedout, person_id, notes,
  total, non_billable)
VALUES
(in_business_unit_id, in_parts_id, in_description, in_qty, in_allocated, 
  in_sellprice, in_fxsellprice, in_serialnumber, in_checkedin, in_checkedout, 
  in_person_id, in_notes, in_total, in_non_billable);

SELECT * INTO retval WHERE id = currval('jcitems_id_seq')::int;

RETURN retval;

END;

Function: public.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: public.to_args(text[])

Returns: text[]

Language: INTERNAL

Turns a setof ARRAY[key,value] into an ARRAY[key||'='||value, key||'='||value,...]

aggregate_dummy

Function: public.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, approved) 
		VALUES (new.id, TG_RELNAME, new.approved);
	ELSEIF tg_op = 'UPDATE' THEN
		IF new.id = old.id AND new.approved = old.approved THEN
			return new;
		ELSE
			UPDATE transactions SET id = new.id, 
                                                approved = new.approved
                         WHERE id = old.id;
		END IF;
	ELSE 
		DELETE FROM transactions WHERE id = old.id;
	END IF;
	RETURN new;
END;

Function: public.trigger_parts_short()

Returns: trigger

Language: PLPGSQL

BEGIN
  IF NEW.onhand >= NEW.rop THEN
    NOTIFY parts_short;
  END IF;
  RETURN NEW;
END;

Function: public.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: public.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: public.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: public.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: public.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: public.user__get_all_users()

Returns: SET OF user_listable

Language: SQL

    
    select * from user_listable;
    

Function: public.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: public.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: public.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: public.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.invoice, 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.invoice, 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, false, 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, false, 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, false, 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: public.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: public.wage__list_for_entity(in_entity_id integer)

Returns: SET OF payroll_wage

Language: SQL

SELECT * FROM payroll_wage WHERE entity_id = $1;

Function: public.wage__list_types(in_country_id integer)

Returns: SET OF payroll_income_type

Language: SQL

 
SELECT * FROM payroll_income_type where country_id = $1

Function: public.wage__save(in_type_id numeric, in_entity_id integer, in_rate integer)

Returns: SET OF payroll_wage

Language: PLPGSQL

 
BEGIN

UPDATE payroll_wage
   SET rate = in_rate
 WHERE entity_id = in_entity_id and in_type_id;


IF NOT FOUND THEN
    INSERT INTO payroll_wage (entity_id, type_id, rate)
    VALUES (in_entity_id, in_type_id, in_rate);
END IF;
  
RETURN QUERY SELECT * FROM payroll_wage 
             WHERE entity_id = in_entity_id and in_type_id;
END;

Function: public.warehouse__list_all()

Returns: SET OF warehouse

Language: SQL

SELECT * FROM warehouse order by description;

Generated by PostgreSQL Autodoc

W3C HTML 4.01 Strict