# LederSMB 1.5.3 Model

## Schema public

standard public schema

### Table: ac_tax_form

Mapping journal_line to country_tax_form for reporting purposes.

Structure of ac_tax_form

entry_id

  integer PRIMARY KEY REFERENCES

reportable

  boolean

### Table: acc_trans

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

Structure of acc_trans

trans_id

  integer NOT NULL REFERENCES

chart_id

  integer NOT NULL REFERENCES

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`

  When 'f', indicates that the amount column states the amount in the currency as specified in the associated ar, ap, payment or gl record. When 't', indicates that the amount column states the difference between the foreighn currency amount and the base amount so that their sum equals the base amount.

memo

  text

invoice_id

  integer REFERENCES

approved

  boolean `DEFAULT true`

cleared_on

  date

reconciled_on

  date

voucher_id

  integer REFERENCES

entry_id

  serial PRIMARY KEY

Indexes on acc_trans

acc_trans_transdate_year_idx

  transdate, date_part('YEAR'::text, transdate)) WHERE (transdate IS NOT NULL

acc_trans_voucher_id_idx

  voucher_id

Tables referencing ac_tax_form via Foreign Key Constraints

### Table: account

This table stores the main account info.

Structure of account

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

  A=asset,L=liability,Q=Equity,I=Income,E=expense

gifi_accno

  text

heading

  integer NOT NULL REFERENCES

contra

  boolean NOT NULL `DEFAULT false`

tax

  boolean NOT NULL `DEFAULT false`

obsolete

  boolean NOT NULL `DEFAULT false`

Constraints on account

account_category_check

  CHECK ((category = ANY (ARRAY['A'::bpchar, 'L'::bpchar, 'Q'::bpchar, 'I'::bpchar, 'E'::bpchar])))

Tables referencing acc_trans via Foreign Key Constraints

### Table: account_checkpoint

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

Structure of account_checkpoint

end_date

  date PRIMARY KEY

account_id

  integer PRIMARY KEY REFERENCES

amount

  numeric NOT NULL

id

  serial UNIQUE NOT NULL

debits

  numeric

credits

  numeric

### Table: account_heading

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

Structure of account_heading

id

  serial UNIQUE NOT NULL

accno

  text PRIMARY KEY

parent_id

  integer REFERENCES

description

  text

category

  character(1)

  Same as the column account.category, except that if NULL the category is automatically derived from the linked accounts.

Constraints on account_heading

account_heading_category_check

  CHECK ((category = ANY (ARRAY['A'::bpchar, 'L'::bpchar, 'Q'::bpchar, 'I'::bpchar, 'E'::bpchar])))

Tables referencing account via Foreign Key Constraints

### View: account_heading_derived_category

Lists for each row the derived category for each heading, based on the categories of the linked accounts.

Structure of account_heading_derived_category

id

  integer

accno

  text

description

  text

parent_id

  integer

original_category

  character(1)

asset_count

  bigint

liability_count

  bigint

expense_count

  bigint

income_count

  bigint

equity_count

  bigint

derived_category

  text

category

  bpchar

Definition of view account_heading_derived_category

    SELECT derivation.id
    ,
        derivation.accno
    ,
        derivation.description
    ,
        derivation.parent_id
    ,
        derivation.original_category
    ,
        derivation.asset_count
    ,
        derivation.liability_count
    ,
        derivation.expense_count
    ,
        derivation.income_count
    ,
        derivation.equity_count
    ,
        derivation.derived_category
    ,
        COALESCE
    (derivation.original_category
         , (derivation.derived_category)::bpchar
    ) AS category

    FROM (
    SELECT category_counts.id
         ,
                category_counts.accno
         ,
                category_counts.description
         ,
                category_counts.parent_id
         ,
                category_counts.original_category
         ,
                category_counts.asset_count
         ,
                category_counts.liability_count
         ,
                category_counts.expense_count
         ,
                category_counts.income_count
         ,
                category_counts.equity_count
         ,
                    CASE
                        WHEN
         (category_counts.equity_count > 0) THEN 'Q'::text
                        WHEN
         (
               (category_counts.income_count > 0)
             AND (category_counts.expense_count > 0)
         ) THEN 'Q'::text
                        WHEN
         (
               (category_counts.asset_count > 0)
             AND (category_counts.liability_count > 0)
         ) THEN 'Q'::text
                        WHEN
         (category_counts.asset_count > 0) THEN 'A'::text
                        WHEN
         (category_counts.liability_count > 0) THEN 'L'::text
                        WHEN
         (category_counts.expense_count > 0) THEN 'E'::text
                        WHEN
         (category_counts.income_count > 0) THEN 'I'::text
                        ELSE NULL::text
                    END AS derived_category

      FROM (
          SELECT ah.id
               ,
                        ah.accno
               ,
                        ah.description
               ,
                        ah.parent_id
               ,
                        ah.category AS original_category
               ,
                        count
               (
                            CASE
                                WHEN
                     (acc.category = 'A'::bpchar) THEN acc.category
                                ELSE NULL::bpchar
                            END
               ) AS asset_count
               ,
                        count
               (
                            CASE
                                WHEN
                     (acc.category = 'L'::bpchar) THEN acc.category
                                ELSE NULL::bpchar
                            END
               ) AS liability_count
               ,
                        count
               (
                            CASE
                                WHEN
                     (acc.category = 'E'::bpchar) THEN acc.category
                                ELSE NULL::bpchar
                            END
               ) AS expense_count
               ,
                        count
               (
                            CASE
                                WHEN
                     (acc.category = 'I'::bpchar) THEN acc.category
                                ELSE NULL::bpchar
                            END
               ) AS income_count
               ,
                        count
               (
                            CASE
                                WHEN
                     (acc.category = 'Q'::bpchar) THEN acc.category
                                ELSE NULL::bpchar
                            END
               ) AS equity_count

            FROM (
                     (account_heading_descendant ahd

                        JOIN account_heading ah
                          ON (
                                 (ahd.id = ah.id)
                           )
                     )

             LEFT JOIN account acc
                    ON (
                           (ahd.descendant_id = acc.heading)
                     )
               )

        GROUP BY ah.id
               , ah.accno
               , ah.description
               , ah.parent_id
               , ah.category
         ) category_counts
    ) derivation;

### View: account_heading_descendant

Returns rows for each heading listing its immediate children, children of children, etc., etc. This is primarily practical when calculating subtotals for PNL and B/S headings.

Structure of account_heading_descendant

id

  integer

level

  integer

descendant_id

  integer

accno

  text

descendant_accno

  text

Definition of view account_heading_descendant

     WITH RECURSIVE account_headings AS
    (

    SELECT account_heading.id
         ,
                1 AS level
         ,
                account_heading.id AS descendant_id
         ,
                account_heading.accno
         ,
                account_heading.accno AS descendant_accno

      FROM account_heading

    UNION ALL

    SELECT at.id
         ,

         (at.level + 1) AS level
         ,
                ah.id AS descendant_id
         ,
                at.accno
         ,
                ah.accno AS descendant_accno

      FROM (account_heading ah

            JOIN account_headings at
              ON (
                     (ah.parent_id = at.descendant_id)
               )
         )

    )

    SELECT account_headings.id
    ,
        account_headings.level
    ,
        account_headings.descendant_id
    ,
        account_headings.accno
    ,
        account_headings.descendant_accno

    FROM account_headings;

### Table: account_heading_translation

Translations for account heading descriptions.

Structure of account_heading_translation

trans_id

  integer PRIMARY KEY REFERENCES

language_code

  character varying(6) PRIMARY KEY

description

  text

### View: account_heading_tree

Returns in the 'path' field an array which contains the path of the heading to its associated root.

Structure of account_heading_tree

id

  integer

accno

  text

description

  text

level

  integer

path

  integer[]

Definition of view account_heading_tree

     WITH RECURSIVE account_headings AS
    (

    SELECT account_heading.id
         ,
                account_heading.accno
         ,
                account_heading.description
         ,
                1 AS level
         ,
                ARRAY[account_heading.id] AS path

      FROM account_heading

     WHERE (account_heading.parent_id IS NULL)

    UNION ALL

    SELECT ah.id
         ,
                ah.accno
         ,
                ah.description
         ,

         (at.level + 1) AS level
         ,
                array_append
         (at.path
               , ah.id
         ) AS path

      FROM (account_heading ah

            JOIN account_headings at
              ON (
                     (ah.parent_id = at.id)
               )
         )

    )

    SELECT account_headings.id
    ,
        account_headings.accno
    ,
        account_headings.description
    ,
        account_headings.level
    ,
        account_headings.path

    FROM account_headings;

### Table: account_link

Structure of account_link

account_id

  integer PRIMARY KEY REFERENCES

description

  text PRIMARY KEY REFERENCES

### Table: account_link_description

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

Structure of account_link_description

description

  text PRIMARY KEY

summary

  boolean NOT NULL

custom

  boolean NOT NULL

Tables referencing account_link via Foreign Key Constraints

### Table: account_translation

Translations for account descriptions.

Structure of account_translation

trans_id

  integer PRIMARY KEY REFERENCES

language_code

  character varying(6) PRIMARY KEY

description

  text

### Table: ap

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

Structure of ap

id

  integer PRIMARY KEY `DEFAULT nextval('id'::regclass)` REFERENCES

invnumber

  text

  Text identifier for the invoice. Must be unique.

transdate

  date `DEFAULT ('now'::text)::date`

entity_id

  integer REFERENCES

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

person_id

  integer REFERENCES

  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.

crdate

  date

is_return

  boolean `DEFAULT false`

entity_credit_account

  integer NOT NULL REFERENCES

  reference for the vendor account used.

Constraints on ap

ap_check

  CHECK ((((amount IS NULL) AND (curr IS NULL)) OR ((amount IS NOT NULL) AND (curr IS NOT NULL))))

### Table: ar

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

Structure of ar

id

  integer PRIMARY KEY `DEFAULT nextval('id'::regclass)` REFERENCES

invnumber

  text

  Text identifier for the invoice. Must be unique.

transdate

  date `DEFAULT ('now'::text)::date`

entity_id

  integer REFERENCES

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

person_id

  integer REFERENCES

  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.

entity_credit_account

  integer NOT NULL REFERENCES

  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`

crdate

  date

setting_sequence

  text

Constraints on ar

ar_check

  CHECK ((((amount IS NULL) AND (curr IS NULL)) OR ((amount IS NOT NULL) AND (curr IS NOT NULL))))

ar_check1

  CHECK (((invnumber IS NOT NULL) OR (NOT approved)))

### Table: assembly

Holds mapping for parts that are members of assemblies.

Structure of assembly

id

  integer PRIMARY KEY REFERENCES

  This is the id of the assembly the part is being mapped to.

parts_id

  integer PRIMARY KEY REFERENCES

  ID of part that is a member of the assembly.

qty

  numeric

bom

  boolean

adj

  boolean

Indexes on assembly

assembly_id_key

  id

### Table: asset_class

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

Structure of asset_class

id

  serial UNIQUE NOT NULL

label

  text PRIMARY KEY

asset_account_id

  integer REFERENCES

dep_account_id

  integer REFERENCES

method

  integer REFERENCES

Tables referencing asset_item via Foreign Key Constraints

### Table: asset_dep_method

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

Structure of asset_dep_method

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

unit_class

  integer NOT NULL REFERENCES

Tables referencing asset_class via Foreign Key Constraints

### Table: asset_disposal_method

Structure of asset_disposal_method

label

  text PRIMARY KEY

id

  serial UNIQUE NOT NULL

multiple

  integer

short_label

  character(1)

Constraints on asset_disposal_method

asset_disposal_method_multiple_check

  CHECK ((multiple = ANY (ARRAY[1, 0, '-1'::integer])))

Tables referencing asset_rl_to_disposal_method via Foreign Key Constraints

### Table: asset_item

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

Structure of asset_item

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

location_id

  integer REFERENCES

department_id

  integer REFERENCES

invoice_id

  integer REFERENCES

asset_account_id

  integer REFERENCES

dep_account_id

  integer REFERENCES

exp_account_id

  integer REFERENCES

obsolete_by

  integer UNIQUE#1 REFERENCES

asset_class_id

  integer REFERENCES

Tables referencing asset_item via Foreign Key Constraints

### Table: asset_note

Structure of asset_note

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"()`

ref_key

  integer NOT NULL REFERENCES

subject

  text

Constraints on asset_note

asset_note_note_class_check

  CHECK ((note_class = 4))

### Table: asset_report

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

Structure of asset_report

id

  serial PRIMARY KEY

report_date

  date

gl_id

  bigint UNIQUE REFERENCES

asset_class

  bigint REFERENCES

report_class

  integer REFERENCES

entered_by

  bigint NOT NULL `DEFAULT person__get_my_entity_id()` REFERENCES

approved_by

  bigint REFERENCES

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 asset_report_line via Foreign Key Constraints

### Table: asset_report_class

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

Structure of asset_report_class

id

  integer UNIQUE NOT NULL

class

  text PRIMARY KEY

Tables referencing asset_report via Foreign Key Constraints

### Table: asset_report_line

Structure of asset_report_line

asset_id

  bigint PRIMARY KEY REFERENCES

report_id

  bigint PRIMARY KEY REFERENCES

amount

  numeric

department_id

  integer REFERENCES

  In case assets are moved between departments, we have to store this here.

warehouse_id

  integer REFERENCES

### Table: asset_rl_to_disposal_method

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

Structure of asset_rl_to_disposal_method

report_id

  integer PRIMARY KEY REFERENCES

asset_id

  integer PRIMARY KEY REFERENCES

disposal_method_id

  integer PRIMARY KEY REFERENCES

percent_disposed

  numeric

### Table: asset_unit_class

Structure of asset_unit_class

id

  integer UNIQUE NOT NULL

class

  text PRIMARY KEY

Tables referencing asset_dep_method via Foreign Key Constraints

### Table: audittrail

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

Structure of audittrail

trans_id

  integer

tablename

  text

reference

  text

formname

  text

action

  text

transdate

  timestamp without time zone `DEFAULT now()`

person_id

  integer NOT NULL REFERENCES

entry_id

  bigserial PRIMARY KEY

Indexes on audittrail

audittrail_trans_id_key

  trans_id

### Table: batch

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

Structure of batch

id

  serial PRIMARY KEY

batch_class_id

  integer NOT NULL REFERENCES

  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

approved_by

  integer REFERENCES

created_by

  integer REFERENCES

locked_by

  integer REFERENCES

created_on

  date `DEFAULT now()`

Constraints on batch

batch_control_code_check

  CHECK ((length(control_code) > 0))

Tables referencing voucher via Foreign Key Constraints

### Table: batch_class

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

Structure of batch_class

id

  serial UNIQUE NOT NULL

class

  character varying PRIMARY KEY

Tables referencing batch via Foreign Key Constraints

### Table: bu_class_to_module

Structure of bu_class_to_module

bu_class_id

  integer PRIMARY KEY REFERENCES

module_id

  integer PRIMARY KEY REFERENCES

### Table: budget_info

Structure of budget_info

id

  serial UNIQUE NOT NULL

start_date

  date NOT NULL

end_date

  date NOT NULL

reference

  text PRIMARY KEY

description

  text NOT NULL

entered_by

  integer NOT NULL `DEFAULT person__get_my_entity_id()` REFERENCES

approved_by

  integer REFERENCES

obsolete_by

  integer REFERENCES

entered_at

  timestamp without time zone NOT NULL `DEFAULT now()`

approved_at

  timestamp without time zone

obsolete_at

  timestamp without time zone

Constraints on budget_info

budget_info_check

  CHECK ((start_date < end_date))

Tables referencing budget_line via Foreign Key Constraints

### Table: budget_line

Structure of budget_line

budget_id

  integer PRIMARY KEY REFERENCES

account_id

  integer PRIMARY KEY REFERENCES

description

  text

amount

  numeric NOT NULL

### Table: budget_note

Structure of budget_note

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"()`

ref_key

  integer NOT NULL REFERENCES

subject

  text

Constraints on budget_note

budget_note_note_class_check

  CHECK ((note_class = 6))

### Table: budget_to_business_unit

Structure of budget_to_business_unit

budget_id

  integer UNIQUE PRIMARY KEY REFERENCES

bu_id

  integer NOT NULL REFERENCES

bu_class

  integer PRIMARY KEY REFERENCES

### Table: business

Groups of Customers assigned joint discounts.

Structure of business

id

  serial PRIMARY KEY

description

  text

discount

  numeric

### Table: business_unit

Tracks Projects, Departments, Funds, Etc.

Structure of business_unit

id

  serial UNIQUE#1 PRIMARY KEY

class_id

  integer UNIQUE#2 UNIQUE#1 NOT NULL REFERENCES

control_code

  text UNIQUE#2

description

  text

start_date

  date

end_date

  date

parent_id

  integer REFERENCES

credit_id

  integer REFERENCES

Tables referencing asset_item via Foreign Key Constraints

### Table: business_unit_ac

Structure of business_unit_ac

entry_id

  integer PRIMARY KEY REFERENCES

class_id

  integer PRIMARY KEY REFERENCES REFERENCES

bu_id

  integer PRIMARY KEY REFERENCES

Indexes on business_unit_ac

business_unit_ac_entry_id_idx

  entry_id

### Table: business_unit_class

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

Structure of business_unit_class

id

  serial UNIQUE NOT NULL

label

  text PRIMARY KEY

active

  boolean NOT NULL `DEFAULT false`

ordering

  integer

Tables referencing bu_class_to_module via Foreign Key Constraints

### Table: business_unit_inv

Structure of business_unit_inv

entry_id

  integer PRIMARY KEY REFERENCES

class_id

  integer PRIMARY KEY REFERENCES REFERENCES

bu_id

  integer PRIMARY KEY REFERENCES

Indexes on business_unit_inv

business_unit_inv_entry_id_idx

  entry_id

### Table: business_unit_jl

Structure of business_unit_jl

entry_id

  integer PRIMARY KEY REFERENCES

bu_class

  integer PRIMARY KEY REFERENCES

bu_id

  integer NOT NULL REFERENCES

### Table: business_unit_oitem

Structure of business_unit_oitem

entry_id

  integer PRIMARY KEY REFERENCES

class_id

  integer PRIMARY KEY REFERENCES REFERENCES

bu_id

  integer PRIMARY KEY REFERENCES

Indexes on business_unit_oitem

business_unit_oitem_entry_id_idx

  entry_id

### Table: business_unit_translation

Translation information for projects, departments, etc.

Structure of business_unit_translation

trans_id

  integer PRIMARY KEY REFERENCES

language_code

  character varying(6) PRIMARY KEY

description

  text

### View: cash_impact

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

Structure of cash_impact

id

  integer

portion

  numeric

rel

  text

transdate

  date

Definition of view cash_impact

    SELECT gl.id
    ,
        '1'::numeric AS portion
    ,
        'gl'::text AS rel
    ,
        gl.transdate

    FROM gl

    UNION ALL

    SELECT 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
    , gl.transdate

    UNION ALL

    SELECT 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
    , gl.transdate;

### View: chart

Compatibility chart for 1.2 and earlier.

Structure of chart

id

  integer

accno

  text

description

  text

charttype

  text

category

  bpchar

link

  text

account_heading

  integer

gifi_accno

  text

contra

  boolean

tax

  boolean

Definition of view chart

    SELECT ah.id
    ,
        ah.accno
    ,
        COALESCE
    (ht.description
         , ah.description
    ) AS description
    ,
        'H'::text AS charttype
    ,
        NULL::bpchar AS category
    ,
        NULL::text AS link
    ,
        ah.parent_id AS account_heading
    ,
        NULL::text AS gifi_accno
    ,
        false AS contra
    ,
        false AS tax

    FROM (account_heading ah

    LEFT JOIN (
          SELECT ht_1.trans_id
               ,
                ht_1.description

            FROM (
                     (account_heading_translation ht_1

                        JOIN user_preference up
                          ON (
                                 (
                                       (up.language)::text =
                                       (ht_1.language_code)::text
                                 )
                           )
                     )

                  JOIN users
                    ON (
                           (up.id = users.id)
                     )
               )

           WHERE (
                     (users.username)::name = "session_user"
                     ()
               )
         ) ht
        ON (
               (ah.id = ht.trans_id)
         )
    )
    UNION

    SELECT c.id
    ,
        c.accno
    ,
        COALESCE
    (at.description
         , c.description
    ) AS 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)
               )
         )

    LEFT JOIN (
          SELECT at_1.trans_id
               ,
                at_1.description

            FROM (
                     (account_translation at_1

                        JOIN user_preference up
                          ON (
                                 (
                                       (up.language)::text =
                                       (at_1.language_code)::text
                                 )
                           )
                     )

                  JOIN users
                    ON (
                           (up.id = users.id)
                     )
               )

           WHERE (
                     (users.username)::name = "session_user"
                     ()
               )
         ) at
        ON (
               (c.id = at.trans_id)
         )
    )

    GROUP BY c.id
    , c.accno
    , COALESCE
    (at.description
         , c.description
    )
    , c.category
    , c.heading
    , c.gifi_accno
    , c.contra
    , c.tax;

### Table: company

Structure of company

id

  serial UNIQUE NOT NULL

entity_id

  integer PRIMARY KEY REFERENCES

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`

Constraints on company

company_legal_name_check

  CHECK ((legal_name ~ '[[:alnum:]_]'::text))

Tables referencing person_to_company via Foreign Key Constraints

### Table: contact_class

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

Structure of contact_class

id

  serial UNIQUE NOT NULL

class

  text PRIMARY KEY

Constraints on contact_class

contact_class_class_check

  CHECK ((class ~ '[[:alnum:]_]'::text))

Tables referencing eca_to_contact via Foreign Key Constraints

### Table: country

Structure of country

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

Constraints on country

country_name_check

  CHECK ((name ~ '[[:alnum:]_]'::text))

country_short_name_check

  CHECK ((short_name ~ '[[:alnum:]_]'::text))

Tables referencing country_tax_form via Foreign Key Constraints

### Table: country_tax_form

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

Structure of country_tax_form

country_id

  integer PRIMARY KEY REFERENCES

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 entity_credit_account via Foreign Key Constraints

### Table: cr_coa_to_account

Provides name mapping for the cash reconciliation screen.

Structure of cr_coa_to_account

chart_id

  integer PRIMARY KEY REFERENCES

account

  text NOT NULL

### Table: cr_report

This table holds header data for cash reports.

Structure of cr_report

id

  bigserial PRIMARY KEY

chart_id

  integer NOT NULL REFERENCES

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()`

entered_by

  integer NOT NULL `DEFAULT person__get_my_entity_id()` REFERENCES

entered_username

  text NOT NULL `DEFAULT "session_user"()`

deleted

  boolean NOT NULL `DEFAULT false`

deleted_by

  integer REFERENCES

approved_by

  integer REFERENCES

approved_username

  text

recon_fx

  boolean `DEFAULT false`

max_ac_id

  integer REFERENCES

Constraints on cr_report

cr_report_check

  CHECK (((deleted IS NOT TRUE) OR (approved IS NOT TRUE)))

Tables referencing cr_report_line via Foreign Key Constraints

### Table: cr_report_line

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

Structure of cr_report_line

id

  bigserial PRIMARY KEY

report_id

  integer NOT NULL REFERENCES

scn

  text

  This is the check number. Maps to gl.reference

their_balance

  numeric

our_balance

  numeric

errorcode

  integer

user

  integer NOT NULL REFERENCES

clear_time

  date

insert_time

  timestamp with time zone NOT NULL `DEFAULT now()`

trans_type

  text

post_date

  date

ledger_id

  integer REFERENCES

voucher_id

  integer

overlook

  boolean NOT NULL `DEFAULT false`

cleared

  boolean NOT NULL `DEFAULT false`

### Table: custom_field_catalog

Deprecated, all use removed from old code.

Structure of custom_field_catalog

field_id

  serial PRIMARY KEY

table_id

  integer REFERENCES

field_name

  text

### Table: custom_table_catalog

Deprecated, all use removed from old code.

Structure of custom_table_catalog

table_id

  serial PRIMARY KEY

extends

  text

table_name

  text

Tables referencing custom_field_catalog via Foreign Key Constraints

### Table: db_patch_log

Structure of db_patch_log

when_applied

  timestamp without time zone PRIMARY KEY

path

  text NOT NULL

sha

  text NOT NULL

sqlstate

  text NOT NULL

error

  text

### Table: db_patches

Structure of db_patches

sha

  text PRIMARY KEY

path

  text NOT NULL

last_updated

  timestamp without time zone NOT NULL

### Table: defaults

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

Structure of defaults

setting_key

  text PRIMARY KEY

value

  text

Tables referencing lsmb_sequence via Foreign Key Constraints

### Table: eca_invoice

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

Structure of eca_invoice

order_id

  integer

  Link to order it was created from

journal_id

  integer PRIMARY KEY REFERENCES

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.

credit_id

  integer NOT NULL REFERENCES

due

  date NOT NULL

language_code

  character(6) REFERENCES

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 asset_item via Foreign Key Constraints

### Table: eca_note

Notes for entity_credit_account entries.

Structure of eca_note

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"()`

ref_key

  integer NOT NULL REFERENCES

  references entity_credit_account.id

subject

  text

Constraints on eca_note

eca_note_note_class_check

  CHECK ((note_class = 3))

### Table: eca_tax

Mapping customers and vendors to taxes.

Structure of eca_tax

eca_id

  integer PRIMARY KEY REFERENCES

chart_id

  integer PRIMARY KEY REFERENCES

### Table: eca_to_contact

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

Structure of eca_to_contact

credit_id

  integer PRIMARY KEY REFERENCES

contact_class_id

  integer PRIMARY KEY REFERENCES

contact

  text PRIMARY KEY

description

  text

Constraints on eca_to_contact

eca_to_contact_contact_check

  CHECK ((contact ~ '[[:alnum:]_]'::text))

### Table: eca_to_location

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

Structure of eca_to_location

location_id

  integer PRIMARY KEY REFERENCES

location_class

  integer PRIMARY KEY REFERENCES

credit_id

  integer PRIMARY KEY REFERENCES

### Table: employee_class

Structure of employee_class

label

  text PRIMARY KEY

id

  serial UNIQUE NOT NULL

Tables referencing employee_to_ec via Foreign Key Constraints

### View: employee_search

Structure of employee_search

entity_id

  integer

startdate

  date

enddate

  date

role

  character varying(20)

ssn

  text

sales

  boolean

manager_id

  integer

employeenumber

  character varying(32)

dob

  date

is_manager

  boolean

manager

  text

note

  text

name

  text

Definition of view employee_search

    SELECT e.entity_id
    ,
        e.startdate
    ,
        e.enddate
    ,
        e.role
    ,
        e.ssn
    ,
        e.sales
    ,
        e.manager_id
    ,
        e.employeenumber
    ,
        e.dob
    ,
        e.is_manager
    ,
        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)
         )
    );

### Table: employee_to_ec

Structure of employee_to_ec

employee_id

  integer PRIMARY KEY REFERENCES

ec_id

  integer REFERENCES

### View: employees

Structure of employees

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

is_manager

  boolean

Definition of view employees

    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
    ,
        ee.is_manager

    FROM (
         (person p

            JOIN entity_employee ee
           USING (entity_id)
         )

    LEFT JOIN salutation s
        ON (
               (p.salutation_id = s.id)
         )
    );

### Table: entity

The primary entity table to map to all contacts

Structure of entity

id

  serial UNIQUE NOT NULL

name

  text

  This is the common name of an entity. If it was a person it may be Joshua Drake, a company Acme Corp. You may also choose to use a domain such as commandprompt.com

entity_class

  integer PRIMARY KEY REFERENCES REFERENCES

created

  date NOT NULL `DEFAULT ('now'::text)::date`

control_code

  text UNIQUE PRIMARY KEY `DEFAULT setting_increment('entity_control'::character varying)`

country_id

  integer NOT NULL REFERENCES

Constraints on entity

entity_name_check

  CHECK ((name ~ '[[:alnum:]_]'::text))

Tables referencing ap via Foreign Key Constraints

### Table: entity_bank_account

This stores bank account information for both companies and persons.

Structure of entity_bank_account

id

  serial UNIQUE NOT NULL

entity_id

  integer PRIMARY KEY REFERENCES

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.

remark

  character varying

  This field contains the notes for an account, like: This is USD account, this one is HUF account, this one is the default account, this account for paying specific taxes. If a $

Tables referencing entity_credit_account via Foreign Key Constraints

### Table: entity_class

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

Structure of entity_class

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

active

  boolean NOT NULL `DEFAULT true`

Constraints on entity_class

entity_class_class_check

  CHECK ((class ~ '[[:alnum:]_]'::text))

Indexes on entity_class

entity_class_idx

  lower(class)

Tables referencing entity via Foreign Key Constraints

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

Structure of entity_credit_account

id

  serial UNIQUE NOT NULL

entity_id

  integer PRIMARY KEY REFERENCES

entity_class

  integer PRIMARY KEY REFERENCES

pay_to_name

  text

discount

  numeric

description

  text

discount_terms

  integer

discount_account_id

  integer REFERENCES

taxincluded

  boolean `DEFAULT false`

creditlimit

  numeric

terms

  smallint

meta_number

  character varying(32) PRIMARY KEY

  This stores the human readable control code for the customer/vendor record. This is typically called the customer/vendor "account" in the application.

business_id

  integer

language_code

  character varying(6) `DEFAULT 'en'::character varying` REFERENCES

pricegroup_id

  integer REFERENCES

curr

  character(3)

startdate

  date `DEFAULT ('now'::text)::date`

enddate

  date

threshold

  numeric

employee_id

  integer REFERENCES

primary_contact

  integer REFERENCES

ar_ap_account_id

  integer REFERENCES

cash_account_id

  integer REFERENCES

bank_account

  integer REFERENCES

taxform_id

  integer REFERENCES

Constraints on entity_credit_account

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 ap via Foreign Key Constraints

### Table: entity_employee

This contains employee-specific extensions to person/entity.

Structure of entity_employee

entity_id

  integer PRIMARY KEY REFERENCES

startdate

  date NOT NULL `DEFAULT ('now'::text)::date`

enddate

  date

role

  character varying(20)

ssn

  text

sales

  boolean `DEFAULT false`

manager_id

  integer REFERENCES

employeenumber

  character varying(32)

dob

  date

is_manager

  boolean `DEFAULT false`

Tables referencing ap via Foreign Key Constraints

### Table: entity_note

Structure of entity_note

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"()`

ref_key

  integer NOT NULL REFERENCES

subject

  text

entity_id

  integer REFERENCES

Constraints on entity_note

entity_note_note_class_check

  CHECK ((note_class = 1))

Indexes on entity_note

entity_note_id_idx

  id

entity_note_vectors_idx

  vector

### Table: entity_other_name

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

Structure of entity_other_name

entity_id

  integer PRIMARY KEY REFERENCES

other_name

  text PRIMARY KEY

Constraints on entity_other_name

entity_other_name_other_name_check

  CHECK ((other_name ~ '[[:alnum:]_]'::text))

### Table: entity_to_contact

This table stores contact information for entities

Structure of entity_to_contact

entity_id

  integer PRIMARY KEY REFERENCES

contact_class_id

  integer PRIMARY KEY REFERENCES

contact

  text PRIMARY KEY

description

  text

Constraints on entity_to_contact

entity_to_contact_contact_check

  CHECK ((contact ~ '[[:alnum:]_]'::text))

### Table: entity_to_location

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

Structure of entity_to_location

location_id

  integer PRIMARY KEY REFERENCES

location_class

  integer PRIMARY KEY REFERENCES

entity_id

  integer PRIMARY KEY REFERENCES

### Table: 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).

Structure of exchangerate

curr

  character(3) PRIMARY KEY

transdate

  date PRIMARY KEY

buy

  numeric

sell

  numeric

Indexes on exchangerate

exchangerate_ct_key

  curr, transdate

### Table: file_base

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

Structure of file_base

content

  bytea NOT NULL

mime_type_id

  integer NOT NULL REFERENCES

file_name

  text PRIMARY KEY

description

  text

uploaded_by

  integer NOT NULL REFERENCES

uploaded_at

  timestamp without time zone NOT NULL `DEFAULT now()`

id

  serial UNIQUE NOT NULL

ref_key

  integer PRIMARY KEY

  This column inheriting tables is used to reference the database row for the attachment. Inheriting tables MUST set the foreign key here appropriately. This can also be used to create classifications of other documents, such as by source of automatic import (where the file is not yet attached) or even standard, long-lived documents.

file_class

  integer PRIMARY KEY REFERENCES

### Table: file_class

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

Structure of file_class

id

  serial UNIQUE NOT NULL

class

  text PRIMARY KEY

Tables referencing file_base via Foreign Key Constraints

### Table: file_eca

File attachments primarily attached to customer and vendor agreements.

Structure of file_eca

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)`

ref_key

  integer PRIMARY KEY REFERENCES

file_class

  integer PRIMARY KEY

Constraints on file_eca

file_eca_file_class_check

  CHECK ((file_class = 5))

### Table: file_entity

File attachments primarily attached to entities.

Structure of file_entity

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)`

ref_key

  integer PRIMARY KEY REFERENCES

file_class

  integer PRIMARY KEY

Constraints on file_entity

file_entity_file_class_check

  CHECK ((file_class = 4))

### Table: file_incoming

This is essentially a spool for files to be reviewed and attached. It is important that the names are somehow guaranteed to be unique, so one may want to prepend them with an email equivalent or the like.

Structure of file_incoming

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)`

ref_key

  integer PRIMARY KEY

  Always must be 0, and we have no primary key since these files all are for interal incoming use, not categorized.

file_class

  integer PRIMARY KEY

Constraints on file_incoming

file_incoming_file_class_check

  CHECK ((file_class = 7))

file_incoming_ref_key_check

  CHECK ((ref_key = 0))

### Table: file_internal

This is for internal files used operationally by LedgerSMB. For example, company logos would be here.

Structure of file_internal

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)`

ref_key

  integer PRIMARY KEY

  Always must be 0, and we have no primary key since these files all are for internal use and against the company, not categorized.

file_class

  integer PRIMARY KEY

Constraints on file_internal

file_internal_file_class_check

  CHECK ((file_class = 6))

file_internal_ref_key_check

  CHECK ((ref_key = 0))

### View: file_links

Structure of file_links

file_id

  integer

ref_key

  integer

reference

  text

type

  text

dest_class

  integer

source_class

  integer

dest_ref

  integer

Definition of view file_links

    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

    UNION

    SELECT 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;

### Table: file_order

File attachments primarily attached to orders and quotations.

Structure of file_order

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)`

ref_key

  integer PRIMARY KEY REFERENCES

file_class

  integer PRIMARY KEY

Constraints on file_order

file_order_file_class_check

  CHECK ((file_class = 2))

Tables referencing file_order_to_order via Foreign Key Constraints

### View: file_order_links

Structure of file_order_links

file_id

  integer

ref_key

  integer

reference

  text

oe_class

  text

dest_class

  integer

source_class

  integer

dest_ref

  integer

Definition of view file_order_links

    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);

### Table: file_order_to_order

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

Structure of file_order_to_order

file_id

  integer PRIMARY KEY REFERENCES

source_class

  integer PRIMARY KEY

ref_key

  integer PRIMARY KEY REFERENCES

dest_class

  integer PRIMARY KEY

attached_by

  integer NOT NULL

attached_at

  timestamp without time zone NOT NULL `DEFAULT now()`

Constraints on file_order_to_order

file_order_to_order_dest_class_check

  CHECK ((dest_class = 2))

file_order_to_order_source_class_check

  CHECK ((source_class = 2))

### Table: file_order_to_tx

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

Structure of file_order_to_tx

file_id

  integer PRIMARY KEY REFERENCES

source_class

  integer PRIMARY KEY

ref_key

  integer PRIMARY KEY REFERENCES

dest_class

  integer PRIMARY KEY

attached_by

  integer NOT NULL

attached_at

  timestamp without time zone NOT NULL `DEFAULT now()`

Constraints on file_order_to_tx

file_order_to_tx_dest_class_check

  CHECK ((dest_class = 1))

file_order_to_tx_source_class_check

  CHECK ((source_class = 2))

### Table: file_part

File attachments primarily attached to goods and services.

Structure of file_part

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)`

ref_key

  integer PRIMARY KEY REFERENCES

file_class

  integer PRIMARY KEY

Constraints on file_part

file_part_file_class_check

  CHECK ((file_class = 3))

### Table: file_secondary_attachment

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

Structure of file_secondary_attachment

file_id

  integer PRIMARY KEY

source_class

  integer PRIMARY KEY REFERENCES

ref_key

  integer PRIMARY KEY

dest_class

  integer PRIMARY KEY REFERENCES

attached_by

  integer NOT NULL REFERENCES

attached_at

  timestamp without time zone NOT NULL `DEFAULT now()`

### Table: file_transaction

File attachments primarily attached to AR/AP/GL.

Structure of file_transaction

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)`

ref_key

  integer PRIMARY KEY REFERENCES

file_class

  integer PRIMARY KEY

Constraints on file_transaction

file_transaction_file_class_check

  CHECK ((file_class = 1))

Tables referencing file_tx_to_order via Foreign Key Constraints

### View: file_tx_links

Structure of file_tx_links

file_id

  integer

ref_key

  integer

reference

  text

type

  text

dest_class

  integer

source_class

  integer

dest_ref

  integer

Definition of view file_tx_links

    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_1.id
               ,
                gl_1.reference
               ,
                'gl'::text AS type

            FROM gl gl_1

           UNION

          SELECT ar.id
               ,
                ar.invnumber
               ,
                    CASE
                        WHEN ar.invoice THEN 'is'::text
                        ELSE 'ar'::text
                    END AS type

            FROM ar

           UNION

          SELECT 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)
               )
         )
    );

### Table: file_tx_to_order

Secondary links from journal entries to orders.

Structure of file_tx_to_order

file_id

  integer PRIMARY KEY REFERENCES

source_class

  integer PRIMARY KEY

ref_key

  integer PRIMARY KEY REFERENCES

dest_class

  integer PRIMARY KEY

attached_by

  integer NOT NULL

attached_at

  timestamp without time zone NOT NULL `DEFAULT now()`

Constraints on file_tx_to_order

file_tx_to_order_dest_class_check

  CHECK ((dest_class = 2))

file_tx_to_order_source_class_check

  CHECK ((source_class = 1))

### Table: file_view_catalog

Structure of file_view_catalog

file_class

  integer PRIMARY KEY REFERENCES

view_name

  text UNIQUE NOT NULL

### Table: fixes

Structure of fixes

checksum

  text PRIMARY KEY

path

  text NOT NULL

stdout

  text

stderr

  text

applied_at

  timestamp without time zone `DEFAULT now()`

### Table: gifi

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

Structure of gifi

accno

  text PRIMARY KEY

description

  text

### Table: gl

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

Structure of gl

id

  integer PRIMARY KEY `DEFAULT nextval('id'::regclass)` REFERENCES

reference

  text

description

  text

transdate

  date `DEFAULT ('now'::text)::date`

person_id

  integer REFERENCES

  the person_id of the employee who created the entry.

notes

  text

approved

  boolean `DEFAULT true`

Tables referencing asset_report via Foreign Key Constraints

### Table: inventory_report

Structure of inventory_report

id

  serial PRIMARY KEY

transdate

  date NOT NULL

source

  text

ar_trans_id

  integer

ap_trans_id

  integer

Tables referencing inventory_report_line via Foreign Key Constraints

### Table: inventory_report_line

Structure of inventory_report_line

adjust_id

  integer PRIMARY KEY REFERENCES

parts_id

  integer PRIMARY KEY REFERENCES

counted

  numeric

expected

  numeric

variance

  numeric

### Table: invoice

Line items of invoices with goods/services attached.

Structure of invoice

id

  serial PRIMARY KEY

trans_id

  integer REFERENCES

parts_id

  integer REFERENCES

description

  text

qty

  numeric

  Positive is normal for sales invoices, negative for vendor invoices.

allocated

  numeric

  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

vendor_sku

  text

notes

  text

Constraints on invoice

invoice_allocation_constraint

  CHECK ((((allocated * ('-1'::integer)::numeric) >= LEAST((0)::numeric, qty)) AND ((allocated * ('-1'::integer)::numeric) <= GREATEST(qty, (0)::numeric))))

Indexes on invoice

invoice_id_key

  id

invoice_trans_id_key

  trans_id

Tables referencing acc_trans via Foreign Key Constraints

### Table: invoice_note

Structure of invoice_note

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"()`

ref_key

  integer NOT NULL REFERENCES

subject

  text

Indexes on invoice_note

invoice_note_id_idx

  id

invoice_note_vectors_idx

  vector

### View: invoice_sum

Structure of invoice_sum

transdate

  date

qty

  numeric

parts_id

  integer

Definition of view invoice_sum

    SELECT a.transdate
    ,
        sum
    (i.qty) AS qty
    ,
        i.parts_id

    FROM (invoice i

      JOIN (
          SELECT ar.id
               ,
                ar.transdate

            FROM ar

           WHERE ar.approved

           UNION

          SELECT ap.id
               ,
                ap.transdate

            FROM ap

           WHERE ap.approved
         ) a
        ON (
               (i.trans_id = a.id)
         )
    )

    GROUP BY a.transdate
    , i.parts_id;

### Table: invoice_tax_form

Maping invoice to country_tax_form.

Structure of invoice_tax_form

invoice_id

  integer PRIMARY KEY REFERENCES

reportable

  boolean

### Table: jcitems

Time and materials cards. Materials cards not implemented.

Structure of jcitems

id

  serial PRIMARY KEY

business_unit_id

  integer REFERENCES

parts_id

  integer

description

  text

qty

  numeric

allocated

  numeric

sellprice

  numeric

fxsellprice

  numeric

serialnumber

  text

checkedin

  timestamp with time zone

checkedout

  timestamp with time zone

person_id

  integer NOT NULL REFERENCES

notes

  text

total

  numeric NOT NULL

non_billable

  numeric NOT NULL

jctype

  integer NOT NULL

curr

  character(3) NOT NULL

Indexes on jcitems

jcitems_id_key

  id

### Table: jctype

Structure of jctype

id

  integer UNIQUE NOT NULL

label

  text PRIMARY KEY

description

  text NOT NULL

is_service

  boolean `DEFAULT true`

is_timecard

  boolean `DEFAULT true`

### Table: job

Structure of job

bu_id

  integer PRIMARY KEY REFERENCES

parts_id

  integer

  Job costing/manufacturing here not implemented.

production

  numeric

completed

  numeric

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

Structure of journal_entry

id

  serial PRIMARY KEY

reference

  text

  Invoice number or journal entry number.

description

  text

locked_by

  integer REFERENCES

journal

  integer REFERENCES

post_date

  date `DEFAULT now()`

effective_start

  date

  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

  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.

entered_by

  integer NOT NULL REFERENCES

approved_by

  integer REFERENCES

Constraints on journal_entry

journal_entry_check

  CHECK (((is_template IS FALSE) OR (approved IS FALSE)))

journal_entry_check1

  CHECK ((is_template OR (reference IS NOT NULL)))

Tables referencing eca_invoice via Foreign Key Constraints

### Table: journal_line

Replaces acc_trans as the main account transaction line table.

Structure of journal_line

id

  serial PRIMARY KEY

account_id

  integer NOT NULL REFERENCES

journal_id

  integer NOT NULL REFERENCES

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.

reconciliation_report

  integer REFERENCES

line_type

  text REFERENCES

Constraints on journal_line

journal_line_amount_check

  CHECK ((amount <> 'NaN'::numeric))

Tables referencing business_unit_jl via Foreign Key Constraints

### Table: journal_note

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

Structure of journal_note

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"()`

ref_key

  integer NOT NULL REFERENCES

subject

  text

internal_only

  boolean NOT NULL `DEFAULT false`

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

Constraints on journal_note

journal_note_note_class_check

  CHECK ((note_class = 5))

### Table: 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

Structure of journal_type

id

  serial UNIQUE NOT NULL

name

  text PRIMARY KEY

Tables referencing journal_entry via Foreign Key Constraints

### Table: language

Languages for manual translations and so forth.

Structure of language

code

  character varying(6) PRIMARY KEY

description

  text

Tables referencing eca_invoice via Foreign Key Constraints

### Table: location

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

Structure of location

id

  serial PRIMARY KEY

line_one

  text NOT NULL

line_two

  text

line_three

  text

city

  text NOT NULL

state

  text

country_id

  integer NOT NULL REFERENCES

mail_code

  text

created

  date NOT NULL `DEFAULT now()`

inactive_date

  timestamp without time zone

active

  boolean NOT NULL `DEFAULT true`

Constraints on location

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 eca_to_location via Foreign Key Constraints

### Table: location_class

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

Structure of location_class

id

  serial UNIQUE NOT NULL

class

  text PRIMARY KEY

authoritative

  boolean PRIMARY KEY

Constraints on location_class

location_class_class_check

  CHECK ((class ~ '[[:alnum:]_]'::text))

Tables referencing eca_to_location via Foreign Key Constraints

### Table: location_class_to_entity_class

This determines which location classes go with which entity classes

Structure of location_class_to_entity_class

id

  serial UNIQUE NOT NULL

location_class

  integer NOT NULL REFERENCES

entity_class

  integer NOT NULL REFERENCES

### Table: lsmb_group

Structure of lsmb_group

role_name

  text PRIMARY KEY

Tables referencing lsmb_group_grants via Foreign Key Constraints

### Table: lsmb_group_grants

Structure of lsmb_group_grants

group_name

  text PRIMARY KEY REFERENCES

granted_role

  text PRIMARY KEY

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

Structure of lsmb_module

id

  integer UNIQUE NOT NULL

label

  text PRIMARY KEY

Tables referencing bu_class_to_module via Foreign Key Constraints

### Table: lsmb_sequence

Structure of lsmb_sequence

label

  text PRIMARY KEY

setting_key

  text NOT NULL REFERENCES

prefix

  text

suffix

  text

sequence

  text NOT NULL `DEFAULT '1'::text`

accept_input

  boolean `DEFAULT true`

### Table: makemodel

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

Structure of makemodel

parts_id

  integer PRIMARY KEY REFERENCES

barcode

  text

make

  text PRIMARY KEY

model

  text PRIMARY KEY

Indexes on makemodel

makemodel_make_key

  lower(make)

makemodel_model_key

  lower(model)

makemodel_parts_id_key

  parts_id

### Table: menu_acl

Provides access control list entries for menu nodes.

Structure of menu_acl

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

node_id

  integer PRIMARY KEY REFERENCES

Constraints on menu_acl

menu_acl_acl_type_check

  CHECK ((((acl_type)::text = 'allow'::text) OR ((acl_type)::text = 'deny'::text)))

Indexes on menu_acl

menu_acl_node_id_idx

  node_id

### Table: menu_attribute

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

Structure of menu_attribute

node_id

  integer PRIMARY KEY REFERENCES

attribute

  character varying PRIMARY KEY

value

  character varying NOT NULL

id

  serial NOT NULL

### View: menu_friendly

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

Structure of menu_friendly

level

  integer

path

  text

label

  text

id

  integer

position

  integer

Definition of view menu_friendly

     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)

     UNION

    SELECT (
               (t_1.path ||
                   ','::text
               ) ||
               (n_1.id)::text
         )
         ,
                n_1.id
         ,
                n_1.parent
         ,

         (t_1.level + 1)
         ,

         (
               (t_1.positions ||
                   ','::text
               ) || n_1."position"
         )

      FROM (menu_node n_1

            JOIN tree t_1
              ON (
                     (t_1.id = n_1.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[];

### Table: menu_node

This table stores the tree structure of the menu.

Structure of menu_node

id

  serial PRIMARY KEY

label

  character varying NOT NULL

parent

  integer UNIQUE#1 REFERENCES

position

  integer UNIQUE#1 NOT NULL

Tables referencing menu_acl via Foreign Key Constraints

### Table: mfg_lot

This tracks assembly restocks. This is designed to work with old code and may change as we refactor the parts.

Structure of mfg_lot

id

  serial UNIQUE NOT NULL

lot_number

  text UNIQUE NOT NULL `DEFAULT (nextval('lot_tracking_number'::regclass))::text`

parts_id

  integer NOT NULL REFERENCES

qty

  numeric NOT NULL

stock_date

  date NOT NULL `DEFAULT (now())::date`

Tables referencing mfg_lot_item via Foreign Key Constraints

### Table: mfg_lot_item

This tracks items used in assembly restocking.

Structure of mfg_lot_item

id

  serial UNIQUE NOT NULL

mfg_lot_id

  integer NOT NULL REFERENCES

parts_id

  integer NOT NULL REFERENCES

qty

  numeric NOT NULL

### Table: mime_type

This is a lookup table for storing MIME types.

Structure of mime_type

id

  serial UNIQUE NOT NULL

mime_type

  text PRIMARY KEY

invoice_include

  boolean `DEFAULT false`

Tables referencing file_base via Foreign Key Constraints

### Table: new_shipto

Tracks ship_to information for orders and invoices.

Structure of new_shipto

id

  serial PRIMARY KEY

trans_id

  integer REFERENCES

oe_id

  integer REFERENCES

location_id

  integer REFERENCES

### Table: note

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

Structure of note

id

  serial PRIMARY KEY

note_class

  integer NOT NULL REFERENCES

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

### Table: note_class

Coordinate with others before adding entries.

Structure of note_class

id

  serial PRIMARY KEY

class

  text NOT NULL

Constraints on note_class

note_class_class_check

  CHECK ((class ~ '[[:alnum:]_]'::text))

Tables referencing note via Foreign Key Constraints

### Table: oe

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

Structure of oe

id

  serial PRIMARY KEY

ordnumber

  text

transdate

  date `DEFAULT ('now'::text)::date`

entity_id

  integer REFERENCES

amount

  numeric

netamount

  numeric

reqdate

  date

taxincluded

  boolean

shippingpoint

  text

notes

  text

curr

  character(3)

person_id

  integer REFERENCES

closed

  boolean `DEFAULT false`

quotation

  boolean `DEFAULT false`

quonumber

  text

intnotes

  text

shipvia

  text

language_code

  character varying(6)

ponumber

  text

terms

  smallint

entity_credit_account

  integer NOT NULL REFERENCES

oe_class_id

  integer NOT NULL REFERENCES

Indexes on oe

oe_id_key

  id

oe_ordnumber_key

  ordnumber

oe_transdate_key

  transdate

Tables referencing file_order via Foreign Key Constraints

### Table: oe_class

Hardwired classifications for orders and quotations. Coordinate before adding.

Structure of oe_class

id

  smallint UNIQUE

oe_class

  text PRIMARY KEY

Constraints on oe_class

oe_class_id_check

  CHECK ((id = ANY (ARRAY[1, 2, 3, 4])))

Tables referencing oe via Foreign Key Constraints

### Table: open_forms

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

Structure of open_forms

id

  serial PRIMARY KEY

session_id

  integer REFERENCES

form_name

  character varying(100)

last_used

  timestamp without time zone

### View: order_sum

Structure of order_sum

transdate

  date

qty

  numeric

parts_id

  integer

Definition of view order_sum

    SELECT oe.transdate
    ,
        sum
    (
         (oi.ship *
               (
            CASE
                WHEN
                     (oe.oe_class_id = 1) THEN 1
                ELSE '-1'::integer
            END
               )::numeric
         )
    ) AS qty
    ,
        oi.parts_id

    FROM (orderitems oi

      JOIN oe
        ON (
               (
                     (oe.closed IS FALSE)
                   AND (oe.oe_class_id = ANY
                           (ARRAY[1
                                 , 2]
                           )
                     )
               )
         )
    )

    GROUP BY oe.transdate
    , oi.parts_id;

### Table: orderitems

Line items for sales/purchase orders and quotations.

Structure of orderitems

id

  serial PRIMARY KEY

trans_id

  integer

parts_id

  integer REFERENCES

description

  text

qty

  numeric

sellprice

  numeric

precision

  integer

discount

  numeric

unit

  character varying(5)

reqdate

  date

ship

  numeric

serialnumber

  text

notes

  text

Indexes on orderitems

orderitems_id_key

  id

orderitems_trans_id_key

  trans_id

Tables referencing business_unit_oitem via Foreign Key Constraints

### View: overpayments

Structure of overpayments

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)

Definition of view overpayments

    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
    ,

    (sum
         (ac.amount) *
         (
            CASE
                WHEN
               (eca.entity_class = 1) THEN '-1'::integer
                ELSE 1
            END
         )::numeric
    ) 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
    , eca.entity_class;

### Table: parts

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

Structure of parts

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 REFERENCES

income_accno_id

  integer REFERENCES

expense_accno_id

  integer REFERENCES

returns_accno_id

  integer REFERENCES

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

Indexes on parts

parts_description_key

  lower(description)

parts_id_key

  id

parts_partnumber_key

  lower(partnumber)

Tables referencing assembly via Foreign Key Constraints

### Table: parts_translation

Translation information for parts.

Structure of parts_translation

trans_id

  integer PRIMARY KEY REFERENCES

language_code

  character varying(6) PRIMARY KEY

description

  text

### Table: partscustomer

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

Structure of partscustomer

parts_id

  integer REFERENCES

credit_id

  integer REFERENCES

pricegroup_id

  integer REFERENCES

pricebreak

  numeric

sellprice

  numeric

validfrom

  date

validto

  date

curr

  character(3)

entry_id

  serial PRIMARY KEY

qty

  numeric

### Table: partsgroup

Groups of parts for Point of Sale screen.

Structure of partsgroup

id

  serial PRIMARY KEY

partsgroup

  text

parent

  integer REFERENCES

Indexes on partsgroup

partsgroup_id_key

  id

Tables referencing partsgroup via Foreign Key Constraints

### Table: partsgroup_translation

Translation information for partsgroups.

Structure of partsgroup_translation

trans_id

  integer PRIMARY KEY REFERENCES

language_code

  character varying(6) PRIMARY KEY

description

  text

### Table: partstax

Mapping of parts to taxes.

Structure of partstax

parts_id

  integer PRIMARY KEY REFERENCES

chart_id

  integer PRIMARY KEY REFERENCES

taxcategory_id

  integer REFERENCES

Indexes on partstax

partstax_parts_id_key

  parts_id

### Table: partsvendor

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

Structure of partsvendor

credit_id

  integer NOT NULL REFERENCES

parts_id

  integer

partnumber

  text

leadtime

  smallint

lastcost

  numeric

curr

  character(3)

entry_id

  serial PRIMARY KEY

Indexes on partsvendor

partsvendor_parts_id_key

  parts_id

### Table: payment

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

Structure of payment

id

  serial PRIMARY KEY

reference

  text NOT NULL

  This field will store the code for both receipts and payment order

gl_id

  integer REFERENCES

  A payment should always be linked to a GL movement

payment_class

  integer NOT NULL

payment_date

  date `DEFAULT ('now'::text)::date`

closed

  boolean `DEFAULT false`

  This will store the current state of a payment/receipt order

entity_credit_id

  integer REFERENCES

employee_id

  integer REFERENCES

currency

  character(3)

notes

  text

Indexes on payment

payment_id_idx

  id

Tables referencing payment_links via Foreign Key Constraints

### Table: payment_links

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

Structure of payment_links

payment_id

  integer REFERENCES

entry_id

  integer REFERENCES

type

  integer

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

Structure of payment_map

line_id

  integer PRIMARY KEY REFERENCES

pays

  integer NOT NULL REFERENCES

### Table: payment_type

Structure of payment_type

id

  serial UNIQUE NOT NULL

label

  text PRIMARY KEY

### Table: payroll_deduction

Structure of payroll_deduction

entry_id

  serial UNIQUE NOT NULL

entity_id

  integer PRIMARY KEY REFERENCES

type_id

  integer PRIMARY KEY REFERENCES

rate

  numeric NOT NULL

### Table: payroll_deduction_class

Structure of payroll_deduction_class

id

  integer UNIQUE#1 NOT NULL

country_id

  integer UNIQUE#1 PRIMARY KEY REFERENCES

label

  text PRIMARY KEY

stored_proc_name

  name NOT NULL

Tables referencing payroll_deduction_type via Foreign Key Constraints

### Table: payroll_deduction_type

Structure of payroll_deduction_type

id

  serial UNIQUE NOT NULL

account_id

  integer NOT NULL REFERENCES

pdc_id

  integer NOT NULL REFERENCES

country_id

  integer NOT NULL REFERENCES

label

  text NOT NULL

unit

  text NOT NULL

default_amount

  numeric

calc_percent

  boolean NOT NULL

Tables referencing payroll_deduction via Foreign Key Constraints

### Table: payroll_employee_class

Structure of payroll_employee_class

id

  serial UNIQUE NOT NULL

label

  text PRIMARY KEY

Tables referencing payroll_employee_class_to_income_type via Foreign Key Constraints

### Table: payroll_employee_class_to_income_type

Structure of payroll_employee_class_to_income_type

ec_id

  integer PRIMARY KEY REFERENCES

it_id

  integer PRIMARY KEY REFERENCES

### Table: payroll_income_category

Structure of payroll_income_category

id

  serial UNIQUE NOT NULL

label

  text

### Table: payroll_income_class

Structure of payroll_income_class

id

  integer UNIQUE#1 NOT NULL

country_id

  integer UNIQUE#1 PRIMARY KEY REFERENCES

label

  text PRIMARY KEY

Tables referencing payroll_income_type via Foreign Key Constraints

### Table: payroll_income_type

Structure of payroll_income_type

id

  serial UNIQUE NOT NULL

account_id

  integer NOT NULL REFERENCES

pic_id

  integer NOT NULL REFERENCES

country_id

  integer NOT NULL REFERENCES

label

  text NOT NULL

unit

  text NOT NULL

default_amount

  numeric

Tables referencing payroll_employee_class_to_income_type via Foreign Key Constraints

### Table: payroll_paid_timeoff

Structure of payroll_paid_timeoff

employee_id

  integer NOT NULL REFERENCES

pto_class_id

  integer NOT NULL REFERENCES

report_id

  integer NOT NULL REFERENCES

amount

  numeric NOT NULL

### Table: payroll_pto_class

Structure of payroll_pto_class

id

  serial UNIQUE NOT NULL

label

  text PRIMARY KEY

Tables referencing payroll_paid_timeoff via Foreign Key Constraints

### Table: payroll_report

Structure of payroll_report

id

  serial PRIMARY KEY

ec_id

  integer NOT NULL REFERENCES

payment_date

  date NOT NULL

created_by

  integer REFERENCES

approved_by

  integer REFERENCES

Tables referencing payroll_paid_timeoff via Foreign Key Constraints

### Table: payroll_report_line

Structure of payroll_report_line

id

  serial UNIQUE NOT NULL

report_id

  integer PRIMARY KEY REFERENCES

employee_id

  integer PRIMARY KEY REFERENCES

it_id

  integer PRIMARY KEY REFERENCES

qty

  numeric NOT NULL

rate

  numeric NOT NULL

description

  text

### Table: payroll_wage

Structure of payroll_wage

entry_id

  serial UNIQUE NOT NULL

entity_id

  integer PRIMARY KEY REFERENCES

type_id

  integer PRIMARY KEY REFERENCES

rate

  numeric NOT NULL

### View: periods

Structure of periods

id

  text

label

  text

date_to

  date

date_from

  date

Definition of view periods

    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

    UNION

    SELECT'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;

### Table: person

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

Structure of person

id

  serial PRIMARY KEY

entity_id

  integer UNIQUE NOT NULL REFERENCES

salutation_id

  integer REFERENCES

first_name

  text NOT NULL

middle_name

  text

last_name

  text NOT NULL

created

  date NOT NULL `DEFAULT ('now'::text)::date`

birthdate

  date

personal_id

  text

Constraints on person

person_first_name_check

  CHECK ((first_name ~ '[[:alnum:]_]'::text))

person_last_name_check

  CHECK ((last_name ~ '[[:alnum:]_]'::text))

Tables referencing audittrail via Foreign Key Constraints

### Table: person_to_company

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

Structure of person_to_company

location_id

  integer PRIMARY KEY REFERENCES

person_id

  integer PRIMARY KEY REFERENCES

company_id

  integer NOT NULL REFERENCES

### Table: pricegroup

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

Structure of pricegroup

id

  serial PRIMARY KEY

pricegroup

  text

Indexes on pricegroup

pricegroup_id_key

  id

pricegroup_pricegroup_key

  pricegroup

Tables referencing entity_credit_account via Foreign Key Constraints

### View: recon_payee

Structure of recon_payee

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

Definition of view recon_payee

    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)
                     )
               )

           UNION

          SELECT 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)
                     )
               )

           UNION

          SELECT gl_1.id
               ,
                gl_1.description

            FROM gl gl_1
         ) n
        ON (
               (n.id = ac.trans_id)
         )
    );

### Table: recurring

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

Structure of recurring

id

  integer UNIQUE NOT NULL REFERENCES

reference

  text

startdate

  date

nextdate

  date

enddate

  date

recurring_interval

  interval

howmany

  integer

payment

  boolean `DEFAULT false`

Tables referencing recurringemail via Foreign Key Constraints

### Table: recurringemail

Email to be sent out when recurring transaction is posted.

Structure of recurringemail

id

  integer PRIMARY KEY REFERENCES

formname

  text PRIMARY KEY

format

  text

message

  text

### Table: recurringprint

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

Structure of recurringprint

id

  integer PRIMARY KEY REFERENCES

formname

  text PRIMARY KEY

format

  text

printer

  text

### Table: robot

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

Structure of robot

id

  serial PRIMARY KEY

entity_id

  integer UNIQUE NOT NULL REFERENCES

first_name

  text

middle_name

  text

last_name

  text NOT NULL

created

  date NOT NULL `DEFAULT ('now'::text)::date`

Constraints on robot

robot_first_name_check

  CHECK ((first_name ~ '[[:alnum:] _\-\.\*]?'::text))

robot_last_name_check

  CHECK ((last_name ~ '[[:alnum:] _\-\.\*]'::text))

### View: role_view

Structure of role_view

roleid

  oid

member

  oid

grantor

  oid

admin_option

  boolean

rolname

  name

rolsuper

  boolean

rolinherit

  boolean

rolcreaterole

  boolean

rolcreatedb

  boolean

rolcanlogin

  boolean

rolreplication

  boolean

rolconnlimit

  integer

rolpassword

  text

rolvaliduntil

  timestamp with time zone

rolbypassrls

  boolean

rolconfig

  text[]

oid

  oid

Definition of view role_view

    SELECT m.roleid
    ,
        m.member
    ,
        m.grantor
    ,
        m.admin_option
    ,
        a.rolname
    ,
        a.rolsuper
    ,
        a.rolinherit
    ,
        a.rolcreaterole
    ,
        a.rolcreatedb
    ,
        a.rolcanlogin
    ,
        a.rolreplication
    ,
        a.rolconnlimit
    ,
        a.rolpassword
    ,
        a.rolvaliduntil
    ,
        a.rolbypassrls
    ,
        a.rolconfig
    ,
        a.oid

    FROM (pg_auth_members m

      JOIN pg_roles a
        ON (
               (m.roleid = a.oid)
         )
    );

### Table: salutation

Structure of salutation

id

  serial UNIQUE NOT NULL

salutation

  text PRIMARY KEY

Tables referencing person via Foreign Key Constraints

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

Structure of session

session_id

  serial PRIMARY KEY

token

  character varying(32)

last_used

  timestamp without time zone `DEFAULT now()`

ttl

  integer NOT NULL `DEFAULT 3600`

users_id

  integer NOT NULL REFERENCES

notify_pasword

  interval NOT NULL `DEFAULT '7 days'::interval`

Constraints on session

session_token_check

  CHECK ((length((token)::text) = 32))

Tables referencing batch via Foreign Key Constraints

### Table: sic

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

Structure of sic

code

  character varying(6) PRIMARY KEY

sictype

  character(1)

description

  text

### Table: status

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

Structure of status

trans_id

  integer PRIMARY KEY

formname

  text PRIMARY KEY

printed

  boolean `DEFAULT false`

emailed

  boolean `DEFAULT false`

spoolfile

  text

Indexes on status

status_trans_id_key

  trans_id

### Table: tax

Information on tax rates.

Structure of tax

chart_id

  integer PRIMARY KEY REFERENCES

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

taxmodule_id

  integer NOT NULL `DEFAULT 1` REFERENCES

### Table: tax_extended

This stores extended information for manual tax calculations.

Structure of tax_extended

tax_basis

  numeric

rate

  numeric

entry_id

  integer PRIMARY KEY REFERENCES

### Table: taxcategory

Structure of taxcategory

taxcategory_id

  serial PRIMARY KEY

taxcategoryname

  text NOT NULL

taxmodule_id

  integer NOT NULL REFERENCES

Tables referencing partstax via Foreign Key Constraints

### Table: taxmodule

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

Structure of taxmodule

taxmodule_id

  serial PRIMARY KEY

taxmodulename

  text NOT NULL

Tables referencing tax via Foreign Key Constraints

### Table: template

Structure of template

id

  serial UNIQUE NOT NULL

template_name

  text UNIQUE#1 NOT NULL

language_code

  character varying(6) UNIQUE#1 REFERENCES

template

  text NOT NULL

format

  text UNIQUE#1 NOT NULL

last_modified

  timestamp with time zone NOT NULL `DEFAULT now()`

### Table: transactions

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

Structure of transactions

id

  integer PRIMARY KEY

table_name

  text

locked_by

  integer REFERENCES

  This should only be used in pessimistic locking measures as required by large batch work flows.

approved

  boolean

approved_by

  integer REFERENCES

approved_at

  timestamp without time zone

Indexes on transactions

transactions_locked_by_i

  locked_by

Tables referencing acc_trans via Foreign Key Constraints

### Table: translation

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

Structure of translation

trans_id

  integer PRIMARY KEY

language_code

  character varying(6) PRIMARY KEY

description

  text

Indexes on translation

translation_trans_id_key

  trans_id

### Table: trial_balance__yearend_types

Structure of trial_balance__yearend_types

type

  text PRIMARY KEY

### View: tx_report

This view provides join and approval information for transactions.

Structure of tx_report

id

  integer

reference

  text

entity_credit_account

  integer

table

  text

approved

  boolean

Definition of view tx_report

    SELECT gl.id
    ,
        gl.reference
    ,
        NULL::integer AS entity_credit_account
    ,
        'gl'::text AS "table"
    ,
        gl.approved

    FROM gl

    UNION ALL

    SELECT ap.id
    ,
        ap.invnumber AS reference
    ,
        ap.entity_credit_account
    ,
        'ap'::text AS "table"
    ,
        ap.approved

    FROM ap

    UNION ALL

    SELECT ar.id
    ,
        ar.invnumber AS reference
    ,
        ar.entity_credit_account
    ,
        'ar'::text AS "table"
    ,
        ar.approved

    FROM ar;

### View: user_listable

Structure of user_listable

id

  integer

username

  character varying(30)

created

  date

Definition of view user_listable

    SELECT u.id
    ,
        u.username
    ,
        e.created

    FROM (entity e

      JOIN users u
        ON (
               (u.entity_id = e.id)
         )
    );

### Table: user_preference

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

Structure of user_preference

id

  integer PRIMARY KEY REFERENCES

language

  character varying(6) REFERENCES

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`

### Table: users

Structure of users

id

  serial UNIQUE NOT NULL

username

  character varying(30) PRIMARY KEY

notify_password

  interval NOT NULL `DEFAULT '7 days'::interval`

entity_id

  integer NOT NULL REFERENCES

Tables referencing session via Foreign Key Constraints

### Table: voucher

Mapping transactions to batches for batch approval.

Structure of voucher

trans_id

  integer NOT NULL REFERENCES

batch_id

  integer NOT NULL REFERENCES

id

  serial PRIMARY KEY

  This is simply a surrogate key for easy reference.

batch_class

  integer NOT NULL REFERENCES

  This is the authoritative class of the voucher.

Tables referencing acc_trans via Foreign Key Constraints

### Table: warehouse

Structure of warehouse

id

  serial PRIMARY KEY

description

  text

Tables referencing asset_item via Foreign Key Constraints

### Table: warehouse_inventory

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

Structure of warehouse_inventory

entity_id

  integer REFERENCES

warehouse_id

  integer

parts_id

  integer

trans_id

  integer

orderitems_id

  integer

qty

  numeric

shippingdate

  date

entry_id

  serial PRIMARY KEY

### Table: yearend

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

Structure of yearend

trans_id

  integer PRIMARY KEY REFERENCES

reversed

  boolean `DEFAULT false`

transdate

  date

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

_entity_location_save(in_entity_id integer, in_location_id integer, in_location_class integer, in_line_one text, in_line_two text, in_line_three text, in_city text, in_state text, in_mail_code text, in_country_id integer)

Function Properties

Language Return Type
- PLPGSQL integer

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;

### account__all_headings()

account__all_headings()

Function Properties

Language Return Type
- SQL SET OF account_heading

    SELECT * FROM account_heading ORDER BY accno;

### account__delete(in_id integer)

account__delete(in_id integer)

Function Properties

Language Return Type
- PLPGSQL boolean

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 tax WHERE chart_id = in_id;
    DELETE FROM account_link WHERE account_id = in_id;
    DELETE FROM account WHERE id = in_id;
    RETURN FOUND;
    END;

### account__delete_translation(in_id integer, in_language_code text)

account__delete_translation(in_id integer, in_language_code text)

Function Properties

Language Return Type
- SQL void

Deletes the translation for the account+language combination.

       DELETE FROM account_translation
        WHERE trans_id = $1
          AND language_code = $2;

### account__get_by_accno(in_accno text)

account__get_by_accno(in_accno text)

Function Properties

Language Return Type
- SQL account

    SELECT * FROM account WHERE accno = $1;

### account__get_by_link_desc(in_description text)

account__get_by_link_desc(in_description text)

Function Properties

Language Return Type
- SQL SET OF account

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);

### account__get_from_accno(in_accno text)

account__get_from_accno(in_accno text)

Function Properties

Language Return Type
- SQL account

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

         select * from account where accno = $1;

### account__get_taxes()

account__get_taxes()

Function Properties

Language Return Type
- SQL SET OF account

Returns set of accounts where the tax attribute is true.

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

### account__is_recon(in_accno text)

account__is_recon(in_accno text)

Function Properties

Language Return Type
- SQL boolean

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;

### account__list_by_heading()

account__list_by_heading()

Function Properties

Language Return Type
- SQL SET OF account

    SELECT * FROM account ORDER BY heading;

### account__list_translations(in_id integer)

account__list_translations(in_id integer)

Function Properties

Language Return Type
- SQL SET OF account_translation

Returns the list of translations for the given account.

       SELECT * FROM account_translation WHERE trans_id = $1;

### account__obtain_balance(in_transdate date, in_account_id integer)

account__obtain_balance(in_transdate date, in_account_id integer)

Function Properties

Language Return Type
- SQL numeric

Returns the account balance at a given point in time, calculating forward from most recent check point. This function is inclusive of in_transdate. For an exclusive function see account__obtain_starting_balance below.

    WITH cp AS (
      SELECT amount, end_date, account_id
        FROM account_checkpoint
       WHERE account_id = in_account_id
         AND end_date <= in_transdate
    ORDER BY end_date DESC LIMIT 1
    ),
    ac AS (
      SELECT acc_trans.amount
        FROM acc_trans
        JOIN (select id from ar where approved
              union select id from ap where approved
              union select id from gl where approved) a on acc_trans.trans_id = a.id
      LEFT JOIN cp ON acc_trans.chart_id = cp.account_id
       WHERE (cp.end_date IS NULL OR transdate > cp.end_date)
         AND transdate <= in_transdate
         AND chart_id = in_account_id)

     SELECT coalesce((select sum(amount)
                        from (select sum(amount) as amount from cp
                              union all
                              select sum(amount) from ac) as a),
                     0);

### account__obtain_starting_balance(in_transdate date, in_account_id integer)

account__obtain_starting_balance(in_transdate date, in_account_id integer)

Function Properties

Language Return Type
- SQL numeric

    SELECT account__obtain_balance($1 - 1, $2);

### account__save(in_id integer, in_accno text, in_description text, in_category bpchar, in_gifi_accno text, in_heading integer, in_contra boolean, in_tax boolean, in_link text[], in_obsolete boolean, in_is_temp boolean)

account__save(in_id integer, in_accno text, in_description text, in_category bpchar, in_gifi_accno text, in_heading integer, in_contra boolean, in_tax boolean, in_link text[], in_obsolete boolean, in_is_temp boolean)

Function Properties

Language Return Type
- PLPGSQL integer

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 = coalesce(in_obsolete,'f'),
                    tax = t_tax,
                    is_temp = coalesce(in_is_temp,'f')
            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, coalesce(in_is_temp, 'f'));

                    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;

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

account__save_tax(in_chart_id integer, in_validto date, in_rate numeric, in_minvalue numeric, in_maxvalue numeric, in_taxnumber text, in_pass integer, in_taxmodule_id integer, in_old_validto date)

Function Properties

Language Return Type
- PLPGSQL boolean

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;

### account__save_translation(in_id integer, in_language_code text, in_description text)

account__save_translation(in_id integer, in_language_code text, in_description text)

Function Properties

Language Return Type
- PLPGSQL void

Saves the translation for the given account, creating a new translation if none existed for the account+language combination.

    BEGIN
       UPDATE account_translation
          SET description = in_description
        WHERE language_code = in_language_code
          AND trans_id = in_id;

       IF NOT FOUND THEN
          INSERT INTO account_translation
                 (trans_id, language_code, description)
          VALUES (in_id, in_language_code, in_description);
       END IF;
       RETURN;
    END;

### account_get(in_id integer)

account_get(in_id integer)

Function Properties

Language Return Type
- SQL chart

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

    select c.id, c.accno, c.description,
           'A'::text as charttype, c.category, concat_colon(l.description) as link,
           heading, gifi_accno, contra, tax
      from account c
      left join account_link l
        ON (c.id = l.account_id)
      where  id = $1
    group by c.id, c.accno, c.description, c.category,
             c.heading, c.gifi_accno, c.contra, c.tax;

### account_has_transactions(in_id integer)

account_has_transactions(in_id integer)

Function Properties

Language Return Type
- PLPGSQL boolean

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;

### account_heading__check_tree()

account_heading__check_tree()

Function Properties

Language Return Type
- PLPGSQL trigger

    BEGIN

    PERFORM * from (
      WITH RECURSIVE account_headings AS (
          SELECT id, accno, 1 as level, accno as path
            FROM account_heading
          UNION ALL
          SELECT ah.id, ah.accno, at.level + 1 as level, at.path  || '||||' || ah.accno
            FROM account_heading ah
            JOIN account_headings at ON ah.parent_id = at.id
           WHERE NOT ah.accno = ANY(string_to_array(path, '||||'))
      )
      SELECT *
        FROM account_heading ah
        JOIN account_headings at ON ah.parent_id = at.id
       WHERE at.path || '||||' ||  ah.accno NOT IN
              (select path from account_headings)
    ) x;

    IF found then
       RAISE EXCEPTION 'ACCOUNT_HEADING_LOOP';
    END IF;

    RETURN NEW;
    end;

### account_heading__delete(in_id integer)

account_heading__delete(in_id integer)

Function Properties

Language Return Type
- PLPGSQL boolean

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

    BEGIN
    DELETE FROM account_heading WHERE id = in_id;
    RETURN FOUND;
    END;

### account_heading__delete_translation(in_id integer, in_language_code text)

account_heading__delete_translation(in_id integer, in_language_code text)

Function Properties

Language Return Type
- SQL void

Deletes the translation for the account+language combination.

       DELETE FROM account_heading_translation
        WHERE trans_id = $1
          AND language_code = $2;

### account_heading__list()

account_heading__list()

Function Properties

Language Return Type
- SQL SET OF account_heading

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

     SELECT * FROM account_heading order by accno;

### account_heading__list_translations(in_id integer)

account_heading__list_translations(in_id integer)

Function Properties

Language Return Type
- SQL SET OF account_heading_translation

Returns the list of translations for the given account.

       SELECT * FROM account_heading_translation WHERE trans_id = $1;

### account_heading__save_translation(in_id integer, in_language_code text, in_description text)

account_heading__save_translation(in_id integer, in_language_code text, in_description text)

Function Properties

Language Return Type
- PLPGSQL void

Saves the translation for the given account, creating a new translation if none existed for the account+language combination.

    BEGIN
       UPDATE account_heading_translation
          SET description = in_description
        WHERE language_code = in_language_code
          AND trans_id = in_id;

       IF NOT FOUND THEN
          INSERT INTO account_heading_translation
                 (trans_id, language_code, description)
          VALUES (in_id, in_language_code, in_description);
       END IF;
       RETURN;
    END;

### account_heading_get(in_id integer)

account_heading_get(in_id integer)

Function Properties

Language Return Type
- SQL chart

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

    SELECT ah.id, ah.accno, ah.description,
           'H'::text as charttype, NULL::char as category, null::text as link,
           ah.parent_id as account_heading,
           null::text as gifi_accno, false as contra,
           false as tax
       from account_heading ah
      WHERE id = in_id;

### account_heading_list()

account_heading_list()

Function Properties

Language Return Type
- SQL SET OF account_heading

Lists all existing account headings.

    SELECT * FROM account_heading order by accno;

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

account_heading_save(in_id integer, in_accno text, in_description text, in_parent integer)

Function Properties

Language Return Type
- PLPGSQL integer

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;

### add_custom_field(table_name character varying, new_field_name character varying, field_datatype character varying)

add_custom_field(table_name character varying, new_field_name character varying, field_datatype character varying)

Function Properties

Language Return Type
- PLPGSQL boolean

    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;

### admin__add_function_to_group(in_func text, in_role text)

admin__add_function_to_group(in_func text, in_role text)

Function Properties

Language Return Type
- PLPGSQL integer

        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;

### admin__add_group_to_role(in_group_name text, in_role_name text)

admin__add_group_to_role(in_group_name text, in_role_name text)

Function Properties

Language Return Type
- PLPGSQL boolean

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.

    DECLARE
       t_group_name text;
       t_role_name  text;
    BEGIN
       t_group_name := lsmb__role(in_group_name);
       t_role_name := lsmb__role(in_role_name);
       PERFORM * FROM lsmb_group_grants
         WHERE group_name = t_group_name AND
               granted_role = t_role_name;

       IF NOT FOUND THEN
          INSERT INTO lsmb_group_grants(group_name, granted_role)
               VALUES (t_group_name, t_role_name);
       END IF;

       EXECUTE 'GRANT ' || quote_ident(t_role_name) || ' TO ' ||
               quote_literal(t_group_name);
       RETURN TRUE;
    END;

### admin__add_user_to_role(in_username text, in_role text)

admin__add_user_to_role(in_username text, in_role text)

Function Properties

Language Return Type
- PLPGSQL integer

        declare
            stmt TEXT;
            a_role name;
            a_user name;
            t_userid int;
        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 database user.';
            END IF;

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

            EXECUTE stmt;

            select id into t_userid from users where username = in_username;
            if not FOUND then
              RAISE EXCEPTION 'Cannot grant permissions to a non-existant application user.';
            end if;

            return 1;
        END;

### admin__create_group(in_group_name text)

admin__create_group(in_group_name text)

Function Properties

Language Return Type
- PLPGSQL integer

        DECLARE
            stmt text;
            group_name text;
        BEGIN
            group_name := lsmb__role(in_group_name);
            stmt := 'create role '|| quote_ident(group_name);
            execute stmt;
            INSERT INTO lsmb_group (role_name)
                 values (group_name);
            return 1;
        END;

### admin__delete_group(in_group_name text)

admin__delete_group(in_group_name text)

Function Properties

Language Return Type
- PLPGSQL boolean

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;
            group_name text;
        BEGIN
            select * into a_role from role_view where rolname = in_group_name;

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

### admin__delete_user(in_username text, in_drop_role boolean)

admin__delete_user(in_username text, in_drop_role boolean)

Function Properties

Language Return Type
- PLPGSQL integer

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;

### admin__drop_session(in_session_id integer)

admin__drop_session(in_session_id integer)

Function Properties

Language Return Type
- PLPGSQL boolean

Drops the session identified, releasing all locks held.

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

### admin__get_roles()

admin__get_roles()

Function Properties

Language Return Type
- SQL SET OF pg_roles

            SELECT *
            FROM
                pg_roles
            WHERE
                rolname ~ ('^' || lsmb__role_prefix())
                AND NOT rolcanlogin
            ORDER BY rolname ASC

### admin__get_roles_for_user(in_user_id integer)

admin__get_roles_for_user(in_user_id integer)

Function Properties

Language Return Type
- PLPGSQL SET OF text

Returns a set of roles that a user is a part of. Note: this function can only be used by - super users - database admins (setup.pl users): - database owners - database users (roles) which were granted the database owner role - application users: - application admins (users with 'manage_users' role) - application users (roles) which query their own roles

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

            -- this function used to be security definer, but that hides the true
            -- CURRENT_USER, returning the DEFINER instead of the caller
            IF a_user.username != CURRENT_USER THEN
                -- super users and application users match the first criterion
                -- db owners and db owner group members match the second criterion
                IF pg_has_role(lsmb__role('users_manage'), 'USAGE') IS FALSE
                   AND pg_has_role((select rolname
                                     from pg_database db inner join pg_roles rol
                                       on db.datdba = rol.oid
                                    where db.datname = current_database()),
                                   'USAGE') IS FALSE THEN
                   RAISE EXCEPTION 'User % querying permissions for %, not authorised', CURRENT_USER, a_user.username;
                END IF;
            END IF;

            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
                and r.rolname like (lsmb__role_prefix() || '%')
             LOOP

                RETURN NEXT u_role.rolname::text;

            END LOOP;
            RETURN;
        end;

### admin__get_roles_for_user_by_entity(in_entity_id integer)

admin__get_roles_for_user_by_entity(in_entity_id integer)

Function Properties

Language Return Type
- PLPGSQL SET OF text

Returns a set of roles that a user is a part of. Note: this function can only be used by - super users - database admins (setup.pl users): - database owners - database users (roles) which were granted the database owner role - application users: - application admins (users with 'manage_users' role) - application users (roles) which query their own roles

        declare
            u_role record;
            a_user users;
        begin
            select * into a_user from admin__get_user_by_entity(in_entity_id);

            -- this function used to be security definer, but that hides the true
            -- CURRENT_USER, returning the DEFINER instead of the caller
            IF a_user.username != CURRENT_USER THEN
                -- super users and application users match the first criterion
                -- db owners and db owner group members match the second criterion
                IF pg_has_role(lsmb__role('users_manage'), 'USAGE') IS FALSE
                   AND pg_has_role((select rolname
                                     from pg_database db inner join pg_roles rol
                                       on db.datdba = rol.oid
                                    where db.datname = current_database()),
                                   'USAGE') IS FALSE THEN
                   RAISE EXCEPTION 'User % querying permissions for %, not authorised', CURRENT_USER, a_user.username;
                END IF;
            END IF;

            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
                and r.rolname like (lsmb__role_prefix() || '%')
             LOOP

                RETURN NEXT u_role.rolname::text;

            END LOOP;
            RETURN;
        end;

### admin__get_user(in_id integer)

admin__get_user(in_id integer)

Function Properties

Language Return Type
- SQL users

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

            select * from users where id = in_id;

### admin__get_user_by_entity(in_entity_id integer)

admin__get_user_by_entity(in_entity_id integer)

Function Properties

Language Return Type
- SQL users

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

            select * from users where entity_id = in_entity_id;

### admin__is_group(in_group_name text)

admin__is_group(in_group_name text)

Function Properties

Language Return Type
- PLPGSQL boolean

        -- 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;

### admin__is_user(in_user text)

admin__is_user(in_user text)

Function Properties

Language Return Type
- PLPGSQL boolean

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

        BEGIN

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

        END;

### admin__list_group_grants(in_group_name text)

admin__list_group_grants(in_group_name text)

Function Properties

Language Return Type
- SQL SET OF lsmb_group_grants

    SELECT * FROM lsmb_group_grants WHERE group_name = $1
    ORDER BY granted_role;

### admin__list_sessions()

admin__list_sessions()

Function Properties

Language Return Type
- SQL SET OF session_result

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;

### admin__remove_function_from_group(in_func text, in_role text)

admin__remove_function_from_group(in_func text, in_role text)

Function Properties

Language Return Type
- PLPGSQL integer

        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;

### admin__remove_group_from_role(in_group_name text, in_role_name text)

admin__remove_group_from_role(in_group_name text, in_role_name text)

Function Properties

Language Return Type
- PLPGSQL boolean

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;

### admin__remove_user_from_role(in_username text, in_role text)

admin__remove_user_from_role(in_username text, in_role text)

Function Properties

Language Return Type
- PLPGSQL integer

        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;

### admin__save_user(in_id integer, in_entity_id integer, in_username text, in_password text, in_pls_import boolean)

admin__save_user(in_id integer, in_entity_id integer, in_username text, in_password text, in_pls_import boolean)

Function Properties

Language Return Type
- PLPGSQL integer

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_pls_import is NOT TRUE 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_pls_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_pls_import IS NOT TRUE 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
                PERFORM admin__add_user_to_role(
                            a_user.username,
                            lsmb__role_prefix() || 'base_user');
                return a_user.id;
            ELSE
                -- Insert cycle

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

                PERFORM * FROM USERS where username = in_username;
                IF NOT FOUND THEN
                    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);
                END IF;

                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
                PERFORM admin__add_user_to_role(
                            in_username,
                            lsmb__role_prefix() || 'base_user');

                return v_user_id ;

            END IF;

        END;

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

admin__search_users(in_username text, in_first_name text, in_last_name text, in_ssn text, in_dob date)

Function Properties

Language Return Type
- SQL SET OF user_result

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

                    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)

### ap_simple_post(in_entity_credit_account integer, in_ap_liablility_chartid integer, in_invnumber text, in_transdate date, in_duedate date, in_curr bpchar, in_description text, in_ordnumber text, in_notes text, in_intnotes text, in_ponumber text, in_memo text[], in_netamount numeric[], in_chartid numeric[], in_taxrate numeric[], in_taxchartid numeric[])

ap_simple_post(in_entity_credit_account integer, in_ap_liablility_chartid integer, in_invnumber text, in_transdate date, in_duedate date, in_curr bpchar, in_description text, in_ordnumber text, in_notes text, in_intnotes text, in_ponumber text, in_memo text[], in_netamount numeric[], in_chartid numeric[], in_taxrate numeric[], in_taxchartid numeric[])

Function Properties

Language Return Type
- PLPGSQL integer

    DECLARE invnumber text;
    DECLARE ap_id   int;
    DECLARE entry_id   int;
    DECLARE chartid int;
    DECLARE tax_chartid int;
    DECLARE invoice bool;
    DECLARE separate_duties bool;
    DECLARE approved bool;
    DECLARE taxincluded bool;
    DECLARE curr char(3);
    DECLARE curr_default char(3);
    DECLARE transdate date;
    DECLARE duedate date;
    DECLARE crdate date;
    DECLARE memo text;
    DECLARE netamount numeric;
    DECLARE netamount_total numeric;
    DECLARE taxrate numeric;
    DECLARE taxamount numeric;
    DECLARE taxamount_total numeric;
    DECLARE amount_total numeric;
    DECLARE person_id int;
    DECLARE dp int;
    DECLARE fx_transaction bool;
    DECLARE cleared bool;
    DECLARE taxform_id int;
    DECLARE reportable bool;
    DECLARE atf_count int;
    DECLARE ect_count int;
    BEGIN
     netamount_total=0.0;
     taxamount_total=0.0;
     invoice=false;
     approved=true;
     taxincluded=false;
     fx_transaction=false;
     cleared=false;

     select value::int       INTO dp FROM setting_get('decimal_places');
     select person__get_my_entity_id into person_id from person__get_my_entity_id();
     SELECT value::bool INTO separate_duties FROM defaults WHERE setting_key='separate_duties';
     IF separate_duties = true THEN
      --RAISE EXCEPTION 'separate_duties not yet treated';
      approved=false;
     END IF;
     select eca.taxform_id::int into taxform_id from entity_credit_account eca where eca.id=in_entity_credit_account;
     IF taxform_id <> 0 THEN
      --FOREIGN KEY (taxform_id) REFERENCES country_tax_form(id)
      RAISE EXCEPTION 'taxform not yet treated';
     END IF;

     transdate=coalesce(in_transdate,'today');
     duedate=coalesce(in_duedate,'today');
     crdate=now();

     --IF in_invnumber IS NULL OR (length(trim(in_invnumber))=0) THEN --NULL as only designator for "no value supplied"
     IF in_invnumber IS NULL THEN
      select setting_increment::text INTO invnumber FROM setting_increment('vinumber');
     ELSE
      invnumber=in_invnumber;
     END IF;

     select defaults_get_defaultcurrency into curr_default from defaults_get_defaultcurrency();
     IF in_curr IS NULL THEN
      curr=curr_default;
     ELSE
      curr=in_curr;
     END IF;
     IF curr <> curr_default THEN
      RAISE EXCEPTION 'curr <> curr_default not yet treated';
     END IF;

     FOR out_count IN array_lower(in_memo, 1) .. array_upper(in_memo, 1)
     LOOP
       netamount=in_netamount[out_count];
       netamount_total=netamount_total+netamount;
       taxrate=in_taxrate[out_count];
       IF taxrate IS NOT NULL THEN
        taxamount=netamount*taxrate;
       ELSE
        taxamount=0.0;
       END IF;
       taxamount_total=taxamount_total+taxamount;
     END LOOP;

     amount_total=netamount_total+taxamount_total;

     amount_total=round(amount_total,dp);
     netamount_total=round(netamount_total,dp);

     INSERT INTO ap (entity_credit_account,invnumber,transdate,invoice,approved,taxincluded,curr,duedate,crdate,netamount,amount,person_id,description,ordnumber,notes,intnotes,ponumber) VALUES(in_entity_credit_account,invnumber,transdate,invoice,approved,taxincluded,curr,duedate,crdate,netamount_total,amount_total,person_id,in_description,in_ordnumber,in_notes,in_intnotes,in_ponumber);

     SELECT currval('id') INTO ap_id;--NOT "id"!

     INSERT INTO acc_trans(trans_id,transdate,chart_id,amount,approved) values(ap_id,transdate,in_ap_liablility_chartid,amount_total,approved);

     FOR out_count IN array_lower(in_memo, 1) .. array_upper(in_memo, 1)
     LOOP
      memo=in_memo[out_count];
      chartid=in_chartid[out_count];
      tax_chartid=in_taxchartid[out_count];
      netamount=in_netamount[out_count];
      taxrate=in_taxrate[out_count];
      taxamount=0.0;
      IF taxrate IS NOT NULL THEN
       IF tax_chartid IS NOT NULL THEN
         select count(*) into ect_count from eca_tax ect where ect.eca_id=in_entity_credit_account and ect.chart_id=tax_chartid;
         IF ect_count = 0 THEN
          RAISE EXCEPTION 'tax_chartid NOT IN  eca_tax';
         END IF;
         taxamount=netamount*taxrate;
         taxamount=round(taxamount,dp);
         INSERT INTO acc_trans (trans_id,chart_id,amount,transdate,fx_transaction,memo) VALUES(ap_id,tax_chartid,taxamount*-1.0,transdate,fx_transaction,memo);
       ELSE --tax_chartid null
        RAISE EXCEPTION 'taxrate NOT NULL but tax_chartid NULL';
       END IF;--tax_chartid
      ELSE --taxrate null
       IF tax_chartid IS NOT NULL THEN
        RAISE EXCEPTION 'taxrate NULL but tax_chartid NOT NULL';
       END IF;
       --taxamount=0.0;
      END IF;--taxrate

      netamount=round(netamount,dp);
      INSERT INTO acc_trans (trans_id,chart_id,amount,transdate,memo,fx_transaction,cleared) VALUES(ap_id,chartid,netamount*-1.0,transdate,memo,fx_transaction,cleared);
      SELECT currval('acc_trans_entry_id_seq') INTO entry_id;

      IF taxform_id <> 0 THEN
       select count(*) into atf_count from ac_tax_form atf where atf.entry_id=entry_id;
       IF atf_count > 0 THEN
        update ac_tax_form atf set atf.reportable=reportable where atf.entry_id=entry_id;
       ELSE
        insert into ac_tax_form(entry_id,reportable) values(entry_id,reportable);
       END IF;
      END IF;--taxform_id

     END LOOP;

     PERFORM trans_id FROM acc_trans WHERE trans_id = ap_id GROUP BY trans_id HAVING sum(amount) <> 0;
     IF FOUND THEN
       RAISE EXCEPTION 'Out of balance';
     END IF;

     return ap_id;
    END;

### ar_ap__transaction_search(in_account_id integer, in_name_part text, in_meta_number text, in_invnumber text, in_ordnumber text, in_ponumber text, in_source text, in_description text, in_notes text, in_shipvia text, in_from_date date, in_to_date date, in_on_hold boolean, in_inc_open boolean, in_inc_closed boolean, in_as_of date, in_entity_class integer, in_approved boolean)

ar_ap__transaction_search(in_account_id integer, in_name_part text, in_meta_number text, in_invnumber text, in_ordnumber text, in_ponumber text, in_source text, in_description text, in_notes text, in_shipvia text, in_from_date date, in_to_date date, in_on_hold boolean, in_inc_open boolean, in_inc_closed boolean, in_as_of date, in_entity_class integer, in_approved boolean)

Function Properties

Language Return Type
- SQL SET OF purchase_info

       SELECT gl.id, gl.invoice,
              gl.invnumber, gl.ordnumber, gl.ponumber, gl.transdate,
              e.name, eca.meta_number::text, e.id, gl.amount,
              gl.amount - sum(CASE WHEN l.description IN ('AR', 'AP')
                                   THEN ac.amount ELSE 0
                               END),
              gl.amount - gl.netamount, gl.curr, gl.datepaid, gl.duedate,
              gl.notes, gl.shippingpoint, gl.shipvia,
              compound_array(bua.business_units || bui.business_units)
         FROM (select id, invoice, invnumber, ordnumber, ponumber, transdate, duedate,
                      description, notes, shipvia, shippingpoint, amount,
                      netamount, curr, datepaid, entity_credit_account, on_hold,
                      approved
                 FROM ar WHERE in_entity_class = 2
                UNION
               select id, invoice, invnumber, ordnumber, ponumber, transdate, duedate,
                      description, notes, shipvia, shippingpoint, amount,
                      netamount, curr, datepaid, entity_credit_account, on_hold,
                      approved
                 FROM ap WHERE in_entity_class = 1) gl
         JOIN entity_credit_account eca ON gl.entity_credit_account = eca.id
         JOIN entity e ON e.id = eca.entity_id
         JOIN acc_trans ac ON gl.id = ac.trans_id
         JOIN account act ON act.id = ac.chart_id
    LEFT JOIN account_link l ON l.account_id = act.id
                              AND l.description IN ('AR', 'AP')
    LEFT JOIN invoice inv ON gl.id = inv.trans_id
    LEFT JOIN (SELECT compound_array(ARRAY[ARRAY[buc.label, bu.control_code]])
                      as business_units, entry_id
                 FROM business_unit_class buc
                 JOIN business_unit bu ON bu.class_id = buc.id
                 JOIN business_unit_ac buac ON buac.bu_id = bu.id
             GROUP BY buac.entry_id) bua
                                     ON bua.entry_id = ac.entry_id
    LEFT JOIN (SELECT compound_array(ARRAY[ARRAY[buc.label, bu.control_code]])
                      as business_units, entry_id
                 FROM business_unit_class buc
                 JOIN business_unit bu ON bu.class_id = buc.id
                 JOIN business_unit_inv buinv ON buinv.bu_id = bu.id
             GROUP BY buinv.entry_id) bui
                                     ON bui.entry_id = inv.id
        WHERE (in_account_id IS NULL OR ac.chart_id = in_account_id)
              AND (in_name_part IS NULL
                    OR to_tsvector(get_default_lang()::regconfig, e.name)
                       @@ plainto_tsquery(get_default_lang()::regconfig, in_name_part))
              AND (in_meta_number IS NULL
                    OR eca.meta_number LIKE in_meta_number || '%')
              AND (in_invnumber IS NULL or gl.invnumber LIKE in_invnumber || '%')
              AND (in_ordnumber IS NULL or gl.ordnumber LIKE in_ordnumber || '%')
              AND (in_ponumber IS NULL or gl.ponumber LIKE in_ponumber || '%')
              AND (in_description IS NULL
                    or to_tsvector(get_default_lang()::regconfig, gl.description)
                      @@ plainto_tsquery(get_default_lang()::regconfig, in_description))
              AND (in_notes IS NULL OR
                    to_tsvector(get_default_lang()::regconfig, gl.notes)
                     @@ plainto_tsquery(get_default_lang()::regconfig, in_notes))
              AND (in_from_date IS NULL OR in_from_date <= gl.transdate)
              AND (in_to_date IS NULL OR in_to_date >= gl.transdate)
              AND (in_on_hold IS NULL OR in_on_hold = gl.on_hold)
              AND (in_as_of IS NULL OR in_as_of >= ac.transdate)
              AND (in_approved is null
                   OR (gl.approved = in_approved AND ac.approved = in_approved))
     GROUP BY gl.id, gl.invnumber, gl.ordnumber, gl.ponumber, gl.transdate,
              gl.duedate, e.name, eca.meta_number, gl.amount,
              gl.netamount, gl.curr, gl.datepaid, gl.duedate,
              gl.notes, gl.shippingpoint, gl.shipvia, e.id, gl.invoice
       HAVING in_source = ANY(array_agg(ac.source)) or in_source IS NULL;

### ar_ap__transaction_search_summary(in_account_id integer, in_name_part text, in_meta_number text, in_invnumber text, in_ordnumber text, in_ponumber text, in_source text, in_description text, in_notes text, in_shipvia text, in_from_date date, in_to_date date, in_on_hold boolean, in_inc_open boolean, in_inc_closed boolean, in_as_of date, in_entity_class integer, in_approved boolean)

ar_ap__transaction_search_summary(in_account_id integer, in_name_part text, in_meta_number text, in_invnumber text, in_ordnumber text, in_ponumber text, in_source text, in_description text, in_notes text, in_shipvia text, in_from_date date, in_to_date date, in_on_hold boolean, in_inc_open boolean, in_inc_closed boolean, in_as_of date, in_entity_class integer, in_approved boolean)

Function Properties

Language Return Type
- SQL SET OF purchase_info

           SELECT null::int, null::bool, null::text, null::text, null::text,
                  null::date, entity_name, meta_number, entity_id, sum(amount),
                  sum(amount_paid), sum(tax), currency, null::date, null::date,
                  null::text, null::text, null::text, null::text[]
             FROM ar_ap__transaction_search
                  (in_account_id, in_name_part, in_meta_number, in_invnumber,
                  in_ordnumber, in_ponumber, in_source, in_description,
                  in_notes, in_shipvia, in_from_date, in_to_date,
                  in_on_hold, in_inc_open, in_inc_closed, in_as_of,
                  in_entity_class, in_approved)
         GROUP BY entity_name, meta_number, entity_id, currency;

### array_endswith(elem anyelement, arr anyarray)

array_endswith(elem anyelement, arr anyarray)

Function Properties

Language Return Type
- SQL boolean

       SELECT $2[array_upper($2,1)]=$1;

### array_splice_from(elem anyelement, arr anyarray)

array_splice_from(elem anyelement, arr anyarray)

Function Properties

Language Return Type
- SQL anyarray

        select $2[i:array_upper($2,1)]
          from generate_subscripts($2,1) as i
         where $2[i] = $1
         order by i
         limit 1;

### array_splice_to(element anyelement, arr anyarray)

array_splice_to(element anyelement, arr anyarray)

Function Properties

Language Return Type
- SQL anyarray

       select $2[1:i]
         from generate_subscripts($2,1) as i
        where $2[i] = $1
       order by i
       limit 1;

### as_array(anyelement)

as_array(anyelement)

Function Properties

Language Return Type
- INTERNAL anyarray

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

    aggregate_dummy

### assembly__stock(in_parts_id integer, in_qty numeric)

assembly__stock(in_parts_id integer, in_qty numeric)

Function Properties

Language Return Type
- SQL numeric

        INSERT INTO mfg_lot(parts_id, qty) VALUES ($1, $2);
        INSERT INTO mfg_lot_item(mfg_lot_id, parts_id, qty)
        SELECT currval('mfg_lot_id_seq')::int, parts_id, qty * $2
          FROM assembly WHERE id = $1;

        SELECT mfg_lot__commit(currval('mfg_lot_id_seq')::int);

### asset__get(in_id integer, in_tag text)

asset__get(in_id integer, in_tag text)

Function Properties

Language Return Type
- SQL asset_item

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.

            SELECT * from asset_item WHERE id = in_id OR in_tag = tag
            ORDER BY id desc limit 1;

### asset__import_from_disposal(in_id integer)

asset__import_from_disposal(in_id integer)

Function Properties

Language Return Type
- PLPGSQL boolean

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;

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

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

Function Properties

Language Return Type
- PLPGSQL asset_item

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;

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

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

Function Properties

Language Return Type
- SQL SET OF asset_item

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.

                    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 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);

### asset_class__get(in_id integer)

asset_class__get(in_id integer)

Function Properties

Language Return Type
- SQL asset_class

returns the row from asset_class identified by in_id.

            SELECT * FROM asset_class WHERE id = in_id;

### asset_class__get_asset_accounts()

asset_class__get_asset_accounts()

Function Properties

Language Return Type
- SQL SET OF account

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;

### asset_class__get_dep_accounts()

asset_class__get_dep_accounts()

Function Properties

Language Return Type
- SQL SET OF account

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;

### asset_class__get_dep_method(in_asset_class integer)

asset_class__get_dep_method(in_asset_class integer)

Function Properties

Language Return Type
- SQL asset_dep_method

Returns the depreciation method associated with the asset class.

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

### asset_class__get_dep_methods()

asset_class__get_dep_methods()

Function Properties

Language Return Type
- SQL SET OF asset_dep_method

Returns a set of asset_dep_methods ordered by the method label.

    SELECT * FROM asset_dep_method ORDER BY method;

### asset_class__list()

asset_class__list()

Function Properties

Language Return Type
- SQL SET OF asset_class

Returns an alphabetical list of asset classes.

    SELECT * FROM asset_class ORDER BY label;

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

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

Function Properties

Language Return Type
- PLPGSQL asset_class

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;

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

asset_class__search(in_asset_account_id integer, in_dep_account_id integer, in_method integer, in_label text)

Function Properties

Language Return Type
- SQL SET OF asset_class_result

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.

                    SELECT ac.id, ac.asset_account_id, aa.accno, aa.description,
                            ac.dep_account_id, 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

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

asset_dep__straight_line_base(in_base_life numeric, in_life numeric, in_used numeric, in_basis numeric, in_dep_to_date numeric)

Function Properties

Language Return Type
- SQL numeric

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;

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

asset_dep__used_months(in_last_dep date, in_dep_date date, in_usable_life numeric)

Function Properties

Language Return Type
- SQL numeric

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;

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

asset_dep_get_usable_life_yr(in_usable_life numeric, in_start_date date, in_dep_date date)

Function Properties

Language Return Type
- SQL numeric

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;

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

asset_dep_straight_line_month(in_asset_ids integer[], in_report_date date, in_report_id integer)

Function Properties

Language Return Type
- SQL boolean

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;

### asset_dep_straight_line_yr_d(in_asset_ids integer[], in_report_date date, in_report_id integer)

asset_dep_straight_line_yr_d(in_asset_ids integer[], in_report_date date, in_report_id integer)

Function Properties

Language Return Type
- SQL boolean

         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;

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

asset_dep_straight_line_yr_m(in_asset_ids integer[], in_report_date date, in_report_id integer)

Function Properties

Language Return Type
- SQL boolean

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;

### asset_depreciation__approve(in_report_id integer, in_expense_acct integer)

asset_depreciation__approve(in_report_id integer, in_expense_acct integer)

Function Properties

Language Return Type
- PLPGSQL asset_report

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;

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

asset_disposal__approve(in_id integer, in_gain_acct integer, in_loss_acct integer, in_asset_acct integer)

Function Properties

Language Return Type
- PLPGSQL asset_report

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;

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

asset_item__add_note(in_id integer, in_subject text, in_note text)

Function Properties

Language Return Type
- SQL asset_note

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');

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

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

Function Properties

Language Return Type
- SQL SET OF asset_item

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

             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);

### asset_nbv_report()

asset_nbv_report()

Function Properties

Language Return Type
- SQL SET OF asset_nbv_line

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 - coalesce(sum(rl.amount), 0)
         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;

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

asset_report__approve(in_id integer, in_expense_acct integer, in_gain_acct integer, in_loss_acct integer)

Function Properties

Language Return Type
- PLPGSQL asset_report

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;

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

asset_report__begin_disposal(in_asset_class integer, in_report_date date, in_report_class integer)

Function Properties

Language Return Type
- PLPGSQL asset_report

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;

### asset_report__begin_import(in_asset_class integer, in_report_date date)

asset_report__begin_import(in_asset_class integer, in_report_date date)

Function Properties

Language Return Type
- SQL asset_report

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');

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

asset_report__disposal_gl(in_id integer, in_gain_acct integer, in_loss_acct integer)

Function Properties

Language Return Type
- SQL boolean

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;

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

asset_report__dispose(in_id integer, in_asset_id integer, in_amount numeric, in_dm integer, in_percent_disposed numeric)

Function Properties

Language Return Type
- PLPGSQL boolean

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;

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

asset_report__generate(in_depreciation boolean, in_asset_class integer, in_report_date date)

Function Properties

Language Return Type
- SQL SET OF asset_item

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;

### asset_report__generate_gl(in_report_id integer, in_accum_account_id integer)

asset_report__generate_gl(in_report_id integer, in_accum_account_id integer)

Function Properties

Language Return Type
- PLPGSQL integer

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;

### asset_report__get(in_id integer)

asset_report__get(in_id integer)

Function Properties

Language Return Type
- SQL asset_report

Returns the asset_report line identified by id.

    select * from asset_report where id = $1;

### asset_report__get_disposal(in_id integer)

asset_report__get_disposal(in_id integer)

Function Properties

Language Return Type
- SQL SET OF asset_disposal_report_line

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;

### asset_report__get_disposal_methods()

asset_report__get_disposal_methods()

Function Properties

Language Return Type
- SQL SET OF asset_disposal_method

Returns a list of asset_disposal_method items ordered by label.

    SELECT * FROM asset_disposal_method order by label;

### asset_report__get_expense_accts()

asset_report__get_expense_accts()

Function Properties

Language Return Type
- SQL SET OF account

Lists all asset expense reports.

        SELECT * FROM account__get_by_link_desc('asset_expense');

### asset_report__get_gain_accts()

asset_report__get_gain_accts()

Function Properties

Language Return Type
- SQL SET OF account

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

        SELECT * FROM account__get_by_link_desc('asset_gain');

### asset_report__get_lines(in_id integer)

asset_report__get_lines(in_id integer)

Function Properties

Language Return Type
- SQL SET OF asset_report_line_result

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;

### asset_report__get_loss_accts()

asset_report__get_loss_accts()

Function Properties

Language Return Type
- SQL SET OF account

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

        SELECT * FROM account__get_by_link_desc('asset_loss');

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

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

Function Properties

Language Return Type
- SQL boolean

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;

### asset_report__record_approve(in_id integer)

asset_report__record_approve(in_id integer)

Function Properties

Language Return Type
- SQL asset_report

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;

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

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

Function Properties

Language Return Type
- PLPGSQL asset_report

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;

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

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

Function Properties

Language Return Type
- SQL SET OF asset_report_result

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;

### asset_report_partial_disposal_details(in_id integer)

asset_report_partial_disposal_details(in_id integer)

Function Properties

Language Return Type
- SQL SET OF partial_disposal_line

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;

### avgcost(integer)

avgcost(integer)

Function Properties

Language Return Type
- PLPGSQL double precision

    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;

### batch__lock(in_batch_id integer)

batch__lock(in_batch_id integer)

Function Properties

Language Return Type
- SQL boolean

    UPDATE batch SET locked_by = (select max(session_id)
                                    FROM "session" where users_id = (
                                            select id from users
                                             WHERE username = SESSION_USER))
     WHERE locked_by IS NULL
    RETURNING true;

### batch__lock_for_update(in_batch_id integer)

batch__lock_for_update(in_batch_id integer)

Function Properties

Language Return Type
- SQL batch

Locks a batch for the duration of the running transaction. To be used when adding vouchers to the batch to prevent others from hitting the batch for other purposes (e.g. approval)

    SELECT * FROM batch WHERE id = $1 FOR UPDATE;

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

batch__search(in_class_id integer, in_description text, in_created_by_eid integer, in_date_from date, in_date_to date, in_amount_gt numeric, in_amount_lt numeric, in_approved boolean)

Function Properties

Language Return Type
- SQL SET OF batch_list_item

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.

                    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 ar.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,
                         batch__lock(b.id)
                    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

### batch__unlock(in_batch_id integer)

batch__unlock(in_batch_id integer)

Function Properties

Language Return Type
- SQL boolean

    UPDATE batch SET locked_by = NULL
     WHERE id = $1 AND locked_by IN (select session_id
                                       from "session" s
                                       join users u on (u.id = s.users_id)
                                      where username = SESSION_USER)
    RETURNING true;

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

batch_create(in_batch_number text, in_description text, in_batch_class text, in_batch_date date)

Function Properties

Language Return Type
- SQL integer

Inserts the batch into the table.

            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))
            RETURNING id;

### batch_delete(in_batch_id integer)

batch_delete(in_batch_id integer)

Function Properties

Language Return Type
- PLPGSQL integer

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, 8, 9);

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

            DELETE FROM invoice WHERE trans_id = ANY(t_transaction_ids);
            DELETE FROM acc_trans WHERE trans_id = ANY(t_transaction_ids);
            DELETE FROM ar WHERE 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;

### batch_get_class_id(in_type text)

batch_get_class_id(in_type text)

Function Properties

Language Return Type
- SQL integer

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

    SELECT id FROM batch_class WHERE class = $1;

### batch_get_users()

batch_get_users()

Function Properties

Language Return Type
- SQL SET OF users

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

                    SELECT * from users WHERE entity_id IN (select created_by from batch)

### batch_list_classes()

batch_list_classes()

Function Properties

Language Return Type
- PLPGSQL SET OF batch_class

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;

### batch_post(in_batch_id integer)

batch_post(in_batch_id integer)

Function Properties

Language Return Type
- SQL date

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

            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;

            SELECT now()::date;

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

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

Function Properties

Language Return Type
- SQL SET OF batch_list_item

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.

                   SELECT b.id, c.class, b.control_code, b.description, u.username,
                            b.created_on, b.default_date, 0::numeric, 0::numeric, false
                    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

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

batch_search_mini(in_class_id integer, in_description text, in_created_by_eid integer, in_approved boolean)

Function Properties

Language Return Type
- SQL SET OF batch_list_item

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.

                    SELECT b.id, c.class, b.control_code, b.description, u.username,
                            b.created_on, b.default_date, NULL::NUMERIC, NULL::numeric, false
                    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

### budget__approve(in_id integer)

budget__approve(in_id integer)

Function Properties

Language Return Type
- SQL budget_info_ext

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

    SELECT budget__get_info($1);

### budget__get_business_units(in_id integer)

budget__get_business_units(in_id integer)

Function Properties

Language Return Type
- SQL SET OF business_unit

     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;

### budget__get_details(in_id integer)

budget__get_details(in_id integer)

Function Properties

Language Return Type
- SQL SET OF budget_line_details

This retrieves the budget lines associated with a budget.

      SELECT l.budget_id, l.account_id, l.description, l.amount,
             a.accno, a.description,
             CASE WHEN l.amount < 0 THEN l.amount * -1 ELSE NULL END,
             CASE WHEN l.amount > 0 THEN l.amount ELSE NULL END
        FROM budget_line l
        JOIN account a ON a.id = l.account_id
       where budget_id = $1;

### budget__get_info(in_id integer)

budget__get_info(in_id integer)

Function Properties

Language Return Type
- SQL budget_info_ext

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;

### budget__get_notes(in_id integer)

budget__get_notes(in_id integer)

Function Properties

Language Return Type
- SQL SET OF budget_note

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;

### budget__mark_obsolete(in_id integer)

budget__mark_obsolete(in_id integer)

Function Properties

Language Return Type
- SQL budget_info_ext

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)

### budget__reject(in_id integer)

budget__reject(in_id integer)

Function Properties

Language Return Type
- PLPGSQL boolean

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;

### budget__save_details(in_id integer, in_details text[])

budget__save_details(in_id integer, in_details text[])

Function Properties

Language Return Type
- PLPGSQL budget_info_ext

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;

### budget__save_info(in_id integer, in_start_date date, in_end_date date, in_reference text, in_description text, in_business_units integer[])

budget__save_info(in_id integer, in_start_date date, in_end_date date, in_reference text, in_description text, in_business_units integer[])

Function Properties

Language Return Type
- PLPGSQL budget_info_ext

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;

### budget__save_note(in_id integer, in_subject text, in_note text)

budget__save_note(in_id integer, in_subject text, in_note text)

Function Properties

Language Return Type
- SQL budget_note

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);

### budget__search(in_start_date date, in_end_date date, in_includes_date date, in_reference text, in_description text, in_entered_by integer, in_approved_by integer, in_obsolete_by integer, in_business_units integer[], in_is_approved boolean, in_is_obsolete boolean)

budget__search(in_start_date date, in_end_date date, in_includes_date date, in_reference text, in_description text, in_entered_by integer, in_approved_by integer, in_obsolete_by integer, in_business_units integer[], in_is_approved boolean, in_is_obsolete boolean)

Function Properties

Language Return Type
- SQL SET OF budget_info_ext

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;

### budget__variance_report(in_id integer)

budget__variance_report(in_id integer)

Function Properties

Language Return Type
- SQL SET OF budget_variance_report

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;

### business_type__list()

business_type__list()

Function Properties

Language Return Type
- SQL SET OF business

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

            SELECT * FROM business ORDER BY description;

### business_unit__get(in_id integer)

business_unit__get(in_id integer)

Function Properties

Language Return Type
- SQL business_unit

     SELECT * FROM business_unit where id = $1;

### business_unit__get_tree_for(in_id integer)

business_unit__get_tree_for(in_id integer)

Function Properties

Language Return Type
- SQL SET OF business_unit_short

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;

### business_unit__list_by_class(in_business_unit_class_id integer, in_active_on date, in_credit_id integer, in_strict_credit boolean)

business_unit__list_by_class(in_business_unit_class_id integer, in_active_on date, in_credit_id integer, in_strict_credit boolean)

Function Properties

Language Return Type
- SQL SET OF business_unit

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.

    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;

### business_unit__list_classes(in_active boolean, in_module text)

business_unit__list_classes(in_active boolean, in_module text)

Function Properties

Language Return Type
- SQL SET OF business_unit_class

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;

### business_unit__save(in_id integer, in_class_id integer, in_control_code text, in_description text, in_start_date date, in_end_date date, in_parent_id integer, in_credit_id integer)

business_unit__save(in_id integer, in_class_id integer, in_control_code text, in_description text, in_start_date date, in_end_date date, in_parent_id integer, in_credit_id integer)

Function Properties

Language Return Type
- PLPGSQL business_unit

    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;

### business_unit_class__get_modules(in_id integer)

business_unit_class__get_modules(in_id integer)

Function Properties

Language Return Type
- SQL SET OF lsmb_module

     SELECT * FROM lsmb_module
        WHERE id IN (select module_id from bu_class_to_module where bu_class_id = $1)
     ORDER BY id;

### business_unit_class__save(in_id integer, in_label text, in_active boolean, in_ordering integer)

business_unit_class__save(in_id integer, in_label text, in_active boolean, in_ordering integer)

Function Properties

Language Return Type
- PLPGSQL business_unit_class

    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;

### business_unit_class__save_modules(in_id integer, in_mod_ids integer[])

business_unit_class__save_modules(in_id integer, in_mod_ids integer[])

Function Properties

Language Return Type
- SQL boolean

    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;

### business_unit_get(in_id integer)

business_unit_get(in_id integer)

Function Properties

Language Return Type
- SQL business_unit

     SELECT * FROM business_unit WHERE id = $1;

### chart_get_ar_ap(in_account_class integer)

chart_get_ar_ap(in_account_class integer)

Function Properties

Language Return Type
- PLPGSQL SET OF chart

This function returns the cash account according 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;

### chart_list_all()

chart_list_all()

Function Properties

Language Return Type
- SQL SET OF chart

Generates a list of chart view entries.

    SELECT * FROM chart ORDER BY accno;

### chart_list_cash(in_account_class integer)

chart_list_cash(in_account_class integer)

Function Properties

Language Return Type
- PLPGSQL SET OF chart

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;

### chart_list_discount(in_account_class integer)

chart_list_discount(in_account_class integer)

Function Properties

Language Return Type
- PLPGSQL SET OF chart

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;

### chart_list_overpayment(in_account_class integer)

chart_list_overpayment(in_account_class integer)

Function Properties

Language Return Type
- PLPGSQL SET OF chart

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;

### chart_list_search(in_search text, in_link_desc text)

chart_list_search(in_search text, in_link_desc text)

Function Properties

Language Return Type
- SQL SET OF account

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.

                    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

### check_expiration()

check_expiration()

Function Properties

Language Return Type
- PLPGSQL boolean

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;

### cogs__add_for_ap(in_parts_id integer, in_qty numeric, in_lastcost numeric)

cogs__add_for_ap(in_parts_id integer, in_qty numeric, in_lastcost numeric)

Function Properties

Language Return Type
- PLPGSQL numeric

    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))[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 (select id, approved, transdate from ar
                 union
                select id, approved, transdate from gl) a
               ON a.id = i.trans_id AND a.approved
         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 > coalesce(t_cp.end_date, t_ar.transdate - 1) 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 > coalesce(t_cp.end_date, t_ar.transdate - 1) 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 > coalesce(t_cp.end_date, t_ar.transdate - 1) 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 > coalesce(t_cp.end_date, t_ar.transdate - 1) 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;

### cogs__add_for_ap_line(in_invoice_id integer)

cogs__add_for_ap_line(in_invoice_id integer)

Function Properties

Language Return Type
- PLPGSQL numeric

    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;

### cogs__add_for_ar(in_parts_id integer, in_qty numeric)

cogs__add_for_ar(in_parts_id integer, in_qty numeric)

Function Properties

Language Return Type
- PLPGSQL numeric[]

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 AND a.approved
      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;

### cogs__add_for_ar_line(in_invoice_id integer)

cogs__add_for_ar_line(in_invoice_id integer)

Function Properties

Language Return Type
- PLPGSQL numeric

    DECLARE
       t_cogs numeric[];
       t_inv invoice;
       t_part parts;
       t_ar ar;
       t_transdate date;
       t_override_cogs int;
    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_ar.is_return THEN
       t_override_cogs = (setting_get('ar_return_account_id')).value::int;
    END IF;

    IF t_part.inventory_accno_id IS NULL THEN
       RETURN 0;
    END IF;

    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 > coalesce(max(end_date), t_ar.transdate - 1) 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, COALESCE(t_override_cogs,
                          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,
           coalesce(t_transdate, t_ar.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;

### cogs__reverse_ap(in_parts_id integer, in_qty numeric)

cogs__reverse_ap(in_parts_id integer, in_qty numeric)

Function Properties

Language Return Type
- PLPGSQL numeric[]

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

    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 AND a.approved
         WHERE qty + allocated < 0 AND parts_id = in_parts_id
      ORDER BY a.transdate, a.id, i.id
    LOOP
       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;

### cogs__reverse_ar(in_parts_id integer, in_qty numeric)

cogs__reverse_ar(in_parts_id integer, in_qty numeric)

Function Properties

Language Return Type
- PLPGSQL numeric[]

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; -- qty to reverse (negative)
            t_cogs numeric := 0;
            t_inv invoice;
            t_reversed numeric;
            t_reallocated numeric;
    BEGIN

    IF in_qty = 0 THEN
       RETURN ARRAY[0, 0];
    END IF;

    FOR t_inv IN
        SELECT i.*
          FROM invoice i
          JOIN (SELECT id, approved, transdate FROM ar
                UNION
                SELECT id, approved, transdate FROM gl) a ON a.id = i.trans_id
         WHERE allocated + qty > 0 and a.approved and parts_id = in_parts_id
       ORDER BY a.transdate ASC, a.id ASC, i.id ASC
    LOOP
       t_reallocated := greatest(in_qty - t_alloc, t_inv.qty + t_inv.allocated);
       UPDATE invoice
          SET allocated = allocated - t_reallocated
        WHERE id = t_inv.id;
       t_alloc := t_alloc - t_reallocated;

       IF t_alloc < in_qty THEN
          RAISE EXCEPTION 'TOO MANY ALLOCATED (1)';
       ELSIF t_alloc = in_qty THEN
          RETURN ARRAY[t_alloc, 0];
       END IF;
    END LOOP;

    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_reversed := least((in_qty - t_alloc) * -1, t_inv.allocated);
       UPDATE invoice SET allocated = allocated - t_reversed
        WHERE id = t_inv.id;
       t_cogs := t_cogs - t_reversed * t_inv.sellprice;
       t_alloc := t_alloc - t_reversed;

       IF t_alloc < in_qty THEN
           RAISE EXCEPTION 'TOO MANY ALLOCATED';
       ELSIF t_alloc = in_qty THEN
           RETURN ARRAY[t_alloc, t_cogs];
       END IF;
    END LOOP;

    END;

### company__get(in_entity_id integer)

company__get(in_entity_id integer)

Function Properties

Language Return Type
- SQL company_entity

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;

### company__get_all_accounts(in_entity_id integer, in_entity_class integer)

company__get_all_accounts(in_entity_id integer, in_entity_class integer)

Function Properties

Language Return Type
- SQL SET OF entity_credit_account

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;

### company__get_by_cc(in_control_code text)

company__get_by_cc(in_control_code text)

Function Properties

Language Return Type
- SQL company_entity

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;

### company__next_id()

company__next_id()

Function Properties

Language Return Type
- SQL bigint

        select nextval('company_id_seq');

### company__save(in_control_code text, in_entity_class integer, in_legal_name text, in_tax_id text, in_entity_id integer, in_sic_code text, in_country_id integer, in_sales_tax_id text, in_license_number text)

company__save(in_control_code text, in_entity_class integer, in_legal_name text, in_tax_id text, in_entity_id integer, in_sic_code text, in_country_id integer, in_sales_tax_id text, in_license_number text)

Function Properties

Language Return Type
- PLPGSQL company

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

    DECLARE t_entity_id INT;
            t_control_code TEXT;
            t_retval COMPANY;
    BEGIN

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

            UPDATE entity
            SET name = in_legal_name,
                    entity_class = in_entity_class,
                    control_code = t_control_code,
                    country_id   = in_country_id
            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 entity_id = t_entity_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;

### company_get_billing_info(in_id integer)

company_get_billing_info(in_id integer)

Function Properties

Language Return Type
- SQL company_billing_info

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

            select coalesce(eca.pay_to_name, c.legal_name), eca.meta_number,
                    e.control_code, eca.cash_account_id, c.tax_id,
                    a.line_one, a.line_two, a.line_three,
                    a.city, a.state, a.mail_code, cc.name
            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);

### compound_array(anyarray)

compound_array(anyarray)

Function Properties

Language Return Type
- INTERNAL anyarray

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

    aggregate_dummy

### concat_colon(text)

concat_colon(text)

Function Properties

Language Return Type
- INTERNAL text

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

### concat_colon(text, text)

concat_colon(text, text)

Function Properties

Language Return Type
- SQL text

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;

### contact__search(in_entity_class integer, in_contact text, in_contact_info text[], in_meta_number text, in_address text, in_city text, in_state text, in_mail_code text, in_country text, in_active_date_from date, in_active_date_to date, in_business_id integer, in_name_part text, in_control_code text, in_notes text, in_users boolean)

contact__search(in_entity_class integer, in_contact text, in_contact_info text[], in_meta_number text, in_address text, in_city text, in_state text, in_mail_code text, in_country text, in_active_date_from date, in_active_date_to date, in_business_id integer, in_name_part text, in_control_code text, in_notes text, in_users boolean)

Function Properties

Language Return Type
- SQL SET OF contact_search_result

                    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)
                                 OR legal_name ilike 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 (in_entity_class is null
                            OR coalesce(ec.entity_class,e.entity_class) = in_entity_class)
                            AND (c.entity_id IN
                           (select entity_id
                              FROM entity_credit_account leca
                              JOIN eca_to_contact le2c ON leca.id = le2c.credit_id
                             WHERE contact ~*~ ANY(in_contact_info))
                          OR '' ILIKE ALL(in_contact_info)
                          OR in_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_credit_account leca
                                       JOIN eca_to_location le2a
                                         ON leca.id = le2a.credit_id
                                       JOIN location ll ON le2a.location_id = ll.id
                                      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)))
                            AND (in_users IS NULL OR in_users =
                                 (exists (select 1 from users
                                           where entity_id = e.id)))
                            AND (in_contact IS NULL
                                 OR e.id IN (select entity_id
                                               FROM entity_to_contact
                                              WHERE description
                                                    @@ plainto_tsquery(in_contact))
                                 OR ec.id IN (SELECT credit_id
                                                FROM eca_to_contact
                                               WHERE description
                                                     @@ plainto_tsquery(in_contact))
                           )
                   ORDER BY legal_name;

### contact_class__list()

contact_class__list()

Function Properties

Language Return Type
- SQL SET OF contact_class

Returns a list of contact classes ordered by ID.

                    SELECT * FROM contact_class ORDER BY id;

### cr_coa_to_account_save(in_accno text, in_description text)

cr_coa_to_account_save(in_accno text, in_description text)

Function Properties

Language Return Type
- PLPGSQL void

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 cr
            JOIN account a on cr.chart_id = a.id
            WHERE accno = 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;

### cr_report_block_changing_approved()

cr_report_block_changing_approved()

Function Properties

Language Return Type
- PLPGSQL trigger

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;

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

currency_get_exchangerate(in_currency bpchar, in_date date, in_account_class integer)

Function Properties

Language Return Type
- PLPGSQL numeric

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;

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

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

Function Properties

Language Return Type
- PLPGSQL integer

        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;

### date_get_all_years()

date_get_all_years()

Function Properties

Language Return Type
- SQL SET OF integer

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.

    WITH RECURSIVE max_dates AS (
        SELECT max(transdate) AS max_date
          FROM acc_trans
        WHERE transdate IS NOT NULL

     UNION ALL
        SELECT (SELECT max(transdate)
                  FROM acc_trans
                       -- the index acc_trans_transdate_year_idx uses the
                       -- date_part function with the exact syntax and capitals
                       -- below; changing the 'YEAR' capitals will stop the
                       -- query optimizer from using the index
                 WHERE date_part('YEAR', transdate) < date_part('YEAR', max_date))
          FROM max_dates
         WHERE max_date IS NOT NULL)

    SELECT date_part('YEAR', max_date)::int
      FROM max_dates
     WHERE max_date IS NOT NULL;

### days_in_month(in_date date)

days_in_month(in_date date)

Function Properties

Language Return Type
- SQL integer

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;

### deduction__list_for_entity(in_entity_id integer)

deduction__list_for_entity(in_entity_id integer)

Function Properties

Language Return Type
- SQL SET OF payroll_deduction

    SELECT * FROM payroll_deduction WHERE entity_id = $1;

### deduction__list_types(in_country_id integer)

deduction__list_types(in_country_id integer)

Function Properties

Language Return Type
- SQL SET OF payroll_deduction_type

    SELECT * FROM payroll_deduction_type where country_id = $1

### deduction__save(in_rate numeric, in_entity_id integer, in_type_id integer)

deduction__save(in_rate numeric, in_entity_id integer, in_type_id integer)

Function Properties

Language Return Type
- PLPGSQL payroll_deduction

    DECLARE return_ded payroll_deduction;
    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;

    SELECT * INTO return_ded FROM payroll_deduction
                 WHERE entity_id = in_entity_id and in_type_id;
    RETURN return_ded;
    END;

### defaults__get_contra_accounts(in_category bpchar)

defaults__get_contra_accounts(in_category bpchar)

Function Properties

Language Return Type
- SQL SET OF account

    SELECT * FROM account WHERE contra AND category = $1;

### defaults_get_defaultcurrency()

defaults_get_defaultcurrency()

Function Properties

Language Return Type
- SQL bpchar

This function return the default currency asigned by the program.

               SELECT substr(value,1,3)
               FROM defaults
               WHERE setting_key = 'curr';

### del_recurring()

del_recurring()

Function Properties

Language Return Type
- PLPGSQL trigger

    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;

### del_yearend()

del_yearend()

Function Properties

Language Return Type
- PLPGSQL trigger

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

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

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

Function Properties

Language Return Type
- SQL SET OF draft_search_result

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

            SELECT id, transdate, invoice, reference, description,
                   type, amount FROM (
                SELECT id, transdate, reference,
                       description, false as invoice,
                       (SELECT SUM(line.amount)
                          FROM acc_trans line
                         WHERE line.amount > 0
                               and line.trans_id = gl.id) as amount,
                       'gl' as type
                  from gl
                 WHERE (lower(in_type) = 'gl' or in_type is null)
                      AND NOT approved
                      AND NOT EXISTS (SELECT 1
                                        FROM voucher v
                                       WHERE v.trans_id = gl.id)
                UNION
                SELECT id, transdate, invnumber as reference,
                    (SELECT name FROM eca__get_entity(entity_credit_account)),
                    invoice, amount, 'ap' as type
                  FROM ap
                 WHERE (lower(in_type) = 'ap' or in_type is null)
                       AND NOT approved
                       AND NOT EXISTS (SELECT 1
                                         FROM voucher v
                                        WHERE v.trans_id = ap.id)
                UNION
                SELECT id, transdate, invnumber as reference,
                    description, invoice, amount, 'ar' as type
                  FROM ar
                 WHERE (lower(in_type) = 'ar' or in_type is null)
                       AND NOT approved
                       AND NOT EXISTS (SELECT 1
                                         FROM voucher v
                                        WHERE v.trans_id = ar.id)) trans
            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 (in_with_accno IS NULL
                   OR id IN (SELECT line.trans_id
                               FROM acc_trans line
                               JOIN account acc ON (line.chart_id = acc.id)
                              WHERE acc.accno = in_with_accno
                                AND NOT approved
                                AND (in_from_date IS NULL
                                     OR line.transdate >= in_from_date)
                                AND (in_to_date IS NULL
                                     OR line.transdate <= in_to_date)))
            ORDER BY trans.reference;

### draft_approve(in_id integer)

draft_approve(in_id integer)

Function Properties

Language Return Type
- PLPGSQL boolean

Posts draft to the books. in_id is the id from the ar, ap, or gl table.

    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;

### draft_delete(in_id integer)

draft_delete(in_id integer)

Function Properties

Language Return Type
- PLPGSQL boolean

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
            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;

### drop_custom_field(character varying, character varying)

drop_custom_field(character varying, character varying)

Function Properties

Language Return Type
- PLPGSQL boolean

    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;

### eca__delete_contact(in_credit_id integer, in_class_id integer, in_contact text)

eca__delete_contact(in_credit_id integer, in_class_id integer, in_contact text)

Function Properties

Language Return Type
- PLPGSQL boolean

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;

### eca__delete_location(in_credit_id integer, in_id integer, in_location_class integer)

eca__delete_location(in_credit_id integer, in_id integer, in_location_class integer)

Function Properties

Language Return Type
- PLPGSQL boolean

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;

### eca__delete_pricematrix(in_credit_id integer, in_entry_id integer)

eca__delete_pricematrix(in_credit_id integer, in_entry_id integer)

Function Properties

Language Return Type
- PLPGSQL boolean

    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;

### eca__get_by_meta_number(in_meta_number text, in_entity_class integer)

eca__get_by_meta_number(in_meta_number text, in_entity_class integer)

Function Properties

Language Return Type
- SQL entity_credit_account

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

### eca__get_entity(in_credit_id integer)

eca__get_entity(in_credit_id integer)

Function Properties

Language Return Type
- SQL SET OF entity

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

        SELECT entity.*
          FROM entity_credit_account
          JOIN entity ON entity_credit_account.entity_id = entity.id
         WHERE entity_credit_account.id = in_credit_id;

### eca__get_pricematrix(in_credit_id integer)

eca__get_pricematrix(in_credit_id integer)

Function Properties

Language Return Type
- SQL SET OF eca__pricematrix

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, pc.qty
      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, null
      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

### eca__get_pricematrix_by_pricegroup(in_credit_id integer)

eca__get_pricematrix_by_pricegroup(in_credit_id integer)

Function Properties

Language Return Type
- SQL SET OF eca__pricematrix

    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, pc.qty
      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

### eca__get_taxes(in_id integer)

eca__get_taxes(in_id integer)

Function Properties

Language Return Type
- SQL SET OF eca_tax

Returns a set of taxable account id's.

    select * from eca_tax where eca_id = $1;

### eca__history(in_name_part text, in_meta_number text, in_contact_info text, in_address_line text, in_city text, in_state text, in_zip text, in_salesperson text, in_notes text, in_country_id integer, in_from_date date, in_to_date date, in_type bpchar, in_start_from date, in_start_to date, in_entity_class integer, in_inc_open boolean, in_inc_closed boolean)

eca__history(in_name_part text, in_meta_number text, in_contact_info text, in_address_line text, in_city text, in_state text, in_zip text, in_salesperson text, in_notes text, in_country_id integer, in_from_date date, in_to_date date, in_type bpchar, in_start_from date, in_start_to date, in_entity_class integer, in_inc_open boolean, in_inc_closed boolean)

Function Properties

Language Return Type
- SQL SET OF eca_history_result

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.

         WITH arap AS (
           select  invnumber, curr, ar.transdate, entity_credit_account, id,
                       person_id, notes
                 FROM ar
                 JOIN acc_trans ON ar.id  = acc_trans.trans_id
                 JOIN account_link l ON acc_trans.chart_id = l.account_id
                      and l.description = 'AR'
                where $16 = 2 and $13 = 'i'
           GROUP BY 1, 2, 3, 4, 5, 6, 7
                      having (($17 and sum(acc_trans.amount) = 0)
                          or ($18 and 0 <> sum(acc_trans.amount)))
                UNION ALL
               select invnumber, curr, ap.transdate, entity_credit_account, id,
                      person_id, notes
                 FROM ap
                 JOIN acc_trans ON ap.id  = acc_trans.trans_id
                 JOIN account_link l ON acc_trans.chart_id = l.account_id
                      and l.description = 'AP'
                where $16 = 1 and $13 = 'i'
           GROUP BY 1, 2, 3, 4, 5, 6, 7
                      having (($17 and sum(acc_trans.amount) = 0) or
                           ($18 and sum(acc_trans.amount) <> 0))
         )
         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 * case when eca.entity_class = 1 THEN -1 ELSE 1 END,
                i.unit::text, i.sellprice, i.discount,
                i.deliverydate,
                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 * FROM arap
               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, p.partnumber;

### eca__history_summary(in_name_part text, in_meta_number text, in_contact_info text, in_address_line text, in_city text, in_state text, in_zip text, in_salesperson text, in_notes text, in_country_id integer, in_from_date date, in_to_date date, in_type bpchar, in_start_from date, in_start_to date, in_entity_class integer, in_inc_open boolean, in_inc_closed boolean)

eca__history_summary(in_name_part text, in_meta_number text, in_contact_info text, in_address_line text, in_city text, in_state text, in_zip text, in_salesperson text, in_notes text, in_country_id integer, in_from_date date, in_to_date date, in_type bpchar, in_start_from date, in_start_to date, in_entity_class integer, in_inc_open boolean, in_inc_closed boolean)

Function Properties

Language Return Type
- SQL SET OF eca_history_result

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::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,
              sellprice
     order by meta_number;

### eca__list_contacts(in_credit_id integer)

eca__list_contacts(in_credit_id integer)

Function Properties

Language Return Type
- PLPGSQL SET OF contact_list

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;

### eca__list_locations(in_credit_id integer)

eca__list_locations(in_credit_id integer)

Function Properties

Language Return Type
- SQL SET OF location_result

Returns a list of locations attached to the credit account.

                    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

### eca__list_notes(in_credit_id integer)

eca__list_notes(in_credit_id integer)

Function Properties

Language Return Type
- PLPGSQL SET OF note

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;

### eca__location_save(in_credit_id integer, in_id integer, in_location_class integer, in_line_one text, in_line_two text, in_line_three text, in_city text, in_state text, in_mail_code text, in_country_id integer, in_old_location_class integer)

eca__location_save(in_credit_id integer, in_id integer, in_location_class integer, in_line_one text, in_line_two text, in_line_three text, in_city text, in_state text, in_mail_code text, in_country_id integer, in_old_location_class integer)

Function Properties

Language Return Type
- PLPGSQL integer

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;

### eca__save(in_id integer, in_entity_class integer, in_entity_id integer, in_description text, in_discount numeric, in_taxincluded boolean, in_creditlimit numeric, in_discount_terms integer, in_terms integer, in_meta_number character varying, in_business_id integer, in_language_code character varying, in_pricegroup_id integer, in_curr bpchar, in_startdate date, in_enddate date, in_threshold numeric, in_ar_ap_account_id integer, in_cash_account_id integer, in_pay_to_name text, in_taxform_id integer, in_discount_account_id integer)

eca__save(in_id integer, in_entity_class integer, in_entity_id integer, in_description text, in_discount numeric, in_taxincluded boolean, in_creditlimit numeric, in_discount_terms integer, in_terms integer, in_meta_number character varying, in_business_id integer, in_language_code character varying, in_pricegroup_id integer, in_curr bpchar, in_startdate date, in_enddate date, in_threshold numeric, in_ar_ap_account_id integer, in_cash_account_id integer, in_pay_to_name text, in_taxform_id integer, in_discount_account_id integer)

Function Properties

Language Return Type
- PLPGSQL integer

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;

### eca__save_contact(in_credit_id integer, in_class_id integer, in_description text, in_contact text, in_old_contact text, in_old_class_id integer)

eca__save_contact(in_credit_id integer, in_class_id integer, in_description text, in_contact text, in_old_contact text, in_old_class_id integer)

Function Properties

Language Return Type
- PLPGSQL eca_to_contact

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

    DECLARE out_contact eca_to_contact;
    BEGIN

        PERFORM *
           FROM eca_to_contact
          WHERE credit_id = in_credit_id
            AND contact_class_id = in_old_class_id
            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_class_id
               AND contact = in_old_contact
            returning * INTO out_contact;
            return out_contact;
        END IF;
            INSERT INTO eca_to_contact(credit_id, contact_class_id,
                    description, contact)
            VALUES (in_credit_id, in_class_id, in_description, in_contact)
            RETURNING * into out_contact;
            return out_contact;

    END;

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

eca__save_notes(in_credit_id integer, in_note text, in_subject text)

Function Properties

Language Return Type
- SQL eca_note

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

            -- 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)
            RETURNING *;

### eca__save_pricematrix(in_parts_id integer, in_credit_id integer, in_pricebreak numeric, in_price numeric, in_lead_time smallint, in_partnumber text, in_validfrom date, in_validto date, in_curr bpchar, in_entry_id integer)

eca__save_pricematrix(in_parts_id integer, in_credit_id integer, in_pricebreak numeric, in_price numeric, in_lead_time smallint, in_partnumber text, in_validfrom date, in_validto date, in_curr bpchar, in_entry_id integer)

Function Properties

Language Return Type
- PLPGSQL eca__pricematrix

    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_lead_time::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, null
          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,
               qty        = in_qty,
               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, qty)
            VALUES (in_parts_id, in_credit_id, in_price, in_validfrom, in_validto,
                    in_curr, in_qty);

            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, pc.qty
          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;

### eca__set_taxes(in_id integer, in_tax_ids integer[])

eca__set_taxes(in_id integer, in_tax_ids integer[])

Function Properties

Language Return Type
- SQL boolean

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.

         DELETE FROM eca_tax WHERE eca_id = $1;
         INSERT INTO eca_tax (eca_id, chart_id)
         SELECT $1, tax_id
           FROM unnest($2) tax_id;
         SELECT TRUE;

### eca_bu_trigger()

eca_bu_trigger()

Function Properties

Language Return Type
- PLPGSQL trigger

    BEGIN
      IF TG_OP = 'INSERT' THEN
          INSERT INTO business_unit(class_id, control_code, description, credit_id)
          SELECT 7 - NEW.entity_class, NEW.meta_number,  e.name, NEW.id
                 FROM entity e WHERE e.id = NEW.entity_id;
      ELSIF TG_OP = 'UPDATE' THEN
          IF new.meta_number <> old.meta_number THEN
             UPDATE business_unit SET control_code = 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 - OLD.entity_class
                      AND credit_id = OLD.id;
          RETURN OLD;
      END IF;
      RETURN NEW;
    END;

### employee__all_managers()

employee__all_managers()

Function Properties

Language Return Type
- SQL SET OF employee_result

       SELECT p.entity_id, e.control_code, p.id, s.salutation, s.id,
              p.first_name, p.middle_name, p.last_name, ee.is_manager,
              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.is_manager
     ORDER BY ee.employeenumber;

### employee__all_salespeople()

employee__all_salespeople()

Function Properties

Language Return Type
- SQL SET OF employee_result

       SELECT p.entity_id, e.control_code, p.id, s.salutation, s.id,
              p.first_name, p.middle_name, p.last_name, ee.is_manager,
              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;

### employee__get(in_entity_id integer)

employee__get(in_entity_id integer)

Function Properties

Language Return Type
- SQL employee_result

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.is_manager,
              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;

### employee__get_user(in_entity_id integer)

employee__get_user(in_entity_id integer)

Function Properties

Language Return Type
- SQL users

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

    SELECT * FROM users WHERE entity_id = $1;

### employee__list_managers(in_id integer)

employee__list_managers(in_id integer)

Function Properties

Language Return Type
- SQL SET OF employees

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

                    SELECT
                        s.salutation,
                        p.first_name,
                        p.last_name,
                        ee.*
                    FROM entity_employee ee
                    JOIN entity e on e.id = ee.entity_id
                    JOIN person p ON p.entity_id = e.id
                    JOIN salutation s ON s.id = p.salutation_id
                    WHERE ee.sales = 't'::bool AND ee.role='manager'
                            AND ee.entity_id <> coalesce(in_id, -1)
                    ORDER BY name

### employee__save(in_entity_id integer, in_start_date date, in_end_date date, in_dob date, in_role text, in_ssn text, in_sales boolean, in_manager_id integer, in_employeenumber text, in_is_manager boolean)

employee__save(in_entity_id integer, in_start_date date, in_end_date date, in_dob date, in_role text, in_ssn text, in_sales boolean, in_manager_id integer, in_employeenumber text, in_is_manager boolean)

Function Properties

Language Return Type
- PLPGSQL integer

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,
                    is_manager = coalesce(in_is_manager, false),
                    sales = in_sales
            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, is_manager, sales)
                    VALUES
                            (coalesce(in_start_date, now()::date), in_end_date,
                                    in_dob, in_role, in_ssn,
                                    in_manager_id, in_employeenumber,
                                    in_entity_id, in_is_manager, in_sales);
                    RETURN in_entity_id;
            END IF;
            RETURN out_id;
    END;

### employee__search(in_employeenumber text, in_startdate_from date, in_startdate_to date, in_first_name text, in_middle_name text, in_last_name text, in_notes text, in_is_user boolean)

employee__search(in_employeenumber text, in_startdate_from date, in_startdate_to date, in_first_name text, in_middle_name text, in_last_name text, in_notes text, in_is_user boolean)

Function Properties

Language Return Type
- SQL SET OF employee_result

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.is_manager,
              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 || '%')
              and ($8 is null
                   or $8 = (exists (select 1 from users where entity_id = e.id)));

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

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

Function Properties

Language Return Type
- SQL SET OF employee_search

                    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')

### entity__delete_bank_account(in_entity_id integer, in_id integer)

entity__delete_bank_account(in_entity_id integer, in_id integer)

Function Properties

Language Return Type
- PLPGSQL boolean

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;

### entity__delete_contact(in_entity_id integer, in_class_id integer, in_contact text)

entity__delete_contact(in_entity_id integer, in_class_id integer, in_contact text)

Function Properties

Language Return Type
- PLPGSQL boolean

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;

### entity__delete_location(in_entity_id integer, in_id integer, in_location_class integer)

entity__delete_location(in_entity_id integer, in_id integer, in_location_class integer)

Function Properties

Language Return Type
- PLPGSQL boolean

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;

### entity__get(in_entity_id integer)

entity__get(in_entity_id integer)

Function Properties

Language Return Type
- SQL SET OF entity

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

        SELECT * FROM entity WHERE id = in_entity_id;

### entity__get_bank_account(in_id integer)

entity__get_bank_account(in_id integer)

Function Properties

Language Return Type
- SQL entity_bank_account

    SELECT * FROM  entity_bank_account WHERE id = $1;

### entity__list_bank_account(in_entity_id integer)

entity__list_bank_account(in_entity_id integer)

Function Properties

Language Return Type
- SQL SET OF entity_bank_account

Lists all bank accounts for the entity.

    SELECT * from entity_bank_account where entity_id = in_entity_id;

### entity__list_classes()

entity__list_classes()

Function Properties

Language Return Type
- PLPGSQL SET OF entity_class

Returns a list of entity classes, ordered by assigned ids

    DECLARE out_row entity_class;
    BEGIN
            FOR out_row IN
                    SELECT * FROM entity_class
                    WHERE active and pg_has_role(SESSION_USER,
                                       lsmb__role_prefix()
                                       || 'contact_class_'
                                       || lower(regexp_replace(class, '( |\-)', '_')),
                                       'USAGE')
                    ORDER BY id
            LOOP
                    RETURN NEXT out_row;
            END LOOP;
    END;

### entity__list_contacts(in_entity_id integer)

entity__list_contacts(in_entity_id integer)

Function Properties

Language Return Type
- SQL SET OF contact_list

Lists all contact info for the entity.

                    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

### entity__list_credit(in_entity_id integer, in_entity_class integer)

entity__list_credit(in_entity_id integer, in_entity_class integer)

Function Properties

Language Return Type
- SQL SET OF entity_credit_retrieve

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

                    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::text, 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

### entity__list_locations(in_entity_id integer)

entity__list_locations(in_entity_id integer)

Function Properties

Language Return Type
- SQL SET OF location_result

Lists all locations for an entity.

                    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;

### entity__list_notes(in_entity_id integer)

entity__list_notes(in_entity_id integer)

Function Properties

Language Return Type
- SQL SET OF entity_note

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

                    SELECT *
                    FROM entity_note
                    WHERE ref_key = in_entity_id
                    ORDER BY created

### entity__location_save(in_entity_id integer, in_id integer, in_location_class integer, in_line_one text, in_line_two text, in_city text, in_state text, in_mail_code text, in_country_id integer, in_created date)

entity__location_save(in_entity_id integer, in_id integer, in_location_class integer, in_line_one text, in_line_two text, in_city text, in_state text, in_mail_code text, in_country_id integer, in_created date)

Function Properties

Language Return Type
- PLPGSQL integer

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;

### entity__save_bank_account(in_entity_id integer, in_credit_id integer, in_bic text, in_iban text, in_remark text, in_bank_account_id integer)

entity__save_bank_account(in_entity_id integer, in_credit_id integer, in_bic text, in_iban text, in_remark text, in_bank_account_id integer)

Function Properties

Language Return Type
- PLPGSQL entity_bank_account

Saves bank account to the credit account.

    DECLARE out_bank entity_bank_account;
    BEGIN
            UPDATE entity_bank_account
               SET bic = coalesce(in_bic,''),
                   iban = in_iban,
                   remark = in_remark
             WHERE id = in_bank_account_id;

            IF FOUND THEN
                 SELECT * INTO out_bank from entity_bank_account WHERE id = in_bank_account_id;

            ELSE
                    INSERT INTO entity_bank_account(entity_id, bic, iban, remark)
                    VALUES(in_entity_id, in_bic, in_iban, in_remark);
                    SELECT * INTO out_bank from entity_bank_account WHERE id = CURRVAL('entity_bank_account_id_seq');
            END IF;

            IF in_credit_id IS NOT NULL THEN
                    UPDATE entity_credit_account SET bank_account = out_bank.id
                    WHERE id = in_credit_id;
            END IF;
            return out_bank;

    END;

### entity__save_contact(in_entity_id integer, in_class_id integer, in_description text, in_contact text, in_old_contact text, in_old_class_id integer)

entity__save_contact(in_entity_id integer, in_class_id integer, in_description text, in_contact text, in_old_contact text, in_old_class_id integer)

Function Properties

Language Return Type
- SQL entity_to_contact

Saves company contact information. The return value is meaningless.

            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)
             RETURNING *;

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

entity__save_notes(in_entity_id integer, in_note text, in_subject text)

Function Properties

Language Return Type
- SQL entity_note

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

            -- 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)
            RETURNING *;

### entity_credit__get(in_id integer)

entity_credit__get(in_id integer)

Function Properties

Language Return Type
- SQL entity_credit_account

Returns the entity credit account info.

    SELECT * FROM entity_credit_account WHERE id = $1;

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

entity_credit_get_id(in_entity_id integer, in_entity_class integer, in_meta_number text)

Function Properties

Language Return Type
- SQL integer

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

            SELECT id FROM entity_credit_account
            WHERE entity_id = in_entity_id
                    AND in_entity_class = entity_class
                    AND in_meta_number = meta_number;

### entity_credit_get_id_by_meta_number(in_meta_number text, in_account_class integer)

entity_credit_get_id_by_meta_number(in_meta_number text, in_account_class integer)

Function Properties

Language Return Type
- SQL integer

Returns the credit id from the meta_number and entity_class.

            SELECT id
            FROM entity_credit_account
            WHERE meta_number = in_meta_number
                    AND entity_class = in_account_class;

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

entity_save(in_entity_id integer, in_name text, in_entity_class integer)

Function Properties

Language Return Type
- PLPGSQL integer

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;

### eoy__latest_checkpoint()

eoy__latest_checkpoint()

Function Properties

Language Return Type
- SQL account_checkpoint

This returns a single checkpoint from the latest set. Which account and info is returned is non-determinative and so only the end date shoudl be relied on.

       SELECT * FROM account_checkpoint ORDER BY end_date DESC LIMIT 1;

### eoy__reopen_books_at(in_reopen_date date)

eoy__reopen_books_at(in_reopen_date date)

Function Properties

Language Return Type
- SQL boolean

      WITH eoy_dates AS (
          SELECT end_date
                  FROM account_checkpoint
                 WHERE end_date >= $1
                 GROUP BY end_date
        ORDER BY end_date DESC
        )
        SELECT eoy_reopen_books(end_date)
          FROM eoy_dates;

    SELECT CASE WHEN (SELECT count(*) > 0 from account_checkpoint
                       where end_date = $1 - 1)
                THEN true
                ELSE eoy_create_checkpoint($1 - 1) > 0
           END;

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

eoy_close_books(in_end_date date, in_reference text, in_description text, in_retention_acc_id integer)

Function Properties

Language Return Type
- PLPGSQL boolean

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;

### eoy_create_checkpoint(in_end_date date)

eoy_create_checkpoint(in_end_date date)

Function Properties

Language Return Type
- PLPGSQL integer

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, 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)
            RIGHT JOIN account ON account.id = a.chart_id or account.id = cp.account_id
            group by COALESCE(a.chart_id, cp.account_id), account.id;

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

            return ret_val;
    END;

### eoy_earnings_accounts()

eoy_earnings_accounts()

Function Properties

Language Return Type
- SQL SET OF account

Lists equity accounts for the retained earnings dropdown.

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

### eoy_reopen_books(in_end_date date)

eoy_reopen_books(in_end_date date)

Function Properties

Language Return Type
- PLPGSQL boolean

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, in_end_date, 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;

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

eoy_zero_accounts(in_end_date date, in_reference text, in_description text, in_retention_acc_id integer)

Function Properties

Language Return Type
- PLPGSQL integer

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;

### file__attach_to_eca(in_content bytea, in_mime_type_id integer, in_file_name text, in_description text, in_id integer, in_ref_key integer, in_file_class integer)

file__attach_to_eca(in_content bytea, in_mime_type_id integer, in_file_name text, in_description text, in_id integer, in_ref_key integer, in_file_class integer)

Function Properties

Language Return Type
- PLPGSQL file_base

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;

### file__attach_to_entity(in_content bytea, in_mime_type_id integer, in_file_name text, in_description text, in_id integer, in_ref_key integer, in_file_class integer)

file__attach_to_entity(in_content bytea, in_mime_type_id integer, in_file_name text, in_description text, in_id integer, in_ref_key integer, in_file_class integer)

Function Properties

Language Return Type
- PLPGSQL file_base

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;

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

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

Function Properties

Language Return Type
- PLPGSQL file_base

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;

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

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

Function Properties

Language Return Type
- PLPGSQL file_base

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;

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

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

Function Properties

Language Return Type
- PLPGSQL file_base

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;

### file__get(in_id integer, in_file_class integer)

file__get(in_id integer, in_file_class integer)

Function Properties

Language Return Type
- SQL file_base

Retrieves the file information specified including content.

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

### file__get_for_template(in_ref_key integer, in_file_class integer)

file__get_for_template(in_ref_key integer, in_file_class integer)

Function Properties

Language Return Type
- SQL SET OF file_list_item

    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
                   GROUP BY ref_key)

### file__get_mime_type(in_mime_type_id integer, in_mime_type_text text)

file__get_mime_type(in_mime_type_id integer, in_mime_type_text text)

Function Properties

Language Return Type
- SQL mime_type

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);

### file__list_by(in_ref_key integer, in_file_class integer)

file__list_by(in_ref_key integer, in_file_class integer)

Function Properties

Language Return Type
- SQL SET OF file_list_item

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;

### file__list_links(in_ref_key integer, in_file_class integer)

file__list_links(in_ref_key integer, in_file_class integer)

Function Properties

Language Return Type
- SQL SET OF file_links

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

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

### file__save_incoming(in_content bytea, in_mime_type_id integer, in_file_name text, in_description text)

file__save_incoming(in_content bytea, in_mime_type_id integer, in_file_name text, in_description text)

Function Properties

Language Return Type
- SQL file_base

If the file_name is not unique, a unique constraint violation will be thrown.

    INSERT INTO file_incoming(content, mime_type_id, file_name, description,
                              ref_key, file_class, uploaded_by)
    SELECT $1, $2, $3, $4, 0, 7, entity_id
      FROM users where username = SESSION_USER
     RETURNING *;

### file__save_internal(in_content bytea, in_mime_type_id integer, in_file_name text, in_description text)

file__save_internal(in_content bytea, in_mime_type_id integer, in_file_name text, in_description text)

Function Properties

Language Return Type
- SQL file_base

If the file_name is not unique, this will overwrite the previous stored file.

    WITH up AS (
        UPDATE file_internal
           SET content = $1, uploaded_at = now(),
               uploaded_by = (select entity_id from users
                               where username = session_user)
         WHERE file_name = $3
     RETURNING true as found_it
    )
    INSERT INTO file_internal (content, mime_type_id, file_name, description,
                              ref_key, file_class, uploaded_by)
    SELECT $1, $2, $3, $4, 0, 6, entity_id
      FROM users
     where username = SESSION_USER
           AND NOT EXISTS (select 1 from up)
    RETURNING *;

### file_links_vrebuild()

file_links_vrebuild()

Function Properties

Language Return Type
- PLPGSQL boolean

    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;

### form_check(in_session_id integer, in_form_id integer)

form_check(in_session_id integer, in_form_id integer)

Function Properties

Language Return Type
- SQL boolean

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;

### form_close(in_session_id integer, in_form_id integer)

form_close(in_session_id integer, in_form_id integer)

Function Properties

Language Return Type
- PLPGSQL boolean

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;

### form_open(in_session_id integer)

form_open(in_session_id integer)

Function Properties

Language Return Type
- PLPGSQL integer

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

    DECLARE usertest bool;
        form_id int;
    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,last_used)
                            VALUES (in_session_id,now())
            RETURNING id INTO form_id;

            RETURN form_id;
    END;

### full_ilike_match(seek text, source text)

full_ilike_match(seek text, source text)

Function Properties

Language Return Type
- SQL boolean

    select seek ilike '%' || source || '%';

### get_default_lang()

get_default_lang()

Function Properties

Language Return Type
- SQL text

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

### get_fractional_month(in_date_first date, in_date_second date)

get_fractional_month(in_date_first date, in_date_second date)

Function Properties

Language Return Type
- SQL numeric

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;

### get_fractional_year(in_date_from date, in_date_to date)

get_fractional_year(in_date_from date, in_date_to date)

Function Properties

Language Return Type
- SQL numeric

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;

### get_link_descriptions()

get_link_descriptions()

Function Properties

Language Return Type
- SQL SET OF account_link_description

Gets a set of all valid account_link descriptions.

        SELECT * FROM account_link_description;

### gifi__list()

gifi__list()

Function Properties

Language Return Type
- SQL SET OF gifi

    SELECT * FROM gifi ORDER BY accno;

### gl_audit_trail_append()

gl_audit_trail_append()

Function Properties

Language Return Type
- PLPGSQL trigger

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;

### goods__history(in_date_from date, in_date_to date, in_partnumber text, in_description text, in_serial_number text, in_inc_po boolean, in_inc_so boolean, in_inc_quo boolean, in_inc_rfq boolean, in_inc_is boolean, in_inc_ir boolean)

goods__history(in_date_from date, in_date_to date, in_partnumber text, in_description text, in_serial_number text, in_inc_po boolean, in_inc_so boolean, in_inc_quo boolean, in_inc_rfq boolean, in_inc_is boolean, in_inc_ir boolean)

Function Properties

Language Return Type
- SQL SET OF parts_history_result

      SELECT p.id, p.partnumber, o.transdate, p.description, p.bin,
             o.id as ord_id, o.ordnumber, o.oe_class, eca.meta_number::text, e.name,
             i.sellprice, i.qty, i.discount, i.serialnumber
        FROM parts p
        JOIN (select id, trans_id, parts_id, sellprice, qty, discount, serialnumber,
                     'o' as i_type
                FROM orderitems
               UNION
              SELECT id, trans_id, parts_id, sellprice, qty, discount, serialnumber,
                     'i' as i_type
                FROM invoice) i ON p.id = i.parts_id
        JOIN (select o.id, 'oe' as o_table, ordnumber as ordnumber, c.oe_class,
                     o.oe_class_id, o.transdate, o.entity_credit_account
                FROM oe o
                JOIN oe_class c ON o.oe_class_id = c.id
               UNION
              SELECT id, 'ar' as o_table, invnumber as ordnumber, 'is' as oe_class,
                     null, transdate, entity_credit_account
                FROM ar
               UNION
              SELECT id, 'ap' as o_table, invnumber as ordnumber, 'ir' as oe_class,
                     null, transdate, entity_credit_account
                FROM ap) o ON o.id = i.trans_id
                              AND (o_table = 'oe') = (i_type = 'o')
        JOIN entity_credit_account eca ON o.entity_credit_account = eca.id
        JOIN entity e ON e.id = eca.entity_id
       WHERE (in_partnumber is null or p.partnumber like in_partnumber || '%')
             AND (in_description IS NULL
                  OR p.description @@ plainto_tsquery(in_description))
             AND (in_date_from is null or in_date_from <= o.transdate)
             and (in_date_to is null or in_date_to >= o.transdate)
             AND (in_inc_po is not true or o.oe_class = 'Purchase Order')
             AND (in_inc_so is not true or o.oe_class = 'Sales Order')
             AND (in_inc_quo is not true or o.oe_class = 'Quotation')
             AND (in_inc_rfq is not true or o.oe_class = 'RFQ')
             AND (in_inc_ir is not true or o.oe_class = 'ir')
             AND (in_inc_is is not true or o.oe_class = 'is')
    ORDER BY o.transdate desc, o.id desc;

### goods__search(in_partnumber text, in_description text, in_partsgroup_id integer, in_serial_number text, in_make text, in_model text, in_drawing text, in_microfiche text, in_status text, in_date_from date, in_date_to date)

goods__search(in_partnumber text, in_description text, in_partsgroup_id integer, in_serial_number text, in_make text, in_model text, in_drawing text, in_microfiche text, in_status text, in_date_from date, in_date_to date)

Function Properties

Language Return Type
- SQL SET OF goods_search_result

           SELECT p.partnumber,
                  p.id, p.description, p.onhand, p.unit::text, p.priceupdate,
                  pg.partsgroup,
                  p.listprice, p.sellprice, p.lastcost, p.avgcost,
                  CASE WHEN p.lastcost = 0 THEN NULL
                       ELSE ((p.sellprice / p.lastcost) - 1) * 100
                  END as markup,
                  p.bin, p.rop, p.weight, p.notes, p.image, p.drawing, p.microfiche,
                  m.make, m.model
             FROM parts p
        LEFT JOIN makemodel m ON m.parts_id = p.id
        LEFT JOIN partsgroup pg ON p.partsgroup_id = pg.id
            WHERE (in_partnumber is null or p.partnumber ilike in_partnumber || '%')
                  AND (in_description is null
                      or p.description @@ plainto_tsquery(in_description))
                  AND (in_partsgroup_id is null
                      or p.partsgroup_id = in_partsgroup_id )
                  AND (in_make is null or m.make ilike in_make || '%')
                  AND (in_model is null or m.model  ilike in_model || '%')
                  AND (in_drawing IS NULL OR p.drawing ilike in_drawing || '%')
                  AND (in_microfiche IS NULL
                      OR p.microfiche ilike in_microfiche || '%')
                  AND (in_serial_number IS NULL OR p.id IN
                          (select parts_id from invoice
                            where in_serial_number is not null
                                  and serialnumber = in_serial_number))
                  AND ((in_status = 'active' and not p.obsolete)
                       OR (in_status = 'obsolete' and p.obsolete)
                       OR (in_status = 'short' and p.onhand <= p.rop)
                       OR (in_status = 'unused'
                          AND NOT EXISTS (select 1 FROM invoice
                                           WHERE parts_id = p.id
                                           UNION
                                          SELECT 1 FROM orderitems
                                           WHERE parts_id = p.id)));

### in_tree(in_node_id integer, in_search_array public.tree_record[])

in_tree(in_node_id integer, in_search_array public.tree_record[])

Function Properties

Language Return Type
- SQL boolean

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

### in_tree(in_node_id integer[], in_search_array public.tree_record[])

in_tree(in_node_id integer[], in_search_array public.tree_record[])

Function Properties

Language Return Type
- SQL boolean

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

### inventory__activity(in_from_date date, in_to_date date, in_partnumber text, in_description text)

inventory__activity(in_from_date date, in_to_date date, in_partnumber text, in_description text)

Function Properties

Language Return Type
- SQL SET OF inv_activity_line

        SELECT p.id, p.description, p.partnumber,
               SUM(CASE WHEN transtype = 'ar' THEN i.qty ELSE 0 END) AS sold,
               SUM(CASE WHEN transtype = 'ar' THEN i.sellprice * i.qty ELSE 0 END)
               AS receivable,
               SUM(CASE WHEN transtype = 'ap' THEN i.qty * -1 ELSE 0 END)
               AS payable,
               SUM(CASE WHEN transtype = 'ap' THEN -1 * i.sellprice * i.qty ELSE 0
                    END) AS expenses
          FROM invoice i
          JOIN parts p ON (i.parts_id = p.id)
          JOIN (select id, approved, transdate, 'ar' as transtype FROM ar
                 UNION
                SELECT id, approved, transdate, 'ap' as transdate FROM ap) a
                ON (a.id = i.trans_id AND a.approved)
         WHERE ($1 IS NULL OR a.transdate >= $1)
               AND ($2 IS NULL OR a.transdate <= $2)
               AND ($3 IS NULL OR p.partnumber ilike $3 || '%')
               AND ($4 IS NULL OR p.description @@ plainto_tsquery($4))
      GROUP BY p.id, p.description, p.partnumber

### inventory__get_item_by_id(in_id integer)

inventory__get_item_by_id(in_id integer)

Function Properties

Language Return Type
- SQL parts

    SELECT * FROM parts WHERE id = $1;

### inventory__get_item_by_partnumber(in_partnumber text)

inventory__get_item_by_partnumber(in_partnumber text)

Function Properties

Language Return Type
- SQL parts

    SELECT * FROM parts WHERE obsolete is not true AND partnumber = $1;

### inventory__search_part(in_parts_id integer, in_partnumber text, in_counted_date date)

inventory__search_part(in_parts_id integer, in_partnumber text, in_counted_date date)

Function Properties

Language Return Type
- SQL part_at_date

    WITH RECURSIVE assembly_comp (a_id, parts_id, qty) AS (
         SELECT id, parts_id, qty FROM assembly
          UNION ALL
         SELECT ac.a_id, a.parts_id, ac.qty * a.qty
           FROM assembly a JOIN assembly_comp ac ON a.parts_id = ac.parts_id
    )
         SELECT p.id, p.partnumber,
                sum((coalesce(i.qty, 0) + coalesce(oi.qty, 0)) * a.qty )
           FROM parts p
      LEFT JOIN assembly_comp a ON a.a_id = p.id
      LEFT JOIN invoice_sum i ON i.parts_id = p.id OR a.parts_id = i.parts_id
      LEFT JOIN order_sum oi ON oi.parts_id = p.id OR a.parts_id = i.parts_id
          WHERE p.id = $1 OR p.partnumber = $2
                OR (p.id IN (select parts_id FROM makemodel WHERE barcode = $2)
                   AND NOT EXISTS (select id from parts
                                    where partnumber = $2 AND NOT obsolete
                ))
                and (i.transdate is null or i.transdate <= $3)
                AND (oi.transdate IS NULL OR oi.transdate <= $3)
       GROUP BY p.id, p.partnumber;

### inventory_adj__details(in_id integer)

inventory_adj__details(in_id integer)

Function Properties

Language Return Type
- SQL SET OF inventory_adjustment_line

       SELECT l.parts_id, p.partnumber, p.description, l.counted, l.expected,
              l.counted - l.expected, p.sellprice, p.lastcost
         FROM inventory_report_line l
         JOIN parts p ON l.parts_id = p.id
        WHERE l.adjust_id = $1;

### inventory_adj__get(in_id integer)

inventory_adj__get(in_id integer)

Function Properties

Language Return Type
- SQL inventory_adjustment_info

       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.adjust_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;

### inventory_adj__search(in_from_date date, in_to_date date, in_partnumber text, in_source text)

inventory_adj__search(in_from_date date, in_to_date date, in_partnumber text, in_source text)

Function Properties

Language Return Type
- SQL SET OF inventory_adjustment_info

       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.adjust_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 || '%')
     GROUP BY r.id, r.transdate, r.source, r.ar_trans_id, r.ap_trans_id,
              ar.invnumber, ap.invnumber

### inventory_adjust__approve(in_id integer)

inventory_adjust__approve(in_id integer)

Function Properties

Language Return Type
- PLPGSQL inventory_report_line

    DECLARE inv inventory_report;
            t_ar ar;
            t_ap ap;
    BEGIN

    SELECT * INTO inv FROM inventory_report where id = in_id;

    INSERT INTO ar (entity_credit_account, invnumber, invoice, approved,
                    amount, netamount, transdate)
    VALUES (setting__get('inventory_ar_eca'), setting_increment('sinumber'),
            't', 'f', 0, 0, inv.transdate);

    SELECT * INTO t_ar FROM ar WHERE id = currval('id');

    UPDATE inventory_report
       set ar_trans_id = t_ar.id,
           ar_invnumber = t_ar.invnumber
     WHERE id = in_id;

    INSERT INTO invoice (trans_id, parts_id, description, qty, sellprice, precision,
                        discount)
    SELECT t_ar.id, p.id, p.description, l.variance * -1, p.sellprice, 3, 1
      FROM parts p
      JOIN inventory_report_line l ON p.id = l.parts_id
     WHERE l.adjust_id = in_id;

    INSERT INTO ap (entity_credit_account, invnumber, invoice, approved, amount,
                    netamount, transdate)
    SELECT setting__get('inventory_ap_eca'), setting_increment('vinumber'),
           't', 'f', sum(l.variance * p.sellprice), sum(l.variance * p.sellprice),
           inv.transdate
      FROM parts p
      JOIN inventory_report_line l ON p.id = l.parts_id
     WHERE l.adjust_id = in_id;

    SELECT * INTO t_ap FROM ap WHERE id = currval('id');

    UPDATE inventory_report
       set ap_trans_id = t_ap.id,
           ap_invnumber = t_ap.invnumber
     WHERE id = in_id;

    INSERT INTO invoice (trans_id, parts_id, description, qty, sellprice, precision,
                        discount, transdate)
    SELECT t_ap.id, p.id, p.description, l.variance * -1, p.sellprice, 3, 0
      FROM parts p
      JOIN inventory_report_line l ON p.id = l.parts_id
     WHERE l.adjust_id = in_id;

    INSERT INTO acc_trans (trans_id, chart_id, amount, transdate, approved)
    SELECT t_ap.id, p.expense_accno_id, sum(l.variance * -1 * p.lastcost),
           inv.transdate, true
      FROM parts p
      JOIN inventory_report_line l ON p.id = l.parts_id
     WHERE l.adjust_id = in_id
     GROUP BY p.expense_accno_id
     UNION
    SELECT t_ap.id, eca.ar_ap_accno_id, sum(l.variance * -1 * p.lastcost),
           inv.transdate, true
      FROM parts p
      JOIN inventory_report_line l ON p.id = l.parts_id
      JOIN entity_credit_account eca on eca_id = t_ap.entity_credit_account
     WHERE l.adjust_id = in_id
     GROUP BY eca.ar_ap_accno_id;

    SELECT * INTO inv FROM inventory_report where id = in_id;

    RETURN inv;

    END;

### inventory_adjust__delete(in_id integer)

inventory_adjust__delete(in_id integer)

Function Properties

Language Return Type
- PLPGSQL boolean

    DECLARE inv inventory_report;
    BEGIN
    SELECT * INTO inv FROM inventory_report where id = in_id;
    IF NOT FOUND THEN
       RETURN FALSE;
    ELSIF inv.ap_trans_id IS NOT NULL OR inv.ar_trans_id IS NOT NULL THEN
       RAISE EXCEPTION 'Set is Already Approved!';
    END IF;

    DELETE FROM inventory_report_line where adjust_id = in_id;
    DELETE FROM inventory_report where id = in_id;

    RETURN TRUE;

    END;

### inventory_adjust__get(in_id integer)

inventory_adjust__get(in_id integer)

Function Properties

Language Return Type
- SQL inventory_report

    SELECT * FROM inventory_report WHERE id = $1;

### inventory_adjust__get_lines(in_id integer)

inventory_adjust__get_lines(in_id integer)

Function Properties

Language Return Type
- SQL SET OF inventory_report_line

    SELECT * FROM inventory_report_line l WHERE adjust_id = $1
     ORDER BY parts_id;

### inventory_adjust__list(in_from_date date, in_to_date date, in_approved boolean)

inventory_adjust__list(in_from_date date, in_to_date date, in_approved boolean)

Function Properties

Language Return Type
- SQL SET OF inventory_report

    SELECT * FROM inventory_report
     WHERE ($1 is null or transdate >= $1)
           AND ($2 IS NULL OR transdate <= $2)
           AND ($3 IS NULL OR $3 = (ar_trans_id IS NULL AND ap_trans_id IS NULL));

### inventory_adjust__save_info(in_transdate date, in_source text)

inventory_adjust__save_info(in_transdate date, in_source text)

Function Properties

Language Return Type
- SQL inventory_report

    INSERT INTO inventory_report(transdate, source)
    VALUES ($1, $2)
    RETURNING *;

### inventory_adjust__save_line(in_adjust_id integer, in_parts_id integer, in_counted numeric, in_expected numeric, in_variance numeric)

inventory_adjust__save_line(in_adjust_id integer, in_parts_id integer, in_counted numeric, in_expected numeric, in_variance numeric)

Function Properties

Language Return Type
- SQL inventory_report_line

    INSERT INTO inventory_report_line
           (adjust_id, parts_id, counted, expected, variance)
    VALUES ($1, $2, $3, $4, $5)
    RETURNING *;

### inventory_create_report(in_transdate date)

inventory_create_report(in_transdate date)

Function Properties

Language Return Type
- SQL inventory_report

            INSERT INTO inventory_report(transdate) values (in_transdate)
            RETURNING *;

### inventory_get_item_at_day(in_transdate date, in_partnumber text)

inventory_get_item_at_day(in_transdate date, in_partnumber text)

Function Properties

Language Return Type
- PLPGSQL parts

    DECLARE out_row RECORD;
            t_parts_id int;
            int_outrow RECORD;
    BEGIN
            SELECT id INTO t_parts_id
            FROM parts
            WHERE (partnumber like in_partnumber|| ' %'
                    or partnumber = in_partnumber)
                    and obsolete is not true
                    and assembly is not true;

            SELECT * INTO out_row FROM parts WHERE id = t_parts_id;

            WITH RECURSIVE c AS (
                 SELECT 1::numeric as multiplier, t_parts_id  as part_used,
                        t_parts_id as current_part_id
                 UNION ALL
                 SELECT c.multiplier * a.qty, t_parts_id as part_used,
                        a.parts_id as current_part_id
                   FROM assembly a
                   JOIN c ON c.current_part_id = a.id
            )
            SELECT  sum(coalesce(c.multiplier, 1) * i.qty) * -1
                    AS onhand
            INTO int_outrow
            FROM parts p
            LEFT JOIN c ON c.part_used = t_parts_id
            JOIN invoice i ON (i.parts_id = p.id OR i.parts_id = c.current_part_id)
            JOIN (select id, transdate from ar
                    UNION select id, transdate from ap) a ON (i.trans_id = a.id)

            WHERE (p.partnumber = in_partnumber
                    or p.partnumber like in_partnumber || ' %')
                    AND a.transdate <= in_transdate
                    AND assembly IS FALSE AND obsolete IS NOT TRUE
            GROUP BY p.id, p.partnumber, p.description, p.unit, p.listprice,
                    p.sellprice, p.lastcost, p.priceupdate, p.weight,
                    p.onhand, p.notes, p.makemodel, p.assembly, p.alternate,
                    p.rop, p.inventory_accno_id, p.income_accno_id, p.expense_accno_id,
                    p.bin, p.obsolete, p.bom, p.image, p.microfiche, p.partsgroup_id,
                    p.avgcost;

            out_row.onhand := int_outrow.onhand;
            RETURN out_row;
    END;

### inventory_report__add_line(in_report_id integer, in_parts_id integer, in_onhand integer, in_counted integer)

inventory_report__add_line(in_report_id integer, in_parts_id integer, in_onhand integer, in_counted integer)

Function Properties

Language Return Type
- SQL inventory_report_line

            INSERT INTO inventory_report_line(adjust_id, parts_id, expected, counted)
            VALUES (in_report_id, in_parts_id, in_onhand, in_counted)
            RETURNING *;

### inventory_report__approve(in_id integer, in_ar_trans_id integer, in_ap_trans_id integer)

inventory_report__approve(in_id integer, in_ar_trans_id integer, in_ap_trans_id integer)

Function Properties

Language Return Type
- SQL integer

    update inventory_report
       SET ar_trans_id = $2, ap_trans_id = $3
     WHERE id = $1 AND ar_trans_id IS NULL AND ap_trans_id IS NULL
    RETURNING id;

### inventory_report__delete(in_id integer)

inventory_report__delete(in_id integer)

Function Properties

Language Return Type
- SQL integer

    DELETE FROM inventory_report_line WHERE adjust_id = $1;
    DELETE FROM inventory_report WHERE id = $1
    RETURNING id;

### invoice__get_by_vendor_number(in_meta_number text, in_invoice_number text)

invoice__get_by_vendor_number(in_meta_number text, in_invoice_number text)

Function Properties

Language Return Type
- SQL ap

            SELECT * FROM ap WHERE entity_credit_account =
                    (select id from entity_credit_account where entity_class = 1
                    AND meta_number = in_meta_number)
                    AND invnumber = in_invoice_number;

### is_leapyear(in_date date)

is_leapyear(in_date date)

Function Properties

Language Return Type
- SQL boolean

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;

### is_same_month(in_date1 date, in_date2 date)

is_same_month(in_date1 date, in_date2 date)

Function Properties

Language Return Type
- SQL boolean

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);

### is_same_year(in_date1 date, in_date2 date)

is_same_year(in_date1 date, in_date2 date)

Function Properties

Language Return Type
- SQL boolean

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

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

### journal__add(in_reference text, in_description text, in_journal integer, in_post_date date, in_approved boolean, in_is_template boolean, in_currency text)

journal__add(in_reference text, in_description text, in_journal integer, in_post_date date, in_approved boolean, in_is_template boolean, in_currency text)

Function Properties

Language Return Type
- SQL journal_entry

            INSERT INTO journal_entry (reference, description, journal, post_date,
                            approved, is_template, effective_start, effective_end,
                            currency, entered_by)
            VALUES (coalesce($1, ''), $2, $3, $4,
                            coalesce($5 , false),
                            coalesce($6, false),
                   $4, $4, $7, person__get_my_entity_id()) RETURNING *;

### journal__add_line(in_account_id integer, in_journal_id integer, in_amount numeric, in_cleared boolean, in_memo text, in_business_units integer[])

journal__add_line(in_account_id integer, in_journal_id integer, in_amount numeric, in_cleared boolean, in_memo text, in_business_units integer[])

Function Properties

Language Return Type
- SQL journal_line

            INSERT INTO journal_line(account_id, journal_id, amount, cleared)
            VALUES (in_account_id, in_journal_id, in_amount,
                    coalesce(in_cleared, false));

            INSERT INTO business_unit_jl(entry_id, bu_class, bu_id)
            SELECT currval('journal_line_id_seq'), class_id, id
              FROM business_unit
             WHERE id = any(in_business_units);

            SELECT * FROM journal_line where id = currval('journal_line_id_seq');

### journal__delete(in_journal_id integer)

journal__delete(in_journal_id integer)

Function Properties

Language Return Type
- SQL void

      DELETE FROM journal_line WHERE journal_id = in_journal_id;

      DELETE FROM journal_entry WHERE id = in_journal_id;

### journal__get_entry(in_id integer)

journal__get_entry(in_id integer)

Function Properties

Language Return Type
- SQL journal_entry

    SELECT * FROM journal_entry where id = $1;

### journal__get_invoice(in_id integer)

journal__get_invoice(in_id integer)

Function Properties

Language Return Type
- SQL eca_invoice

    SELECT * FROM eca_invoice where journal_id = $1;

### journal__lines(in_id integer)

journal__lines(in_id integer)

Function Properties

Language Return Type
- SQL SET OF journal_line

    select * from journal_line where journal_id = $1;

### journal__make_invoice(in_order_id integer, in_journal_id integer, in_on_hold boolean, in_reverse boolean, in_credit_id integer, in_language_code character varying)

journal__make_invoice(in_order_id integer, in_journal_id integer, in_on_hold boolean, in_reverse boolean, in_credit_id integer, in_language_code character varying)

Function Properties

Language Return Type
- PLPGSQL eca_invoice

    DECLARE retval eca_invoice;
    BEGIN
            INSERT INTO eca_invoice (order_id, journal_id, on_hold, reverse,
                    credit_id, language_code, due)
            VALUES (in_order_id, in_journal_id, coalesce(in_on_hold, false),
                    in_reverse, in_credit_id, in_language_code, 'today');

            SELECT * INTO retval FROM eca_invoice WHERE journal_id = in_journal_id;

            RETURN retval;
    END;

### journal__save_recurring_print(in_id integer, in_formname text, in_printer text)

journal__save_recurring_print(in_id integer, in_formname text, in_printer text)

Function Properties

Language Return Type
- SQL recurringprint

    insert into recurringprint (id, formname, format, printer)
    values ($1, $2, 'PDF', $3)
    returning *;

### journal__search(in_reference text, in_description text, in_entry_type integer, in_transaction_date date, in_approved boolean, in_department_id integer, in_is_template boolean, in_meta_number text, in_entity_class integer, in_recurring boolean)

journal__search(in_reference text, in_description text, in_entry_type integer, in_transaction_date date, in_approved boolean, in_department_id integer, in_is_template boolean, in_meta_number text, in_entity_class integer, in_recurring boolean)

Function Properties

Language Return Type
- PLPGSQL SET OF journal_search_result

    DECLARE retval journal_search_result;
    BEGIN
            FOR retval IN
                    SELECT j.id, j.reference , j.description, j.journal,
                            j.post_date , j.approved,
                            j.is_template, eca.meta_number,
                            e.name, ec.class,
                            coalesce(
                              r.startdate + 0, -- r.recurring_interval,
                              j.post_date )
                    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)
                    LEFT JOIN recurring r ON j.id = r.id
    --              WHERE (in_reference IS NULL OR in_reference = j.reference) AND
    --                      (in_description IS NULL
    --                              or in_description = j.description) AND
    --                      (in_entry_type is null or in_entry_type = j.journal)
    --                      and (in_transaction_date is null
    --                              or in_transaction_date = j.post_date) and
    --                      j.approved = coalesce(in_approved, true) and
    --                      j.is_template = coalesce(in_is_template, false) 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) AND
     --                       (in_recurring IS NOT TRUE OR
      --                              coalesce(r.startdate, r.nextdate) <= now()::date
       --                     )
            LOOP
                    RETURN NEXT retval;
            END LOOP;
    END;

### journal__validate_entry(in_id integer)

journal__validate_entry(in_id integer)

Function Properties

Language Return Type
- SQL boolean

            SELECT sum(amount) = 0 FROM journal_line WHERE journal_id = $1;

### lastcost(integer)

lastcost(integer)

Function Properties

Language Return Type
- PLPGSQL double precision

    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;

### leap_days(in_year_from integer, in_year_to integer)

leap_days(in_year_from integer, in_year_to integer)

Function Properties

Language Return Type
- SQL integer

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);

### list_taxforms(in_entity_id integer)

list_taxforms(in_entity_id integer)

Function Properties

Language Return Type
- PLPGSQL SET OF country_tax_form

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;

### location__deactivate(in_id integer)

location__deactivate(in_id integer)

Function Properties

Language Return Type
- SQL location

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

    SELECT * FROM location WHERE id = 1;

### location__get(in_id integer)

location__get(in_id integer)

Function Properties

Language Return Type
- SQL location

Returns the location specified by in_id.

            SELECT * FROM location WHERE id = in_id;

### location_delete(in_id integer)

location_delete(in_id integer)

Function Properties

Language Return Type
- SQL void

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

            DELETE FROM location WHERE id = in_id;

### location_list_class()

location_list_class()

Function Properties

Language Return Type
- SQL SET OF location_class_item

Lists location classes, by default in order entered.

                    SELECT l.*, as_array(e.entity_class)
                      FROM location_class l
                      JOIN location_class_to_entity_class e
                           ON (l.id = e.location_class)
                  GROUP BY l.id, l.class, l.authoritative
                  ORDER BY l.id;

### location_list_country()

location_list_country()

Function Properties

Language Return Type
- SQL SET OF country

Lists countries, by default in alphabetical order.

                    SELECT * FROM country ORDER BY name;

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

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

Function Properties

Language Return Type
- PLPGSQL integer

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;

### lock_record(in_id integer, in_session_id integer)

lock_record(in_id integer, in_session_id integer)

Function Properties

Language Return Type
- PLPGSQL boolean

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;

### lsmb__create_role(in_role text)

lsmb__create_role(in_role text)

Function Properties

Language Return Type
- PLPGSQL boolean

    BEGIN
      PERFORM * FROM pg_roles WHERE rolname = lsmb__role(in_role);
      IF FOUND THEN
         RETURN TRUE;
      END IF;

      EXECUTE 'CREATE ROLE ' || quote_ident(lsmb__role(in_role))
      || ' WITH INHERIT NOLOGIN';

      RETURN TRUE;
    END;

### lsmb__decompose_timestamp(in_timestamp timestamp with time zone)

lsmb__decompose_timestamp(in_timestamp timestamp with time zone)

Function Properties

Language Return Type
- SQL lsmb_date_fields

    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;

### lsmb__grant_exec(in_role text, in_func text)

lsmb__grant_exec(in_role text, in_func text)

Function Properties

Language Return Type
- PLPGSQL boolean

    BEGIN
       EXECUTE 'GRANT EXECUTE ON FUNCTION ' || in_func || ' TO '
       || quote_ident(lsmb__role(in_role));
       RETURN TRUE;
    END;

### lsmb__grant_menu(in_role text, in_node_id integer, in_perm_type text)

lsmb__grant_menu(in_role text, in_node_id integer, in_perm_type text)

Function Properties

Language Return Type
- PLPGSQL boolean

    BEGIN
       PERFORM * FROM pg_roles WHERE rolname = lsmb__role(in_role);
       IF NOT FOUND THEN
          RAISE EXCEPTION 'Role not found';
       END IF;
       PERFORM * FROM menu_attribute
         WHERE attribute = 'menu' AND node_id = in_node_id;
       IF FOUND THEN
          RAISE EXCEPTION 'Cannot grant to submenu';
       END IF;
       IF in_perm_type NOT IN ('allow', 'deny') THEN
          RAISE EXCEPTION 'Invalid perm type';
       END IF;
       PERFORM * FROM menu_acl
         WHERE node_id = in_node_id AND role_name = lsmb__role(in_role)
               AND acl_type = in_perm_type;
       IF FOUND THEN RETURN TRUE;
       END IF;
       INSERT INTO menu_acl (node_id, role_name, acl_type)
       VALUES (in_node_id, lsmb__role(in_role), in_perm_type);
       RETURN TRUE;
    END;

### lsmb__grant_perms(in_role text, in_table text, in_perms text)

lsmb__grant_perms(in_role text, in_table text, in_perms text)

Function Properties

Language Return Type
- PLPGSQL boolean

    BEGIN
       IF upper(in_perms) NOT IN ('ALL', 'INSERT', 'UPDATE', 'SELECT', 'DELETE') THEN
          RAISE EXCEPTION 'Invalid permission';
       END IF;
       EXECUTE 'GRANT ' || in_perms || ' ON ' || quote_ident(in_table)
       || ' TO ' ||  quote_ident(lsmb__role(in_role));

       RETURN TRUE;
    END;

### lsmb__grant_perms(in_role text, in_table text, in_perms text, in_cols text[])

lsmb__grant_perms(in_role text, in_table text, in_perms text, in_cols text[])

Function Properties

Language Return Type
- PLPGSQL boolean

    BEGIN
       IF upper(in_perms) NOT IN ('ALL', 'INSERT', 'UPDATE', 'SELECT', 'DELETE') THEN
          RAISE EXCEPTION 'Invalid permission';
       END IF;
       EXECUTE 'GRANT ' || in_perms
       || '(' || array_to_string(quote_ident_array(in_cols), ', ')
       || ') ON ' || quote_ident(in_table)|| ' TO '
       ||  quote_ident(lsmb__role(in_role));
       RETURN TRUE;
    END;

### lsmb__grant_role(in_child text, in_parent text)

lsmb__grant_role(in_child text, in_parent text)

Function Properties

Language Return Type
- PLPGSQL boolean

    BEGIN
       EXECUTE 'GRANT ' || quote_ident(lsmb__role(in_parent)) || ' TO '
       || quote_ident(lsmb__role(in_child));
       RETURN TRUE;
    END;

### lsmb__is_allowed_role(in_rolelist text[])

lsmb__is_allowed_role(in_rolelist text[])

Function Properties

Language Return Type
- SQL boolean

    select bool_and(pg_has_role(lsmb__role(r), 'USAGE'))
      from unnest(in_rolelist) r;

### lsmb__max_date()

lsmb__max_date()

Function Properties

Language Return Type
- SQL date

     SELECT max(transdate) FROM acc_trans;

### lsmb__min_date()

lsmb__min_date()

Function Properties

Language Return Type
- SQL date

     SELECT min(transdate) from acc_trans;

### lsmb__role(global_role text)

lsmb__role(global_role text)

Function Properties

Language Return Type
- SQL text

Prepends the role prefix to a role name. E.g. 'contact_edit' is converted to 'lsmb_mycompany__contact_edit'

     select lsmb__role_prefix() || $1;

### lsmb__role_prefix()

lsmb__role_prefix()

Function Properties

Language Return Type
- SQL text

Returns the prefix text to be used for roles. E.g. 'lsmb__mycompany_'

     select coalesce((setting_get('role_prefix')).value,
                       'lsmb_' || current_database() || '__');

### lsmb_module__get(in_id integer)

lsmb_module__get(in_id integer)

Function Properties

Language Return Type
- SQL lsmb_module

Retrieves a single module's info by id.

     SELECT * FROM lsmb_module where id = $1;

### lsmb_module__list()

lsmb_module__list()

Function Properties

Language Return Type
- SQL SET OF lsmb_module

Returns a list of all defined modules, ordered by id.

     SELECT * FROM lsmb_module ORDER BY id

### menu_children(in_parent_id integer)

menu_children(in_parent_id integer)

Function Properties

Language Return Type
- SQL SET OF menu_item

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;

### menu_generate()

menu_generate()

Function Properties

Language Return Type
- SQL SET OF menu_item

This function returns the complete menu tree. It is used to generate nested menus for the web interface.

                   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[]

### menu_insert(in_parent_id integer, in_position integer, in_label text)

menu_insert(in_parent_id integer, in_position integer, in_label text)

Function Properties

Language Return Type
- PLPGSQL integer

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;

### mfg_lot__commit(in_id integer)

mfg_lot__commit(in_id integer)

Function Properties

Language Return Type
- PLPGSQL numeric

    DECLARE t_mfg_lot mfg_lot;
    BEGIN
        SELECT * INTO t_mfg_lot FROM mfg_lot WHERE id = $1;
        IF NOT FOUND THEN
            RAISE EXCEPTION 'Lot not found';
        END IF;

        UPDATE parts SET onhand = onhand
                                  - (select t_mfg_lot.qty from mfg_lot_item
                                      WHERE parts_id = parts.id AND
                                            mfg_lot_id = $1)
         WHERE id in (select parts_id from mfg_lot_item
                       WHERE mfg_lot_id = $1);

        UPDATE parts SET onhand = onhand + t_mfg_lot.qty
         where id = t_mfg_lot.parts_id;

        INSERT INTO gl (reference, description, transdate, approved)
        values ('mfg-' || $1::TEXT, 'Manufacturing lot',
                now(), true);

        INSERT INTO invoice (trans_id, parts_id, qty, allocated)
        SELECT currval('id')::int, parts_id, qty, 0
          FROM mfg_lot_item WHERE mfg_lot_id = $1;

        PERFORM cogs__add_for_ar_line(id) FROM invoice
          WHERE trans_id = currval('id')::int;

        PERFORM * FROM invoice
          WHERE qty + allocated <> 0 AND trans_id = currval('id')::int;

        IF FOUND THEN
           RAISE EXCEPTION 'Not enough parts in stock';
        END IF;

        INSERT INTO invoice (trans_id, parts_id, qty, allocated, sellprice)
        SELECT currval('id')::int, t_mfg_lot.parts_id, t_mfg_lot.qty * -1, 0,
               sum(amount) / t_mfg_lot.qty
          FROM acc_trans
         WHERE amount < 0 and trans_id = currval('id')::int;

        PERFORM cogs__add_for_ap_line(currval('invoice_id_seq')::int);

        -- move from reverse COGS.
        INSERT INTO acc_trans(trans_id, chart_id, transdate, amount)
        SELECT trans_id, chart_id, transdate, amount * -1
          FROM acc_trans
         WHERE amount < 0 and trans_id = currval('id')::int;

        -- difference goes into inventory
        INSERT INTO acc_trans(trans_id, transdate, amount, chart_id)
        SELECT trans_id, now(), sum(amount) * -1,
               (select inventory_accno_id from parts where id = t_mfg_lot.parts_id)
          FROM acc_trans
         WHERE trans_id = currval('id')::int
      GROUP BY trans_id;

        RETURN t_mfg_lot.qty;
    END;

### months_passed(in_start timestamp without time zone, in_end timestamp without time zone)

months_passed(in_start timestamp without time zone, in_end timestamp without time zone)

Function Properties

Language Return Type
- SQL integer

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;

### next_leap_year_calc(in_date date, is_end boolean)

next_leap_year_calc(in_date date, is_end boolean)

Function Properties

Language Return Type
- SQL integer

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;

### order__combine(in_ids integer[])

order__combine(in_ids integer[])

Function Properties

Language Return Type
- PLPGSQL SET OF oe

    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;

### order__search(in_oe_class_id integer, in_meta_number text, in_legal_name text, in_ponumber text, in_ordnumber text, in_open boolean, in_closed boolean, in_shipvia text, in_description text, in_date_from date, in_date_to date, in_shippable boolean, in_buisness_units integer[])

order__search(in_oe_class_id integer, in_meta_number text, in_legal_name text, in_ponumber text, in_ordnumber text, in_open boolean, in_closed boolean, in_shipvia text, in_description text, in_date_from date, in_date_to date, in_shippable boolean, in_buisness_units integer[])

Function Properties

Language Return Type
- SQL SET OF order_search_line

           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.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.id
             FROM oe o
             JOIN entity_credit_account ct ON (o.entity_credit_account = ct.id)
             JOIN entity c ON (c.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.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 and o.closed is false)
                     OR (in_closed is true and 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);

### overpayment__reverse(in_id integer, in_transdate date, in_batch_id integer, in_account_class integer, in_exchangerate numeric, in_curr bpchar)

overpayment__reverse(in_id integer, in_transdate date, in_batch_id integer, in_account_class integer, in_exchangerate numeric, in_curr bpchar)

Function Properties

Language Return Type
- PLPGSQL boolean

    declare t_id int;
            in_cash_accno text;
    BEGIN

    -- reverse overpayment gl

    INSERT INTO gl (transdate, reference, description, approved)
    SELECT transdate, reference || '-reversal', 'reversal of ' || description, '0'
      FROM gl WHERE id = (select gl_id from payment where id = in_id);

    IF NOT FOUND THEN
       RETURN FALSE;
    END IF;

    t_id := currval('id');

    INSERT INTO voucher (batch_id, trans_id, batch_class)
    VALUES (in_batch_id, t_id, CASE WHEN in_account_class = 1 THEN 4 ELSE 7 END);

    INSERT INTO acc_trans (transdate, trans_id, chart_id, amount)
    SELECT in_transdate, t_id, chart_id, amount * -1
      FROM acc_trans
     WHERE trans_id = in_id;

    -- reverse overpayment usage
    PERFORM payment__reverse(ac.source, ac.transdate, eca.id, at.accno,
            in_transdate, eca.entity_class, in_batch_id, null,
            in_exchangerate, in_curr)
      FROM acc_trans ac
      JOIN account at ON ac.chart_id = at.id
      JOIN account_link al ON at.id = al.account_id AND al.description like 'A%paid'
      JOIN (select id, entity_credit_account FROM ar UNION
            select id, entity_credit_account from ap) a ON a.id = ac.trans_id
      JOIN entity_credit_account eca ON a.entity_credit_account = eca.id
      JOIN payment_links pl ON pl.entry_id = ac.entry_id
      JOIN overpayments op ON op.payment_id = pl.payment_id
      JOIN payment p ON p.id = op.payment_id
     WHERE p.gl_id = in_id
    GROUP BY ac.source, ac.transdate, eca.id, eca.entity_class,
             at.accno, al.description;

    RETURN TRUE;
    END;

### parse_date(in_date date)

parse_date(in_date date)

Function Properties

Language Return Type
- SQL date

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

     select $1;

### part__get_by_id(in_id integer)

part__get_by_id(in_id integer)

Function Properties

Language Return Type
- SQL parts

    select * from parts where id = $1;

### parts__get_by_id(in_id integer)

parts__get_by_id(in_id integer)

Function Properties

Language Return Type
- SQL parts

    SELECT * FROM parts WHERE id = $1;

### parts__get_by_partnumber(in_partnumber text)

parts__get_by_partnumber(in_partnumber text)

Function Properties

Language Return Type
- SQL parts

    SELECT * FROM parts where partnumber = $1 AND NOT OBSOLETE;

### parts__search_lite(in_partnumber text, in_description text)

parts__search_lite(in_partnumber text, in_description text)

Function Properties

Language Return Type
- SQL SET OF parts

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

### partsgroup__search(in_pricegroup text)

partsgroup__search(in_pricegroup text)

Function Properties

Language Return Type
- SQL SET OF partsgroup

      SELECT * FROM partsgroup
       WHERE $1 is null or partsgroup ilike $1 || '%'
    ORDER BY partsgroup;

### payment__get_gl(in_payment_id integer)

payment__get_gl(in_payment_id integer)

Function Properties

Language Return Type
- SQL gl

    SELECT * FROM gl WHERE id = (select id from payment where id = $1);

### payment__overpayments_list(in_date_from date, in_date_to date, in_control_code text, in_meta_number text, in_name_part text)

payment__overpayments_list(in_date_from date, in_date_to date, in_control_code text, in_meta_number text, in_name_part text)

Function Properties

Language Return Type
- SQL SET OF overpayment_list_item

    -- I don't like the subquery below but we are looking for the first line, and
    -- I can't think of a better way to do that. --CT

    -- This should never hit an income statement-side account but I have handled it
    -- in case of configuration error. --CT
    SELECT o.payment_id, e.name, o.available, g.transdate,
           (select amount * CASE WHEN c.category in ('A', 'E') THEN -1 ELSE 1 END
              from acc_trans
             where g.id = trans_id
                   AND chart_id = o.chart_id ORDER BY entry_id ASC LIMIT 1) as amount
      FROM overpayments o
      JOIN payment p ON o.payment_id = p.id
      JOIN gl g ON g.id = p.gl_id
      JOIN account c ON c.id = o.chart_id
      JOIN entity_credit_account eca ON eca.id = o.entity_credit_id
      JOIN entity e ON eca.entity_id = e.id
     WHERE ($1 IS NULL OR $1 <= g.transdate) AND
           ($2 IS NULL OR $2 >= g.transdate) AND
           ($3 IS NULL OR $3 = e.control_code) AND
           ($4 IS NULL OR $4 = eca.meta_number) AND
           ($5 IS NULL OR e.name @@ plainto_tsquery($5));

### payment__reverse(in_source text, in_date_paid date, in_credit_id integer, in_cash_accno text, in_date_reversed date, in_account_class integer, in_batch_id integer, in_voucher_id integer, in_exchangerate numeric, in_currency bpchar)

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

Function Properties

Language Return Type
- PLPGSQL integer

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 coalesce(a.source, '') = coalesce(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;

### payment__search(in_source text, in_from_date date, in_to_date date, in_credit_id integer, in_cash_accno text, in_entity_class integer, in_currency bpchar, in_meta_number text)

payment__search(in_source text, in_from_date date, in_to_date date, in_credit_id integer, in_cash_accno text, in_entity_class integer, in_currency bpchar, in_meta_number text)

Function Properties

Language Return Type
- SQL SET OF payment_record

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.

                    select sum(CASE WHEN c.entity_class = 1 then a.amount
                                    ELSE a.amount * -1 END), c.meta_number,
                            c.id, e.name as 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, approved
                            FROM ar WHERE in_entity_class = 2
                            UNION
                            SELECT entity_credit_account, id, curr, approved
                            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 account ch ON (ch.id = a.chart_id)
                    JOIN entity e ON (c.entity_id = e.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
                            OR (in_cash_accno IS NULL
                                AND ch.id IN (select account_id
                                                FROM account_link
                                               WHERE description IN('AR_paid',
                                                                    'AP_paid')))))
                          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_from_date
                                  OR in_from_date IS NULL)
                          AND (a.transdate <= in_to_date OR in_to_date IS NULL)
                          AND (source = in_source OR in_source IS NULL)
                          AND arap.approved AND a.approved
                          AND (c.meta_number = in_meta_number
                                  OR in_meta_number IS NULL)
                    GROUP BY c.meta_number, c.id, e.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;

### payment_bulk_post(in_transactions numeric[], in_batch_id integer, in_source text, in_total numeric, in_ar_ap_accno text, in_cash_accno text, in_payment_date date, in_account_class integer, in_exchangerate numeric, in_currency text)

payment_bulk_post(in_transactions numeric[], in_batch_id integer, in_source text, in_total numeric, in_ar_ap_accno text, in_cash_accno text, in_payment_date date, in_account_class integer, in_exchangerate numeric, in_currency text)

Function Properties

Language Return Type
- PLPGSQL integer

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;
            t_batch batch;
    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
                    SELECT * INTO t_batch FROM batch WHERE in_batch_id = id;
                    IF t_batch.approved_by IS NOT NULL THEN
                        RAISE EXCEPTION 'Approved Batch';
                    ELSIF t_batch.locked_by IS NOT NULL THEN
                        PERFORM * FROM session
                           JOIN users ON (session.users_id = users.id)
                          WHERE session_id = t_batch.locked_by
                                AND users.username = SESSION_USER;

                        IF NOT FOUND THEN
                            -- locked by someone else
                            RAISE EXCEPTION 'batch locked by %, I am %', t_batch.locked_by, session_user;
                        END IF;
                    END IF;
                    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
            IF t_cash_sign IS NULL THEN
                 raise exception 't_cash_sign is null';
            ELSIF t_exchangerate IS NULL THEN
                 raise exception 't_exchangerate is null';
            END IF;
            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
                   and eca.discount_terms is not null AND discount IS NOT NULL
                   AND eca.discount_account_id IS NOT NULL;

            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
                   AND eca.discount_terms IS NOT NULL AND discount IS NOT NULL
                   AND eca.discount_account_id IS NOT NULL;

            -- 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;

### payment_gather_header_info(in_account_class integer, in_payment_id integer)

payment_gather_header_info(in_account_class integer, in_payment_id integer)

Function Properties

Language Return Type
- SQL SET OF payment_header_item

This function finds a payment based on the id and retrieves the record, it is usefull for printing payments :)

       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;

### payment_gather_line_info(in_account_class integer, in_payment_id integer)

payment_gather_line_info(in_account_class integer, in_payment_id integer)

Function Properties

Language Return Type
- SQL SET OF payment_line_item

This function finds a payment based on the id and retrieves all the line records, it is usefull for printing payments and build reports :)

         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, ac.fx_transaction,
         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;

### payment_get_all_accounts(in_account_class integer)

payment_get_all_accounts(in_account_class integer)

Function Properties

Language Return Type
- SQL SET OF payment_open_account

This function takes a single argument (1 for vendor, 2 for customer as always) and returns all entities with accounts of the appropriate type.

                    SELECT  ec.id,
                            e.name, ec.entity_class
                    FROM entity e
                    JOIN entity_credit_account ec ON (ec.entity_id = e.id)
                                    WHERE e.entity_class = in_account_class

### payment_get_all_contact_invoices(in_account_class integer, in_business_id integer, in_currency bpchar, in_date_from date, in_date_to date, in_batch_id integer, in_ar_ap_accno text, in_meta_number text)

payment_get_all_contact_invoices(in_account_class integer, in_business_id integer, in_currency bpchar, in_date_from date, in_date_to date, in_batch_id integer, in_ar_ap_accno text, in_meta_number text)

Function Properties

Language Return Type
- SQL SET OF payment_contact_invoice

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.

                      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  c.threshold is null or (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;

### payment_get_available_overpayment_amount(in_account_class integer, in_entity_credit_id integer)

payment_get_available_overpayment_amount(in_account_class integer, in_entity_credit_id integer)

Function Properties

Language Return Type
- SQL SET OF payment_overpayments_available_amount

                  SELECT chart_id, accno,   chart_description, available
                  FROM overpayments
                  WHERE payment_class  = in_account_class
                  AND entity_credit_id = in_entity_credit_id
                  AND available <> 0;

### payment_get_entity_account_payment_info(in_entity_credit_id integer)

payment_get_entity_account_payment_info(in_entity_credit_id integer)

Function Properties

Language Return Type
- SQL payment_vc_info

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

     SELECT ec.id, coalesce(ec.pay_to_name, 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;

### payment_get_entity_accounts(in_account_class integer, in_vc_name text, in_vc_idn text, in_datefrom date, in_dateto date)

payment_get_entity_accounts(in_account_class integer, in_vc_name text, in_vc_idn text, in_datefrom date, in_dateto date)

Function Properties

Language Return Type
- SQL SET OF payment_vc_info

Returns a minimal set of information about customer or vendor accounts as needed for discount calculations and the like.

                  SELECT ec.id, coalesce(ec.pay_to_name, e.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)
                    WHERE ec.entity_class = in_account_class
                    AND (e.name ilike coalesce('%'||in_vc_name||'%','%%')
                        OR EXISTS (select 1 FROM company
                                    WHERE entity_id = e.id AND tax_id = in_vc_idn))
                    AND (coalesce(ec.enddate, now()::date)
                         >= coalesce(in_datefrom, now()::date))
                    AND (coalesce(ec.startdate, now()::date)
                         <= coalesce(in_dateto, now()::date))

### payment_get_open_accounts(in_account_class integer, in_datefrom date, in_dateto date)

payment_get_open_accounts(in_account_class integer, in_datefrom date, in_dateto date)

Function Properties

Language Return Type
- SQL SET OF payment_open_account

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.

                    SELECT ec.id, e.name, ec.entity_class
                    FROM entity e
                    JOIN entity_credit_account ec ON (ec.entity_id = e.id)
                            WHERE ec.entity_class = in_account_class
                            AND (coalesce(ec.enddate, now()::date)
                                 <= coalesce(in_dateto, now()::date))
                            AND (coalesce(ec.startdate, now()::date)
                                 >= coalesce(in_datefrom, now()::date))
                            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;

### payment_get_open_invoice(in_account_class integer, in_entity_credit_id integer, in_curr bpchar, in_datefrom date, in_dateto date, in_amountfrom numeric, in_amountto numeric, in_invnumber text)

payment_get_open_invoice(in_account_class integer, in_entity_credit_id integer, in_curr bpchar, in_datefrom date, in_dateto date, in_amountfrom numeric, in_amountto numeric, in_invnumber text)

Function Properties

Language Return Type
- SQL SET OF payment_invoice

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

                    SELECT * from payment_get_open_invoices(in_account_class, in_entity_credit_id, in_curr, in_datefrom, in_dateto, in_amountfrom,
                    in_amountto)
                    WHERE (invnumber like in_invnumber OR in_invnumber IS NULL);

### payment_get_open_invoices(in_account_class integer, in_entity_credit_id integer, in_curr bpchar, in_datefrom date, in_dateto date, in_amountfrom numeric, in_amountto numeric)

payment_get_open_invoices(in_account_class integer, in_entity_credit_id integer, in_curr bpchar, in_datefrom date, in_dateto date, in_amountfrom numeric, in_amountto numeric)

Function Properties

Language Return Type
- SQL SET OF payment_invoice

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.

                    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, 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, 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 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;

### payment_get_open_overpayment_entities(in_account_class integer)

payment_get_open_overpayment_entities(in_account_class integer)

Function Properties

Language Return Type
- SQL SET OF payment_vc_info

                    SELECT DISTINCT entity_credit_id, legal_name, e.entity_class, null::int, o.meta_number
                    FROM overpayments o
                    JOIN entity e ON (e.id=o.entity_id)
                    WHERE available <> 0 AND in_account_class = payment_class;

### payment_get_unused_overpayment(in_account_class integer, in_entity_credit_id integer, in_chart_id integer)

payment_get_unused_overpayment(in_account_class integer, in_entity_credit_id integer, in_chart_id integer)

Function Properties

Language Return Type
- SQL SET OF overpayments

Returns a list of available overpayments

                  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;

### payment_get_vc_info(in_entity_credit_id integer, in_location_class_id integer)

payment_get_vc_info(in_entity_credit_id integer, in_location_class_id integer)

Function Properties

Language Return Type
- SQL SET OF payment_location_result

This function returns vendor or customer info

                    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.id)
                    WHERE ec.id = in_entity_credit_id AND
                          lc.id = in_location_class_id
                    ORDER BY lc.id, l.id, c.name

### payment_post(in_datepaid date, in_account_class integer, in_entity_credit_id integer, in_curr bpchar, in_notes text, in_gl_description text, in_cash_account_id integer[], in_amount numeric[], in_cash_approved boolean[], in_source text[], in_memo text[], in_transaction_id integer[], in_op_amount numeric[], in_op_cash_account_id integer[], in_op_source text[], in_op_memo text[], in_op_account_id integer[], in_ovp_payment_id integer[], in_approved boolean)

payment_post(in_datepaid date, in_account_class integer, in_entity_credit_id integer, in_curr bpchar, in_notes text, in_gl_description text, in_cash_account_id integer[], in_amount numeric[], in_cash_approved boolean[], in_source text[], in_memo text[], in_transaction_id integer[], in_op_amount numeric[], in_op_cash_account_id integer[], in_op_source text[], in_op_memo text[], in_op_account_id integer[], in_ovp_payment_id integer[], in_approved boolean)

Function Properties

Language Return Type
- PLPGSQL integer

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, 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_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]
                            ELSE in_amount[out_count]* - 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;

                    IF current_exchangerate <> 1 THEN
                       INSERT INTO acc_trans (chart_id, amount, fx_transaction,
                                                  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-1)
                            ELSE in_amount[out_count]*(current_exchangerate-1)* - 1
                            END, 't'::boolean,
                            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 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]* -1
                            ELSE in_amount[out_count]
                            END,
                            in_transaction_id[out_count], in_datepaid,  coalesce(in_approved, true),
                            in_source[out_count], in_memo[out_count]);
             -- Now we set the links
             INSERT INTO payment_links
                    VALUES (var_payment_id, currval('acc_trans_entry_id_seq'), 1);
             INSERT INTO acc_trans (chart_id, amount, fx_transaction,
                                    trans_id, transdate, approved, source, memo)
                    VALUES (var_account_id,
                            CASE WHEN in_account_class = 1 THEN
                            in_amount[out_count]*(old_exchangerate-1) * -1
                            ELSE in_amount[out_count]*(old_exchangerate-1)
                            END, 't'::boolean,
                            in_transaction_id[out_count], in_datepaid,  coalesce(in_approved, true),
                            in_source[out_count], in_memo[out_count]);
             -- Now we set the links
             INSERT INTO payment_links
                    VALUES (var_payment_id, currval('acc_trans_entry_id_seq'), 1);
             -- 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]);
                -- Now we set the links
                INSERT INTO payment_links
                    VALUES (var_payment_id, currval('acc_trans_entry_id_seq'), 1);
             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]);
                -- Now we set the links
                INSERT INTO payment_links
                    VALUES (var_payment_id, currval('acc_trans_entry_id_seq'), 1);
             END IF;
           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)
                  VALUES (setting_increment('glnumber'),
                          in_gl_description, in_datepaid, var_employee,
                          in_notes, in_approved);
           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;

### payment_type__get_label(in_payment_type_id integer)

payment_type__get_label(in_payment_type_id integer)

Function Properties

Language Return Type
- SQL SET OF payment_type

Returns all information on a payment type by the id. This should be renamed to account for its behavior in future versions.

    SELECT * FROM payment_type where id=in_payment_type_id;

### payment_type__list()

payment_type__list()

Function Properties

Language Return Type
- SQL SET OF payment_type

    SELECT * FROM payment_type;

### payments_get_open_currencies(in_account_class integer)

payments_get_open_currencies(in_account_class integer)

Function Properties

Language Return Type
- PLPGSQL SET OF bpchar

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;

### payments_set_exchangerate(in_account_class integer, in_exchangerate numeric, in_curr bpchar, in_datepaid date)

payments_set_exchangerate(in_account_class integer, in_exchangerate numeric, in_curr bpchar, in_datepaid date)

Function Properties

Language Return Type
- PLPGSQL integer

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;

### payroll_deduction_type__search(in_account_id integer, in_pdc_id integer, in_country_id integer, in_label text, in_unit text)

payroll_deduction_type__search(in_account_id integer, in_pdc_id integer, in_country_id integer, in_label text, in_unit text)

Function Properties

Language Return Type
- SQL SET OF payroll_deduction_type

    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);

### payroll_income_category__list()

payroll_income_category__list()

Function Properties

Language Return Type
- SQL SET OF payroll_income_category

    SELECT * FROM payroll_income_category order by id;

### payroll_income_class__for_country(in_country_id integer)

payroll_income_class__for_country(in_country_id integer)

Function Properties

Language Return Type
- SQL SET OF payroll_income_class

    SELECT * FROM payroll_income_class where country_id = $1
    ORDER BY label;

### payroll_income_type__get(in_id integer)

payroll_income_type__get(in_id integer)

Function Properties

Language Return Type
- SQL payroll_income_type

    SELECT * FROM payroll_income_type WHERE id  = $1;

### payroll_income_type__save(in_id integer, in_account_id integer, in_pic_id integer, in_country_id integer, in_label text, in_unit text, in_default_amount numeric)

payroll_income_type__save(in_id integer, in_account_id integer, in_pic_id integer, in_country_id integer, in_label text, in_unit text, in_default_amount numeric)

Function Properties

Language Return Type
- PLPGSQL payroll_income_type

       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;

### payroll_income_type__search(in_account_id integer, in_pic_id integer, in_country_id integer, in_label text, in_unit text)

payroll_income_type__search(in_account_id integer, in_pic_id integer, in_country_id integer, in_label text, in_unit text)

Function Properties

Language Return Type
- SQL SET OF payroll_income_type

    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);

### periods_get()

periods_get()

Function Properties

Language Return Type
- SQL SET OF periods

Returns dates for year to date, and last year.

    SELECT * FROM periods ORDER BY id

### person__delete_contact(in_person_id integer, in_contact_class_id integer, in_contact text)

person__delete_contact(in_person_id integer, in_contact_class_id integer, in_contact text)

Function Properties

Language Return Type
- SQL boolean

Deletes a contact record specified for the person. Returns true if a record was found and deleted, false if not.

    DELETE FROM entity_to_contact
     WHERE entity_id = (SELECT entity_id FROM person WHERE id = in_person_id)
           and contact_class_id = in_contact_class_id
           and contact= in_contact
    RETURNING TRUE;

### person__delete_location(in_person_id integer, in_location_id integer, in_location_class integer)

person__delete_location(in_person_id integer, in_location_id integer, in_location_class integer)

Function Properties

Language Return Type
- SQL boolean

Deletes a location mapping to a person. Returns true if found, false if no data deleted.

    DELETE FROM entity_to_location
     WHERE entity_id = (select entity_id from person where id = in_person_id)
           AND location_id = in_location_id
           AND location_class = in_location_class
    RETURNING TRUE;

### person__get(in_entity_id integer)

person__get(in_entity_id integer)

Function Properties

Language Return Type
- SQL person_entity

    SELECT e.id, e.control_code, e.name, e.country_id, c.name,
           p.first_name, p.middle_name, p.last_name, e.entity_class,
           p.birthdate, p.personal_id
      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;

### person__get_by_cc(in_control_code text)

person__get_by_cc(in_control_code text)

Function Properties

Language Return Type
- SQL person_entity

    SELECT e.id, e.control_code, e.name, e.country_id, c.name,
           p.first_name, p.middle_name, p.last_name, e.entity_class,
           p.birthdate, p.personal_id
      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;

### person__get_my_entity_id()

person__get_my_entity_id()

Function Properties

Language Return Type
- SQL integer

Returns the entity_id of the current, logged in user.

            SELECT entity_id from users where username = SESSION_USER;

### person__get_my_id()

person__get_my_id()

Function Properties

Language Return Type
- SQL integer

Returns the person id of the current, logged in user.

            SELECT p.id from person p
            JOIN users u ON u.entity_id = p.entity_id
            WHERE username = SESSION_USER;

### person__list_bank_account(in_entity_id integer)

person__list_bank_account(in_entity_id integer)

Function Properties

Language Return Type
- SQL SET OF entity_bank_account

Lists bank accounts for a person

    SELECT * from entity_bank_account where entity_id = in_entity_id

### person__list_contacts(in_entity_id integer)

person__list_contacts(in_entity_id integer)

Function Properties

Language Return Type
- SQL SET OF contact_list

Returns a list of contacts attached to the function.

                    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.entity_id = p.entity_id)
                    WHERE p.entity_id = in_entity_id

### person__list_languages()

person__list_languages()

Function Properties

Language Return Type
- SQL SET OF language

Returns a list of languages ordered by code

     SELECT * FROM language ORDER BY description ASC

### person__list_locations(in_entity_id integer)

person__list_locations(in_entity_id integer)

Function Properties

Language Return Type
- PLPGSQL SET OF location_result

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.entity_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;

### person__list_notes(in_entity_id integer)

person__list_notes(in_entity_id integer)

Function Properties

Language Return Type
- SQL SET OF entity_note

Returns a list of notes attached to a person.

                    SELECT *
                    FROM entity_note
                    WHERE ref_key = in_entity_id
                    ORDER BY created

### person__list_salutations()

person__list_salutations()

Function Properties

Language Return Type
- SQL SET OF salutation

Returns a list of salutations ordered by id.

     SELECT * FROM salutation ORDER BY id ASC

### person__save(in_entity_id integer, in_salutation_id integer, in_first_name text, in_middle_name text, in_last_name text, in_country_id integer, in_birthdate date, in_personal_id text)

person__save(in_entity_id integer, in_salutation_id integer, in_first_name text, in_middle_name text, in_last_name text, in_country_id integer, in_birthdate date, in_personal_id text)

Function Properties

Language Return Type
- PLPGSQL integer

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,
                birthdate = in_birthdate,
                personal_id = in_personal_id
        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,
                               birthdate, personal_id)
            VALUES (in_salutation_id, in_first_name, in_last_name, e_id,
                    in_birthdate, in_personal_id);

            RETURN e_id;

        END IF;
    END;

### person__save_contact(in_entity_id integer, in_contact_class integer, in_old_contact text, in_contact_new text, in_description text, in_old_contact_class integer)

person__save_contact(in_entity_id integer, in_contact_class integer, in_old_contact text, in_contact_new text, in_description text, in_old_contact_class integer)

Function Properties

Language Return Type
- PLPGSQL integer

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;

### person__save_location(in_entity_id integer, in_location_id integer, in_location_class integer, in_line_one text, in_line_two text, in_line_three text, in_city text, in_state text, in_mail_code text, in_country_code integer, in_old_location_class integer)

person__save_location(in_entity_id integer, in_location_id integer, in_location_class integer, in_line_one text, in_line_two text, in_line_three text, in_city text, in_state text, in_mail_code text, in_country_code integer, in_old_location_class integer)

Function Properties

Language Return Type
- PLPGSQL integer

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;

### pnl__customer(in_id integer, in_from_date date, in_to_date date, in_language text)

pnl__customer(in_id integer, in_from_date date, in_to_date date, in_language text)

Function Properties

Language Return Type
- SQL SET OF pnl_line

    WITH acc_meta AS (
      SELECT a.id, a.accno,
             coalesce(at.description, a.description) as description,
             CASE WHEN (SELECT value::int FROM defaults where setting_key = 'earn_id') IS NULL THEN aht.path
             ELSE array_splice_from((SELECT value::int FROM defaults
                                 WHERE setting_key = 'earn_id'),aht.path)
             END AS path,
             a.category, 'A'::char as account_type, contra, a.gifi_accno,
             gifi.description as gifi_description
         FROM account a
        INNER JOIN account_heading_tree aht on a.heading = aht.id
         LEFT JOIN gifi ON a.gifi_accno = gifi.accno
         LEFT JOIN (SELECT trans_id, description
                      FROM account_translation
                     WHERE language_code =
                            coalesce($4,
                              (SELECT up.language
                                 FROM user_preference up
                           INNER JOIN users ON up.id = users.id
                                WHERE users.username = SESSION_USER))) at
                   ON a.id = at.trans_id
       WHERE array_splice_from((SELECT value::int FROM defaults
                                 WHERE setting_key = 'earn_id'),aht.path)
                              IS NOT NULL
             -- legacy: earn_id not configured (yet)
             OR (NOT EXISTS (SELECT 1 FROM defaults
                             WHERE setting_key = 'earn_id'
                               AND value IS NOT NULL)
                 AND category IN ('E', 'I'))
    ),
    hdr_meta AS (
       SELECT aht.id, aht.accno,
              coalesce(at.description, aht.description) as description,
              CASE WHEN (SELECT value::int FROM defaults where setting_key = 'earn_id') IS NULL THEN aht.path
              ELSE array_splice_from((SELECT value::int FROM defaults
                                  WHERE setting_key = 'earn_id'),aht.path)
              END AS path,
              ahc.derived_category as category,
              'H'::char as account_type, 'f'::boolean as contra
         FROM account_heading_tree aht
        INNER JOIN account_heading_derived_category ahc ON aht.id = ahc.id
        LEFT JOIN (SELECT trans_id, description
                     FROM account_translation
                    WHERE language_code =
                           coalesce($4,
                             (SELECT up.language
                                FROM user_preference up
                          INNER JOIN users ON up.id = users.id
                               WHERE users.username = SESSION_USER))) at
                  ON aht.id = at.trans_id
        WHERE ((SELECT value::int FROM defaults
                                  WHERE setting_key = 'earn_id') IS NOT NULL
               AND array_splice_from((SELECT value::int FROM defaults
                                  WHERE setting_key = 'earn_id'),aht.path)
                               IS NOT NULL)
              -- legacy: earn_id not configured; select headings belonging to
              --    selected accounts
              OR ((SELECT value::int FROM defaults
                                  WHERE setting_key = 'earn_id') IS NULL
                  AND EXISTS (SELECT 1 FROM acc_meta
                                      WHERE aht.id = ANY(acc_meta.path)))
    ),
    acc_balance AS (
    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 ac.chart_id AS id, sum(ac.amount) AS balance
      FROM acc_trans ac
      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)
     GROUP BY ac.chart_id
       HAVING sum(ac.amount) <> 0.00
     ),
    hdr_balance AS (
       select ahd.id, sum(balance) as balance
         FROM acc_balance ab
        INNER JOIN account acc ON ab.id = acc.id
        INNER JOIN account_heading_descendant ahd
                ON acc.heading = ahd.descendant_id
        GROUP BY ahd.id
    )
       SELECT hm.id, hm.accno, hm.description, hm.account_type, hm.category,
              null::text as gifi, null::text as gifi_description, hm.contra,
              hb.balance, hm.path
         FROM hdr_meta hm
        INNER JOIN hdr_balance hb ON hm.id = hb.id
       UNION
       SELECT am.id, am.accno, am.description, am.account_type, am.category,
              gifi_accno as gifi, gifi_description, am.contra, ab.balance, am.path
         FROM acc_meta am
        INNER JOIN acc_balance ab on am.id = ab.id

### pnl__income_statement_accrual(in_from_date date, in_to_date date, in_ignore_yearend text, in_business_units integer[], in_language text)

pnl__income_statement_accrual(in_from_date date, in_to_date date, in_ignore_yearend text, in_business_units integer[], in_language text)

Function Properties

Language Return Type
- SQL SET OF pnl_line

    WITH acc_meta AS (
      SELECT a.id, a.accno,
             coalesce(at.description, a.description) as description,
             CASE WHEN (SELECT value::int FROM defaults where setting_key = 'earn_id') IS NULL THEN aht.path
             ELSE array_splice_from((SELECT value::int FROM defaults
                                 WHERE setting_key = 'earn_id'),aht.path)
             END AS path,
             a.category, 'A'::char as account_type, contra, a.gifi_accno,
             gifi.description as gifi_description
         FROM account a
        INNER JOIN account_heading_tree aht on a.heading = aht.id
         LEFT JOIN gifi ON a.gifi_accno = gifi.accno
         LEFT JOIN (SELECT trans_id, description
                      FROM account_translation
                     WHERE language_code =
                            coalesce($5,
                              (SELECT up.language
                                 FROM user_preference up
                           INNER JOIN users ON up.id = users.id
                                WHERE users.username = SESSION_USER))) at
                   ON a.id = at.trans_id
       WHERE array_splice_from((SELECT value::int FROM defaults
                                 WHERE setting_key = 'earn_id'),aht.path)
                              IS NOT NULL
             -- legacy: earn_id not configured (yet)
             OR (NOT EXISTS (SELECT 1 FROM defaults
                             WHERE setting_key = 'earn_id'
                                   AND value IS NOT NULL)
                 AND category IN ('E', 'I'))
    ),
    hdr_meta AS (
       SELECT aht.id, aht.accno,
              coalesce(at.description, aht.description) as description,
              CASE WHEN (SELECT value::int FROM defaults where setting_key = 'earn_id') IS NULL THEN aht.path
              ELSE array_splice_from((SELECT value::int FROM defaults
                                  WHERE setting_key = 'earn_id'),aht.path)
              END AS path,
              ahc.derived_category as category,
              'H'::char as account_type, 'f'::boolean as contra
         FROM account_heading_tree aht
        INNER JOIN account_heading_derived_category ahc ON aht.id = ahc.id
        LEFT JOIN (SELECT trans_id, description
                     FROM account_translation
                    WHERE language_code =
                           coalesce($5,
                             (SELECT up.language
                                FROM user_preference up
                          INNER JOIN users ON up.id = users.id
                               WHERE users.username = SESSION_USER))) at
                  ON aht.id = at.trans_id
        WHERE ((SELECT value::int FROM defaults
                                  WHERE setting_key = 'earn_id') IS NOT NULL
               AND array_splice_from((SELECT value::int FROM defaults
                                  WHERE setting_key = 'earn_id'),aht.path)
                               IS NOT NULL)
              -- legacy: earn_id not configured; select headings belonging to
              --    selected accounts
              OR ((SELECT value::int FROM defaults
                                  WHERE setting_key = 'earn_id') IS NULL
                  AND EXISTS (SELECT 1 FROM acc_meta
                                      WHERE aht.id = ANY(acc_meta.path)))
    ),
    acc_balance AS (
       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 ac.chart_id AS id, sum(ac.amount) AS balance
         FROM acc_trans ac
        INNER JOIN tx_report gl ON ac.trans_id = gl.id AND gl.approved
         LEFT JOIN (SELECT array_agg(path) AS bu_ids, entry_id
                      FROM business_unit_ac buac
                     INNER 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
              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 ($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 ac.chart_id
         HAVING sum(ac.amount) <> 0.00
     ),
    hdr_balance AS (
       select ahd.id, sum(balance) as balance
         FROM acc_balance ab
        INNER JOIN account acc ON ab.id = acc.id
        INNER JOIN account_heading_descendant ahd
                ON acc.heading = ahd.descendant_id
        GROUP BY ahd.id
    )
       SELECT hm.id, hm.accno, hm.description, hm.account_type, hm.category,
              null::text as gifi, null::text as gifi_description, hm.contra,
              hb.balance, hm.path
         FROM hdr_meta hm
        INNER JOIN hdr_balance hb ON hm.id = hb.id
       UNION
       SELECT am.id, am.accno, am.description, am.account_type, am.category,
              gifi_accno as gifi, gifi_description, am.contra, ab.balance, am.path
         FROM acc_meta am
        INNER JOIN acc_balance ab on am.id = ab.id

### pnl__income_statement_cash(in_from_date date, in_to_date date, in_ignore_yearend text, in_business_units integer[], in_language text)

pnl__income_statement_cash(in_from_date date, in_to_date date, in_ignore_yearend text, in_business_units integer[], in_language text)

Function Properties

Language Return Type
- SQL SET OF pnl_line

    WITH acc_meta AS (
      SELECT a.id, a.accno,
             coalesce(at.description, a.description) as description,
             CASE WHEN (SELECT value::int FROM defaults where setting_key = 'earn_id') IS NULL THEN aht.path
             ELSE array_splice_from((SELECT value::int FROM defaults
                                 WHERE setting_key = 'earn_id'),aht.path)
             END AS path,
             a.category, 'A'::char as account_type, contra, a.gifi_accno,
             gifi.description as gifi_description
         FROM account a
        INNER JOIN account_heading_tree aht on a.heading = aht.id
         LEFT JOIN gifi ON a.gifi_accno = gifi.accno
         LEFT JOIN (SELECT trans_id, description
                      FROM account_translation
                     WHERE language_code =
                            coalesce($5,
                              (SELECT up.language
                                 FROM user_preference up
                           INNER JOIN users ON up.id = users.id
                                WHERE users.username = SESSION_USER))) at
                   ON a.id = at.trans_id
       WHERE array_splice_from((SELECT value::int FROM defaults
                                 WHERE setting_key = 'earn_id'),aht.path)
                              IS NOT NULL
             -- legacy: earn_id not configured (yet)
             OR (NOT EXISTS (SELECT 1 FROM defaults
                             WHERE setting_key = 'earn_id'
                               AND value IS NOT NULL)
                 AND category IN ('E', 'I'))
    ),
    hdr_meta AS (
       SELECT aht.id, aht.accno,
              coalesce(at.description, aht.description) as description,
              CASE WHEN (SELECT value::int FROM defaults where setting_key = 'earn_id') IS NULL THEN aht.path
              ELSE array_splice_from((SELECT value::int FROM defaults
                                  WHERE setting_key = 'earn_id'),aht.path)
              END AS path,
              ahc.derived_category as category,
              'H'::char as account_type, 'f'::boolean as contra
         FROM account_heading_tree aht
        INNER JOIN account_heading_derived_category ahc ON aht.id = ahc.id
        LEFT JOIN (SELECT trans_id, description
                     FROM account_translation
                    WHERE language_code =
                           coalesce($5,
                             (SELECT up.language
                                FROM user_preference up
                          INNER JOIN users ON up.id = users.id
                               WHERE users.username = SESSION_USER))) at
                  ON aht.id = at.trans_id
        WHERE ((SELECT value::int FROM defaults
                                  WHERE setting_key = 'earn_id') IS NOT NULL
               AND array_splice_from((SELECT value::int FROM defaults
                                  WHERE setting_key = 'earn_id'),aht.path)
                               IS NOT NULL)
              -- legacy: earn_id not configured; select headings belonging to
              --    selected accounts
              OR ((SELECT value::int FROM defaults
                                  WHERE setting_key = 'earn_id') IS NULL
                  AND EXISTS (SELECT 1 FROM acc_meta
                                      WHERE aht.id = ANY(acc_meta.path)))
    ),
    acc_balance AS (
    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 ac.chart_id AS id, sum(ac.amount * ca.portion) AS balance
         FROM acc_trans ac
         JOIN tx_report gl ON ac.trans_id = gl.id AND gl.approved
         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
              AND ($4 = '{}'
                  OR $4 is null or in_tree($4, bu_ids))
              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 ac.chart_id
       HAVING sum(ac.amount * ca.portion) <> 0.00
     ),
    hdr_balance AS (
       select ahd.id, sum(balance) as balance
         FROM acc_balance ab
        INNER JOIN account acc ON ab.id = acc.id
        INNER JOIN account_heading_descendant ahd
                ON acc.heading = ahd.descendant_id
        GROUP BY ahd.id
    )
       SELECT hm.id, hm.accno, hm.description, hm.account_type, hm.category,
              null::text as gifi, null::text as gifi_description, hm.contra,
              hb.balance, hm.path
         FROM hdr_meta hm
        INNER JOIN hdr_balance hb ON hm.id = hb.id
       UNION
       SELECT am.id, am.accno, am.description, am.account_type, am.category,
              gifi_accno as gifi, gifi_description, am.contra, ab.balance, am.path
         FROM acc_meta am
        INNER JOIN acc_balance ab on am.id = ab.id

### pnl__invoice(in_id integer, in_language text)

pnl__invoice(in_id integer, in_language text)

Function Properties

Language Return Type
- SQL SET OF pnl_line

    WITH acc_meta AS (
      SELECT a.id, a.accno,
             coalesce(at.description, a.description) as description,
             CASE WHEN (SELECT value::int FROM defaults where setting_key = 'earn_id') IS NULL THEN aht.path
             ELSE array_splice_from((SELECT value::int FROM defaults
                                 WHERE setting_key = 'earn_id'),aht.path)
             END AS path,
             a.category, 'A'::char as account_type, contra, a.gifi_accno,
             gifi.description as gifi_description
         FROM account a
        INNER JOIN account_heading_tree aht on a.heading = aht.id
         LEFT JOIN gifi ON a.gifi_accno = gifi.accno
         LEFT JOIN (SELECT trans_id, description
                      FROM account_translation
                     WHERE language_code =
                            coalesce($2,
                              (SELECT up.language
                                 FROM user_preference up
                           INNER JOIN users ON up.id = users.id
                                WHERE users.username = SESSION_USER))) at
                   ON a.id = at.trans_id
       WHERE array_splice_from((SELECT value::int FROM defaults
                                 WHERE setting_key = 'earn_id'),aht.path)
                              IS NOT NULL
             -- legacy: earn_id not configured (yet)
             OR (NOT EXISTS (SELECT 1 FROM defaults
                             WHERE setting_key = 'earn_id'
                               AND value IS NOT NULL)
                 AND category IN ('E', 'I'))
    ),
    hdr_meta AS (
       SELECT aht.id, aht.accno,
              coalesce(at.description, aht.description) as description,
              CASE WHEN (SELECT value::int FROM defaults where setting_key = 'earn_id') IS NULL THEN aht.path
              ELSE array_splice_from((SELECT value::int FROM defaults
                                  WHERE setting_key = 'earn_id'),aht.path)
              END AS path,
              ahc.derived_category as category,
              'H'::char as account_type, 'f'::boolean as contra
         FROM account_heading_tree aht
        INNER JOIN account_heading_derived_category ahc ON aht.id = ahc.id
        LEFT JOIN (SELECT trans_id, description
                     FROM account_translation
                    WHERE language_code =
                           coalesce($2,
                             (SELECT up.language
                                FROM user_preference up
                          INNER JOIN users ON up.id = users.id
                               WHERE users.username = SESSION_USER))) at
                  ON aht.id = at.trans_id
        WHERE ((SELECT value::int FROM defaults
                                  WHERE setting_key = 'earn_id') IS NOT NULL
               AND array_splice_from((SELECT value::int FROM defaults
                                  WHERE setting_key = 'earn_id'),aht.path)
                               IS NOT NULL)
              -- legacy: earn_id not configured; select headings belonging to
              --    selected accounts
              OR ((SELECT value::int FROM defaults
                                  WHERE setting_key = 'earn_id') IS NULL
                  AND EXISTS (SELECT 1 FROM acc_meta
                                      WHERE aht.id = ANY(acc_meta.path)))
    ),
    acc_balance AS (
    SELECT ac.chart_id AS id, sum(ac.amount) AS balance
      FROM acc_trans ac
     WHERE ac.approved AND ac.trans_id = $1
     GROUP BY ac.chart_id
     ),
    hdr_balance AS (
       select ahd.id, sum(balance) as balance
         FROM acc_balance ab
        INNER JOIN account acc ON ab.id = acc.id
        INNER JOIN account_heading_descendant ahd
                ON acc.heading = ahd.descendant_id
        GROUP BY ahd.id
    )
       SELECT hm.id, hm.accno, hm.description, hm.account_type, hm.category,
              null::text as gifi, null::text as gifi_description, hm.contra,
              hb.balance, hm.path
         FROM hdr_meta hm
        INNER JOIN hdr_balance hb ON hm.id = hb.id
       UNION
       SELECT am.id, am.accno, am.description, am.account_type, am.category,
              gifi_accno as gifi, gifi_description, am.contra, ab.balance, am.path
         FROM acc_meta am
        INNER JOIN acc_balance ab on am.id = ab.id

### pnl__product(in_from_date date, in_to_date date, in_parts_id integer, in_business_units integer[], in_language text)

pnl__product(in_from_date date, in_to_date date, in_parts_id integer, in_business_units integer[], in_language text)

Function Properties

Language Return Type
- SQL SET OF pnl_line

    WITH acc_meta AS (
      SELECT a.id, a.accno,
             coalesce(at.description, a.description) as description,
             CASE WHEN (SELECT value::int FROM defaults where setting_key = 'earn_id') IS NULL THEN aht.path
             ELSE array_splice_from((SELECT value::int FROM defaults
                                 WHERE setting_key = 'earn_id'),aht.path)
             END AS path,
             a.category, 'A'::char as account_type, contra, a.gifi_accno,
             gifi.description as gifi_description
         FROM account a
        INNER JOIN account_heading_tree aht on a.heading = aht.id
         LEFT JOIN gifi ON a.gifi_accno = gifi.accno
         LEFT JOIN (SELECT trans_id, description
                      FROM account_translation
                     WHERE language_code =
                            coalesce($5,
                              (SELECT up.language
                                 FROM user_preference up
                           INNER JOIN users ON up.id = users.id
                                WHERE users.username = SESSION_USER))) at
                   ON a.id = at.trans_id
       WHERE array_splice_from((SELECT value::int FROM defaults
                                 WHERE setting_key = 'earn_id'),aht.path)
                              IS NOT NULL
             -- legacy: earn_id not configured (yet)
             OR (NOT EXISTS (SELECT 1 FROM defaults
                             WHERE setting_key = 'earn_id'
                               AND value IS NOT NULL)
                 AND category IN ('E', 'I'))
    ),
    hdr_meta AS (
       SELECT aht.id, aht.accno,
              coalesce(at.description, aht.description) as description,
              CASE WHEN (SELECT value::int FROM defaults where setting_key = 'earn_id') IS NULL THEN aht.path
              ELSE array_splice_from((SELECT value::int FROM defaults
                                  WHERE setting_key = 'earn_id'),aht.path)
              END AS path,
              ahc.derived_category as category,
              'H'::char as account_type, 'f'::boolean as contra
         FROM account_heading_tree aht
        INNER JOIN account_heading_derived_category ahc ON aht.id = ahc.id
        LEFT JOIN (SELECT trans_id, description
                     FROM account_translation
                    WHERE language_code =
                           coalesce($5,
                             (SELECT up.language
                                FROM user_preference up
                          INNER JOIN users ON up.id = users.id
                               WHERE users.username = SESSION_USER))) at
                  ON aht.id = at.trans_id
        WHERE ((SELECT value::int FROM defaults
                                  WHERE setting_key = 'earn_id') IS NOT NULL
               AND array_splice_from((SELECT value::int FROM defaults
                                  WHERE setting_key = 'earn_id'),aht.path)
                               IS NOT NULL)
              -- legacy: earn_id not configured; select headings belonging to
              --    selected accounts
              OR ((SELECT value::int FROM defaults
                                  WHERE setting_key = 'earn_id') IS NULL
                  AND EXISTS (SELECT 1 FROM acc_meta
                                      WHERE aht.id = ANY(acc_meta.path)))
    ),
    acc_balance AS (
       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 ac.chart_id AS id, sum(ac.amount) AS balance
         FROM acc_trans ac
         JOIN invoice i ON i.id = ac.invoice_id
         JOIN account_link l ON l.account_id = ac.chart_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 ac.chart_id
       HAVING sum(ac.amount) <> 0.00
        UNION
       SELECT ac.chart_id,
              sum(i.sellprice * i.qty * (1 - coalesce(i.discount, 0)))
         FROM invoice i
         JOIN acc_trans ac ON ac.invoice_id = i.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 ($4 is null or $4 = '{}' OR in_tree($4, bu_ids))
     GROUP BY ac.chart_id
       HAVING sum(i.sellprice * i.qty * (1 - coalesce(i.discount, 0))) <> 0.00
     ),
    hdr_balance AS (
       select ahd.id, sum(balance) as balance
         FROM acc_balance ab
        INNER JOIN account acc ON ab.id = acc.id
        INNER JOIN account_heading_descendant ahd
                ON acc.heading = ahd.descendant_id
        GROUP BY ahd.id
    )
       SELECT hm.id, hm.accno, hm.description, hm.account_type, hm.category,
              null::text as gifi, null::text as gifi_description, hm.contra,
              hb.balance, hm.path
         FROM hdr_meta hm
        INNER JOIN hdr_balance hb ON hm.id = hb.id
       UNION
       SELECT am.id, am.accno, am.description, am.account_type, am.category,
              gifi_accno as gifi, gifi_description, am.contra, ab.balance, am.path
         FROM acc_meta am
        INNER JOIN acc_balance ab on am.id = ab.id

### prevent_closed_transactions()

prevent_closed_transactions()

Function Properties

Language Return Type
- PLPGSQL trigger

    DECLARE t_end_date date;
    BEGIN
    SELECT max(end_date) into t_end_date FROM account_checkpoint;
    IF new.transdate <= t_end_date THEN
        RAISE EXCEPTION 'Transaction entered into closed period.  Transdate: %',
                       new.transdate;
    END IF;
    RETURN new;
    END;

### pricegroup__list()

pricegroup__list()

Function Properties

Language Return Type
- SQL SET OF pricegroup

Returns an alphabetically ordered pricegroup list.

    SELECT * FROM pricegroup ORDER BY pricegroup;

### pricegroup__search(in_pricegroup text)

pricegroup__search(in_pricegroup text)

Function Properties

Language Return Type
- SQL SET OF pricegroup

      SELECT * FROM pricegroup
       WHERE $1 IS NULL OR pricegroup ilike $1 || '%'
    ORDER BY pricegroup;

### pricegroups__list()

pricegroups__list()

Function Properties

Language Return Type
- SQL SET OF pricegroup

    SELECT * FROM pricegroup;

### pricelist__delete(in_entry_id integer, in_credit_id integer)

pricelist__delete(in_entry_id integer, in_credit_id integer)

Function Properties

Language Return Type
- SQL boolean

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

### pricelist__save(in_parts_id integer, in_credit_id integer, in_pricebreak numeric, in_price numeric, in_lead_time smallint, in_partnumber text, in_validfrom date, in_validto date, in_curr bpchar, in_entry_id integer, in_qty numeric)

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

Function Properties

Language Return Type
- PLPGSQL eca__pricematrix

    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,
               qty        = in_qty,
               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, qty)
            VALUES (in_parts_id, in_credit_id, in_price, in_validfrom, in_validto,
                    in_curr, qty);

            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, qty
          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;

### pricematrix__for_customer(in_credit_id integer, in_parts_id integer, in_transdate date, in_qty numeric, in_currency text)

pricematrix__for_customer(in_credit_id integer, in_parts_id integer, in_transdate date, in_qty numeric, in_currency text)

Function Properties

Language Return Type
- SQL SET OF partscustomer

       SELECT p.*
         FROM partscustomer p
         JOIN entity_credit_account eca ON eca.id = in_credit_id
    LEFT JOIN pricegroup pg ON eca.pricegroup_id = pg.id
        WHERE p.parts_id = in_parts_id
            AND coalesce(p.validfrom, in_transdate) <=
                in_transdate
            AND coalesce(p.validto, in_transdate) >=
                in_transdate
            AND (p.credit_id = eca.id OR p.pricegroup_id = pg.id
                 OR (p.credit_id is null and p.pricegroup_id is null))
            AND coalesce(qty, 0) <= coalesce(in_qty, 0)
            AND coalesce(p.curr, defaults_get_defaultcurrency()) =
                coalesce(in_currency, defaults_get_defaultcurrency())
      ORDER BY case WHEN p.credit_id = eca.id THEN 1
                    WHEN p.pricegroup_id = pg.id THEN 2
                    ELSE 3
                end asc, qty desc;

### pricematrix__for_vendor(in_credit_id integer, in_parts_id integer)

pricematrix__for_vendor(in_credit_id integer, in_parts_id integer)

Function Properties

Language Return Type
- SQL SET OF partsvendor

    SELECT *
      FROM partsvendor
     WHERE parts_id = in_parts_id
           AND credit_id = in_credit_id;

### product(numeric)

product(numeric)

Function Properties

Language Return Type
- INTERNAL numeric

    aggregate_dummy

### product(numeric, numeric)

product(numeric, numeric)

Function Properties

Language Return Type
- SQL numeric

    SELECT $1 * $2;

### quote_ident_array(text[])

quote_ident_array(text[])

Function Properties

Language Return Type
- SQL text[]

       SELECT array_agg(quote_ident(e))
         FROM unnest($1) e;

### reconciliation__account_list()

reconciliation__account_list()

Function Properties

Language Return Type
- SQL SET OF recon_accounts

returns set of accounts set up for reconciliation. Currently we pull the account number and description from the account table.

        SELECT DISTINCT
            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;

### reconciliation__add_entry(in_report_id integer, in_scn text, in_type text, in_date timestamp without time zone, in_amount numeric)

reconciliation__add_entry(in_report_id integer, in_scn text, in_type text, in_date timestamp without time zone, in_amount numeric)

Function Properties

Language Return Type
- PLPGSQL integer

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.... It returns the ID of the inserted/updated entry

        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 t_uid IS NULL THEN
                    t_uid = robot__get_my_entity_id();
            END IF;
            IF in_scn = '' THEN
                    t_scn := NULL;
            ELSIF in_scn !~ '^[0-9]+$' THEN
                    t_scn := in_scn;
            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 AND post_date = in_date;

                    IF in_count = 0 THEN
                            -- YLA - Where does our_balance comes from?
                            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)
                            RETURNING id INTO lid;
                    ELSIF in_count = 1 THEN
                            SELECT id INTO lid
                            WHERE t_scn = scn AND report_id = in_report_id
                                    AND their_balance = 0 AND post_date = in_date;
                            UPDATE cr_report_line
                            SET their_balance = t_amount, clear_time = in_date,
                                    cleared = true
                            WHERE id = lid;
                    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
                                    AND post_date = in_date;

                            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
                                          AND post_date = in_date
                                    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
                                    SELECT id INTO lid FROM cr_report_line
                                    WHERE t_scn = scn AND report_id = in_report_id
                                            AND our_value = t_amount
                                            AND their_balance = 0
                                            AND post_date = in_date;
                                    UPDATE cr_report_line
                                    SET their_balance = t_amount,
                                            trans_type = in_type,
                                            clear_time = in_date,
                                            cleared = true
                                    WHERE id = lid;
                            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
                                            AND post_date = in_date
                                    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)
                            RETURNING id INTO lid;
                    ELSIF in_count = 1 THEN -- perfect match
                            SELECT id INTO lid
                            WHERE report_id = in_report_id
                                    AND our_balance = t_amount
                                    AND their_balance = 0
                                    AND post_date = in_date
                                    AND in_scn NOT LIKE t_prefix || '%';
                            UPDATE cr_report_line SET their_balance = t_amount,
                                            trans_type = in_type,
                                            clear_time = in_date,
                                            cleared = true
                            WHERE id = lid;
                    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 lid;

        END;

### reconciliation__check(in_end_date date, in_chart_id integer)

reconciliation__check(in_end_date date, in_chart_id integer)

Function Properties

Language Return Type
- SQL SET OF defaults

    WITH unapproved_tx as (
         SELECT 'unapproved_transactions'::text, count(*)::text
           FROM (SELECT          id::text FROM ar        WHERE approved IS FALSE AND transdate < $1
          UNION  SELECT          id::text FROM ap        WHERE approved IS FALSE AND transdate < $1
          UNION  SELECT          id::text FROM gl        WHERE approved IS FALSE AND transdate < $1
          UNION  SELECT DISTINCT source   FROM acc_trans WHERE approved IS FALSE AND transdate < $1 AND chart_id = $2
                ) tx
    ),
         unapproved_cr as (
         SELECT 'unapproved_reports'::text, count(*)::text
           FROM cr_report
          WHERE end_date < $1 AND approved IS NOT TRUE AND chart_id = $2
    )
    SELECT * FROM unapproved_tx
    UNION SELECT * FROM unapproved_cr;

### reconciliation__delete_my_report(in_report_id integer)

reconciliation__delete_my_report(in_report_id integer)

Function Properties

Language Return Type
- SQL boolean

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.

        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
        RETURNING TRUE;

### reconciliation__delete_unapproved(in_report_id integer)

reconciliation__delete_unapproved(in_report_id integer)

Function Properties

Language Return Type
- SQL boolean

This function deletes any specified unapproved transaction.

        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
        RETURNING TRUE;

### reconciliation__get_cleared_balance(in_chart_id integer, in_report_date date)

reconciliation__get_cleared_balance(in_chart_id integer, in_report_date date)

Function Properties

Language Return Type
- SQL numeric

Gets the cleared balance of the account specified by chart_id, as of in_report_date. 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 sum(ac.amount) * CASE WHEN c.category in('A', 'E') THEN -1 ELSE 1 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 cleared
          AND ac.approved IS true
                    GROUP BY c.id, c.category;

### reconciliation__get_current_balance(in_account_id integer, in_date date)

reconciliation__get_current_balance(in_account_id integer, in_date date)

Function Properties

Language Return Type
- SQL numeric

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.

            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;

### reconciliation__new_report_id(in_chart_id integer, in_total numeric, in_end_date date, in_recon_fx boolean)

reconciliation__new_report_id(in_chart_id integer, in_total numeric, in_end_date date, in_recon_fx boolean)

Function Properties

Language Return Type
- SQL integer

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;

### reconciliation__pending_transactions(in_report_id integer, in_their_total numeric)

reconciliation__pending_transactions(in_report_id integer, in_their_total numeric)

Function Properties

Language Return Type
- PLPGSQL integer

Ensures that the list of pending transactions in the report is up to date.

        DECLARE
            gl_row RECORD;
            t_recon_fx BOOL;
            t_chart_id integer;
            t_end_date date;
        BEGIN
           SELECT end_date, recon_fx, chart_id
             INTO t_end_date, t_recon_fx, t_chart_id
             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 cr_report r ON r.id = in_report_id
            LEFT JOIN exchangerate ex ON gl.transdate = ex.transdate
            WHERE ac.cleared IS FALSE
                    AND ac.approved IS TRUE
                    AND ac.chart_id = t_chart_id
                    AND ac.transdate <= t_end_date
                    AND (t_recon_fx is not true
                         OR (t_recon_fx is true
                             AND (gl.table <> 'gl'
                                  OR ac.fx_transaction IS TRUE)))
                    AND (ac.entry_id > coalesce(r.max_ac_id, 0))
            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 = date_trunc('second', now()),
                    their_total = coalesce(in_their_total, their_total),
                    max_ac_id = (select max(entry_id) from acc_trans)
            where id = in_report_id;

        RETURN in_report_id;
        END;

### reconciliation__reject_set(in_report_id integer)

reconciliation__reject_set(in_report_id integer)

Function Properties

Language Return Type
- SQL boolean

         UPDATE cr_report set submitted = false
          WHERE id = in_report_id
                AND approved is not true
         RETURNING true;

### reconciliation__report_approve(in_report_id integer)

reconciliation__report_approve(in_report_id integer)

Function Properties

Language Return Type
- PLPGSQL integer

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;
            IF NOT FOUND THEN
                RAISE EXCEPTION 'No report at %.', $1;
            END IF;

            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;

### reconciliation__report_details(in_report_id integer)

reconciliation__report_details(in_report_id integer)

Function Properties

Language Return Type
- SQL SET OF cr_report_line

Returns the details of the report.

                    select * from cr_report_line where report_id = in_report_id
                    order by scn, post_date

### reconciliation__report_details_payee(in_report_id integer)

reconciliation__report_details_payee(in_report_id integer)

Function Properties

Language Return Type
- SQL SET OF recon_payee

                    select * from recon_payee where report_id = in_report_id
                    order by scn, post_date

### reconciliation__report_details_payee_with_days(in_report_id integer, in_end_date date)

reconciliation__report_details_payee_with_days(in_report_id integer, in_end_date date)

Function Properties

Language Return Type
- PLPGSQL SET OF recon_payee_days

Pulls the payee information for the reconciliation report.

    BEGIN
                RETURN QUERY
                    SELECT rp.id,
                            CASE WHEN in_end_date IS NULL THEN NULL
                            ELSE      in_end_date - clear_time
                            END AS d
                    FROM recon_payee rp
                    WHERE rp.report_id = in_report_id;

    RETURN;
    END;

### reconciliation__report_summary(in_report_id integer)

reconciliation__report_summary(in_report_id integer)

Function Properties

Language Return Type
- SQL cr_report

            select * from cr_report where id = in_report_id;

### reconciliation__save_set(in_report_id integer, in_line_ids integer[])

reconciliation__save_set(in_report_id integer, in_line_ids integer[])

Function Properties

Language Return Type
- SQL boolean

Sets which lines of the report are cleared.

            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)
            RETURNING TRUE;

### reconciliation__search(in_date_from date, in_date_to date, in_balance_from numeric, in_balance_to numeric, in_account_id integer, in_submitted boolean, in_approved boolean)

reconciliation__search(in_date_from date, in_date_to date, in_balance_from numeric, in_balance_to numeric, in_account_id integer, in_submitted boolean, in_approved boolean)

Function Properties

Language Return Type
- SQL SET OF cr_report

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.

                    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_account_id IS NULL OR in_account_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

### reconciliation__submit_set(in_report_id integer, in_line_ids integer[])

reconciliation__submit_set(in_report_id integer, in_line_ids integer[])

Function Properties

Language Return Type
- PLPGSQL boolean

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;

### report__aa_outstanding(in_entity_class integer, in_account_id integer, in_entity_name text, in_meta_number text, in_employee_id integer, in_business_units integer[], in_ship_via text, in_on_hold boolean, in_from_date date, in_to_date date, in_partnumber text, in_parts_id integer)

report__aa_outstanding(in_entity_class integer, in_account_id integer, in_entity_name text, in_meta_number text, in_employee_id integer, in_business_units integer[], in_ship_via text, in_on_hold boolean, in_from_date date, in_to_date date, in_partnumber text, in_parts_id integer)

Function Properties

Language Return Type
- SQL SET OF aa_transactions_line

    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,
        $11, $12)
     GROUP BY meta_number, entity_name, entity_id;

### report__aa_outstanding_details(in_entity_class integer, in_account_id integer, in_entity_name text, in_meta_number text, in_employee_id integer, in_business_units integer[], in_ship_via text, in_on_hold boolean, in_from_date date, in_to_date date, in_partnumber text, in_parts_id integer)

report__aa_outstanding_details(in_entity_class integer, in_account_id integer, in_entity_name text, in_meta_number text, in_employee_id integer, in_business_units integer[], in_ship_via text, in_on_hold boolean, in_from_date date, in_to_date date, in_partnumber text, in_parts_id integer)

Function Properties

Language Return Type
- SQL SET OF aa_transactions_line

    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 as paid, p.due, p.last_payment, a.duedate, a.notes,
           a.till, ee.name, me.name, a.shippingpoint, a.shipvia,
           '{}'::text[] 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, force_closed
              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, force_closed
              FROM ap
             WHERE in_entity_class = 1 and approved) a
      LEFT
      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_to_date is null or transdate <= in_to_date)
          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
      LEFT
      JOIN entity_employee ON entity_employee.entity_id = a.person_id
      LEFT
      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)
               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 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_from_date IS NULL OR a.transdate >= in_from_date)
           AND (in_to_date IS NULL OR a.transdate <= in_to_date)
           AND p.due::numeric(100,2) <> 0
           AND a.force_closed IS NOT TRUE
           AND (in_partnumber IS NULL
              OR EXISTS(SELECT 1 FROM invoice inv
                          JOIN parts ON inv.parts_id = parts.id
                         WHERE inv.trans_id = a.id))
           AND (in_parts_id IS NULL
              OR EXISTS (select 1 FROM invoice
                          WHERE parts_id = in_parts_id AND trans_id = a.id))

### report__aa_transactions(in_entity_class integer, in_account_id integer, in_entity_name text, in_meta_number text, in_employee_id integer, in_manager_id integer, in_invnumber text, in_ordnumber text, in_ponumber text, in_source text, in_description text, in_notes text, in_shipvia text, in_from_date date, in_to_date date, in_on_hold boolean, in_taxable boolean, in_tax_account_id integer, in_open boolean, in_closed boolean, in_approved boolean)

report__aa_transactions(in_entity_class integer, in_account_id integer, in_entity_name text, in_meta_number text, in_employee_id integer, in_manager_id integer, in_invnumber text, in_ordnumber text, in_ponumber text, in_source text, in_description text, in_notes text, in_shipvia text, in_from_date date, in_to_date date, in_on_hold boolean, in_taxable boolean, in_tax_account_id integer, in_open boolean, in_closed boolean, in_approved boolean)

Function Properties

Language Return Type
- SQL SET OF aa_transactions_line

    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, '{}'::text[]

      FROM (select id, transdate, invnumber, amount, netamount, duedate, notes,
                   till, person_id, entity_credit_account, invoice, shippingpoint,
                   shipvia, ordnumber, ponumber, description, on_hold, force_closed
              FROM ar
             WHERE in_entity_class = 2
                   and (in_approved is null or (in_approved = approved))
             UNION
            SELECT id, transdate, invnumber, amount, netamount, duedate, notes,
                   null, person_id, entity_credit_account, invoice, shippingpoint,
                   shipvia, ordnumber, ponumber, description, on_hold, force_closed
              FROM ap
             WHERE in_entity_class = 1
                   and (in_approved is null or (in_approved = approved))) a
      LEFT
      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
      LEFT
      JOIN entity_employee ee ON ee.entity_id = a.person_id
      LEFT
      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 || '%'
               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 = 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_from_date IS NULL OR a.transdate >= in_from_date)
           AND (in_to_date IS NULL OR a.transdate <= in_to_date)
           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'))
                )
                AND ( -- open/closed handling
                  (in_open IS TRUE AND ( a.force_closed IS NOT TRUE AND
                     abs(p.due) > 0.005))                  -- threshold due to
                                                           -- impossibility to
                                                           -- collect below -CT
                   OR (in_closed IS TRUE AND ( a.force_closed IS NOT TRUE AND
                     abs(p.due) > 0.005) IS NOT TRUE)
                )

### report__balance_sheet(in_to_date date, in_language text)

report__balance_sheet(in_to_date date, in_language text)

Function Properties

Language Return Type
- SQL SET OF balance_sheet_line

This produces a balance sheet and the paths (acount numbers) of all headings necessary; output is generated in the language requested, or in the users default language if not available.

    WITH hdr_meta AS (
       SELECT aht.id, aht.accno, coalesce(at.description, aht.description) as description,
              aht.path,
              ahc.derived_category as category, 'H'::char as account_type,
              'f'::boolean as contra
         FROM account_heading_tree aht
        INNER JOIN account_heading_derived_category ahc ON aht.id = ahc.id
        LEFT JOIN (SELECT trans_id, description
                     FROM account_translation
                    WHERE language_code =
                           coalesce($2,
                             (SELECT up.language
                                FROM user_preference up
                          INNER JOIN users ON up.id = users.id
                               WHERE users.username = SESSION_USER))) at
                  ON aht.id = at.trans_id
         WHERE array_endswith((SELECT value::int FROM defaults
                                WHERE setting_key = 'earn_id'), aht.path)
               -- legacy (no earn_id) returns all headers
               OR (NOT aht.path @> ARRAY[(SELECT value::int FROM defaults
                                          WHERE setting_key = 'earn_id')])
    ),
    acc_meta AS (
      SELECT a.id, a.accno, coalesce(at.description, a.description) as description,
             aht.path, a.category, 'A'::char as account_type, contra,
             a.gifi_accno, gifi.description as gifi_description
         FROM account a
        INNER JOIN account_heading_tree aht on a.heading = aht.id
         LEFT JOIN gifi ON a.gifi_accno = gifi.accno
         LEFT JOIN (SELECT trans_id, description
                      FROM account_translation
                     WHERE language_code =
                            coalesce($2,
                              (SELECT up.language
                                 FROM user_preference up
                           INNER JOIN users ON up.id = users.id
                                WHERE users.username = SESSION_USER))) at
                   ON a.id = at.trans_id
         WHERE array_endswith((SELECT value::int FROM defaults
                                WHERE setting_key = 'earn_id'), aht.path)
               -- legacy (no earn_id) returns all accounts; bug?
               OR (NOT aht.path @> ARRAY[(SELECT value::int FROM defaults
                                          WHERE setting_key = 'earn_id')])
    ),
    acc_balance AS (
       SELECT ac.chart_id as id, sum(ac.amount) as balance
         FROM acc_trans ac
         JOIN tx_report t ON t.approved AND t.id = ac.trans_id
        WHERE ac.transdate <= coalesce($1, (select max(transdate) from acc_trans))
     GROUP BY ac.chart_id
       HAVING sum(ac.amount) <> 0.00
    ),
    hdr_balance AS (
       select ahd.id, sum(balance) as balance
         FROM acc_balance ab
        INNER JOIN account acc ON ab.id = acc.id
        INNER JOIN account_heading_descendant ahd
                ON acc.heading = ahd.descendant_id
        GROUP BY ahd.id
    )
       SELECT hm.id, hm.accno, hm.description, hm.account_type, hm.category,
              hm.contra, null::text as gifi_accno,
              null::text as gifi_description, hb.balance, hm.path
         FROM hdr_meta hm
        INNER JOIN hdr_balance hb ON hm.id = hb.id
       UNION
       SELECT am.id, am.accno, am.description, am.account_type, am.category,
              am.contra, am.gifi_accno, am.gifi_description, ab.balance, am.path
         FROM acc_meta am
        INNER JOIN acc_balance ab on am.id = ab.id

### report__cash_summary(in_from_date date, in_to_date date, in_from_accno text, in_to_accno text)

report__cash_summary(in_from_date date, in_to_date date, in_from_accno text, in_to_accno text)

Function Properties

Language Return Type
- SQL SET OF cash_summary_item

    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;

### report__coa()

report__coa()

Function Properties

Language Return Type
- SQL SET OF coa_entry

    WITH ac (chart_id, amount) AS (
         SELECT chart_id, sum(amount)
           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
          WHERE acc_trans.approved and gl.approved
          GROUP BY chart_id
    ),
    l(account_id, link) AS (
         SELECT account_id, array_to_string(array_agg(description), ':')
           FROM account_link
       GROUP BY account_id
    ),
    hh(parent_id) AS (
         SELECT DISTINCT parent_id
           FROM account_heading
    ),
    ha(heading) AS (
         SELECT heading
           FROM account
    ),
    eca(account_id) AS (
        SELECT DISTINCT discount_account_id
          FROM entity_credit_account
        UNION
        SELECT DISTINCT ar_ap_account_id
          FROM entity_credit_account
        UNION
        SELECT DISTINCT cash_account_id
          FROM entity_credit_account
    ),
    ta(account_id) AS (
        SELECT chart_id
          FROM eca_tax
      GROUP BY 1
    )
    SELECT a.id, a.is_heading, a.accno, a.description, a.gifi_accno,
           CASE WHEN sum(ac.amount) < 0 THEN sum(ac.amount) * -1
                ELSE null::numeric END,
           CASE WHEN sum(ac.amount) > 0 THEN sum(ac.amount)
                ELSE null::numeric END,
           count(ac.*)+count(hh.*)+count(ha.*)+count(eca.*)+count(ta.*), l.link
      FROM (SELECT id, heading, false as is_heading, accno, description, gifi_accno
              FROM account
             UNION
            SELECT id, parent_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
     LEFT JOIN hh ON hh.parent_id = a.id AND a.is_heading
     LEFT JOIN ha ON ha.heading = a.id AND a.is_heading
     LEFT JOIN eca ON eca.account_id = a.id AND NOT a.is_heading
     LEFT JOIN ta ON ta.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;

### report__general_balance(in_from_date date, in_to_date date)

report__general_balance(in_from_date date, in_to_date date)

Function Properties

Language Return Type
- SQL SET OF general_balance_line

    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;

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

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

Function Properties

Language Return Type
- PLPGSQL SET OF gl_report_item

    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 - '1 day'::interval)::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 null OR
                         (in_approved = g.approved AND (ac.approved or in_approved is false)))
                  AND (in_from_amount IS NULL OR abs(ac.amount) >= in_from_amount)
                  AND (in_to_amount IS NULL OR abs(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;

### report__incoming_cogs_line(in_date_from date, in_date_to date, in_partnumber text, in_parts_description text)

report__incoming_cogs_line(in_date_from date, in_date_to date, in_partnumber text, in_parts_description text)

Function Properties

Language Return Type
- SQL SET OF incoming_lot_cogs_line

    SELECT i.id, a.id, a.invnumber, a.transdate, i.parts_id, p.partnumber,
           i.description, i.qty * -1, i.allocated, p.onhand,
           i.sellprice, i.qty * i.sellprice * -1, i.allocated * i.sellprice
      FROM ap a
      JOIN invoice i ON a.id = i.trans_id
      JOIN parts p ON i.parts_id = p.id
     WHERE p.income_accno_id IS NOT NULL AND p.expense_accno_id IS NOT NULL
           AND (a.transdate >= $1 OR $1 IS NULL)
           AND (a.transdate <= $2 OR $2 IS NULL)
           AND (p.partnumber like $3 || '%' OR $3 IS NULL)
           AND (p.description @@ plainto_tsquery($4)
                OR p.description LIKE '%' || $4 || '%'
                OR $4 IS NULL)
     ORDER BY p.partnumber, a.invnumber;

### report__invoice_aging_detail(in_entity_id integer, in_entity_class integer, in_accno text, in_to_date date, in_business_units integer[], in_use_duedate boolean, in_name_part text)

report__invoice_aging_detail(in_entity_id integer, in_entity_class integer, in_accno text, in_to_date date, in_business_units integer[], in_use_duedate boolean, in_name_part text)

Function Properties

Language Return Type
- SQL SET OF report_aging_item

                      WITH RECURSIVE bu_tree (id, path) AS (
                    SELECT id, id::text AS path
                      FROM business_unit
                     WHERE id = any(in_business_units)
                           OR in_business_units 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 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, force_closed,
                                   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, force_closed,
                                   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)
                           AND a.force_closed IS NOT TRUE
                           AND (in_name_part IS NULL
                                OR e.name like '%' || in_name_part || '%')
                  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[]))
                           AND sum(ac.amount::numeric(20,2)) <> 0
                  ORDER BY entity_id, curr, transdate, invnumber

### report__invoice_aging_summary(in_entity_id integer, in_entity_class integer, in_accno text, in_to_date date, in_business_units integer[], in_use_duedate boolean, in_name_part text)

report__invoice_aging_summary(in_entity_id integer, in_entity_class integer, in_accno text, in_to_date date, in_business_units integer[], in_use_duedate boolean, in_name_part text)

Function Properties

Language Return Type
- SQL SET OF report_aging_item

    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, $7)
     GROUP BY entity_id, account_number, name, contact_name, curr
     ORDER BY account_number

### report_trial_balance(in_datefrom date, in_dateto date, in_department_id integer, in_project_id integer, in_gifi boolean)

report_trial_balance(in_datefrom date, in_dateto date, in_department_id integer, in_project_id integer, in_gifi boolean)

Function Properties

Language Return Type
- PLPGSQL SET OF trial_balance_line

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;

### robot__get(in_entity_id integer)

robot__get(in_entity_id integer)

Function Properties

Language Return Type
- SQL robot_entity

    SELECT e.id, e.control_code, e.name, e.country_id, c.name,
           p.first_name, p.middle_name, p.last_name, e.entity_class
      FROM entity e
      JOIN country c ON c.id = e.country_id
      JOIN robot p ON p.entity_id = e.id
     WHERE e.id = $1;

### robot__get_by_cc(in_control_code text)

robot__get_by_cc(in_control_code text)

Function Properties

Language Return Type
- SQL robot_entity

    SELECT e.id, e.control_code, e.name, e.country_id, c.name,
           p.first_name, p.middle_name, p.last_name, e.entity_class
      FROM entity e
      JOIN country c ON c.id = e.country_id
      JOIN robot p ON p.entity_id = e.id
     WHERE e.control_code = $1;

### robot__get_my_entity_id()

robot__get_my_entity_id()

Function Properties

Language Return Type
- SQL integer

Returns the entity_id of the current, logged in user.

            SELECT entity_id from users where username = SESSION_USER OR username = 'Migrator';

### robot__list_notes(in_entity_id integer)

robot__list_notes(in_entity_id integer)

Function Properties

Language Return Type
- SQL SET OF entity_note

Returns a list of notes attached to a robot.

                    SELECT *
                    FROM entity_note
                    WHERE ref_key = in_entity_id
                    ORDER BY created

### robot__save(in_entity_id integer, in_first_name text, in_middle_name text, in_last_name text, in_country_id integer)

robot__save(in_entity_id integer, in_first_name text, in_middle_name text, in_last_name text, in_country_id integer)

Function Properties

Language Return Type
- PLPGSQL integer

Saves the robot 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 robot SET
                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 robot (first_name, last_name, entity_id)
            VALUES (in_first_name, in_last_name, e_id);

            RETURN e_id;

        END IF;
    END;

### save_taxform(in_country_code integer, in_taxform_name text)

save_taxform(in_country_code integer, in_taxform_name text)

Function Properties

Language Return Type
- SQL boolean

Saves tax form information. Returns true or raises exception.

            INSERT INTO country_tax_form(country_id, form_name)
            values (in_country_code, in_taxform_name);

            SELECT true;

### sequence__delete(in_label text)

sequence__delete(in_label text)

Function Properties

Language Return Type
- SQL lsmb_sequence

    DELETE FROM lsmb_sequence where label = $1;

    SELECT NULL::lsmb_sequence;

### sequence__get(in_label text)

sequence__get(in_label text)

Function Properties

Language Return Type
- SQL lsmb_sequence

    SELECT * FROM lsmb_sequence WHERE label = $1;

### sequence__increment(in_label text)

sequence__increment(in_label text)

Function Properties

Language Return Type
- PLPGSQL defaults

    DECLARE t_seq lsmb_sequence;
            new_value text;
            retval    defaults;
    BEGIN

       SELECT * INTO t_seq FROM lsmb_sequence WHERE label = in_label
              FOR UPDATE;

       new_value := setting__increment_base(t_seq.sequence);

       UPDATE lsmb_sequence SET sequence = new_value WHERE label = in_label;

       retval := row(t_seq.setting_key, t_seq.prefix || new_value || t_seq.suffix);
       return retval;

    END;

### sequence__list()

sequence__list()

Function Properties

Language Return Type
- SQL SET OF lsmb_sequence

    SELECT * FROM lsmb_sequence order by label;

### sequence__list_by_key(in_setting_key text)

sequence__list_by_key(in_setting_key text)

Function Properties

Language Return Type
- SQL SET OF lsmb_sequence

    SELECT * FROM lsmb_sequence where setting_key = $1 order by label;

### sequence__save(in_label text, in_setting_key text, in_prefix text, in_suffix text, in_sequence text, in_accept_input boolean)

sequence__save(in_label text, in_setting_key text, in_prefix text, in_suffix text, in_sequence text, in_accept_input boolean)

Function Properties

Language Return Type
- PLPGSQL lsmb_sequence

    DECLARE retval lsmb_sequence;
    BEGIN
    UPDATE lsmb_sequence
       SET prefix = coalesce(in_prefix, ''),
           suffix = coalesce(in_suffix, ''),
           sequence = coalesce(in_sequence, '1'),
           setting_key = in_setting_key,
           accept_input = coalesce(in_accept_input, false)
     WHERE label = in_label;

    IF FOUND THEN
       retval := sequence__get(in_label);
       RETURN retval;
    END IF;

    INSERT INTO lsmb_sequence(label, setting_key, prefix, suffix, sequence,
                              accept_input)
    VALUES (in_label, in_setting_key,
            coalesce(in_prefix, ''),
            coalesce(in_suffix, ''),
            coalesce(in_sequence, '1'),
            coalesce(in_accept_input, false)
    );

    retval := sequence__get(in_label);
    RETURN retval;

    end;

### session_check(in_session_id integer, in_token text)

session_check(in_session_id integer, in_token text)

Function Properties

Language Return Type
- PLPGSQL session

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

    DECLARE out_row session%ROWTYPE;
    BEGIN
            PERFORM * FROM defaults
                WHERE setting_key='never_logout' and value = '1';
            IF NOT FOUND THEN
                    DELETE FROM session
                    WHERE last_used < now() - coalesce((SELECT value FROM defaults
                                                            WHERE setting_key = 'session_timeout')::interval,
                                                       '90 minutes'::interval);
            END IF;

            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)
            RETURNING * INTO out_row;

            -- if there is no matching row, return NULL values
            -- note: there is also a failing match when the token doesn't
            -- match; which might mean a replay attack!
            RETURN out_row;
    END;

### setting__get_currencies()

setting__get_currencies()

Function Properties

Language Return Type
- SQL text[]

Returns an array of currencies from the defaults table.

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

### setting__increment_base(in_raw_var text)

setting__increment_base(in_raw_var text)

Function Properties

Language Return Type
- PLPGSQL character varying

    declare raw_value VARCHAR;
           base_value VARCHAR;
           increment  INTEGER;
           inc_length INTEGER;
           new_value VARCHAR;
    begin
        raw_value := in_raw_var;
        base_value := substring(raw_value from
                                    '(' || E'\\' || 'd*)(' || E'\\' || 'D*|<'
                                        || E'\\' || '?lsmb [^<>] ' || E'\\'
                                        || '?>)*$');
        IF base_value like '0%' THEN
             increment := base_value::integer + 1;
             inc_length := char_length(increment::text);
             new_value := overlay(base_value placing increment::varchar
                                  from (char_length(base_value)
                                        - inc_length + 1)
                                  for inc_length);
        ELSE
             new_value := base_value::integer + 1;
        END IF;
        return regexp_replace(raw_value, base_value, new_value);
    end;

### setting__set(in_setting_key character varying, in_value character varying)

setting__set(in_setting_key character varying, in_value character varying)

Function Properties

Language Return Type
- PLPGSQL boolean

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;

### setting_get(in_key character varying)

setting_get(in_key character varying)

Function Properties

Language Return Type
- SQL defaults

Returns the value of the setting in the defaults table.

    SELECT * FROM defaults WHERE setting_key = $1;

### setting_get_default_accounts()

setting_get_default_accounts()

Function Properties

Language Return Type
- SQL SET OF defaults

Returns a set of settings for default accounts.

                    SELECT * FROM defaults
                    WHERE setting_key like '%accno_id'
                    ORDER BY setting_key

### setting_increment(in_key character varying)

setting_increment(in_key character varying)

Function Properties

Language Return Type
- SQL character varying

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.

            UPDATE defaults SET value = setting__increment_base(value)
            WHERE setting_key = in_key
            RETURNING value;

### sic__list()

sic__list()

Function Properties

Language Return Type
- SQL SET OF sic

    SELECT * FROM sic ORDER BY code;

### tax_form__get(in_form_id integer)

tax_form__get(in_form_id integer)

Function Properties

Language Return Type
- SQL country_tax_form

Retrieves specified tax form information from the database.

    SELECT * FROM country_tax_form where id = $1;

### tax_form__list_all()

tax_form__list_all()

Function Properties

Language Return Type
- SQL SET OF tax_form_list

Returns a set of all tax forms, ordered by country_id and id

    SELECT tf.id, tf.form_name, c.name, tf.default_reportable, tf.is_accrual
      FROM country c
      JOIN country_tax_form tf ON c.id = tf.country_id
     ORDER BY country_id, form_name;

### tax_form__list_ext()

tax_form__list_ext()

Function Properties

Language Return Type
- SQL SET OF taxform_list

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;

### tax_form__save(in_id integer, in_country_id integer, in_form_name text, in_default_reportable boolean, in_is_accrual boolean)

tax_form__save(in_id integer, in_country_id integer, in_form_name text, in_default_reportable boolean, in_is_accrual boolean)

Function Properties

Language Return Type
- PLPGSQL integer

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,
                                         is_accrual)
            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;

### tax_form_details_report(in_tax_form_id integer, in_begin date, in_end date, in_meta_number text)

tax_form_details_report(in_tax_form_id integer, in_begin date, in_end date, in_meta_number text)

Function Properties

Language Return Type
- SQL SET OF tax_form_report_detail_item

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.

                  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

### tax_form_details_report_accrual(in_tax_form_id integer, in_begin date, in_end date, in_meta_number text)

tax_form_details_report_accrual(in_tax_form_id integer, in_begin date, in_end date, in_meta_number text)

Function Properties

Language Return Type
- SQL SET OF tax_form_report_detail_item

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.

                  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

### tax_form_summary_report(in_tax_form_id integer, in_begin date, in_end date)

tax_form_summary_report(in_tax_form_id integer, in_begin date, in_end date)

Function Properties

Language Return Type
- SQL SET OF tax_form_report_item

This provides the total reportable value per vendor. As per 1099 forms, these are cash-basis documents and show amounts paid.

                  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

### tax_form_summary_report_accrual(in_tax_form_id integer, in_begin date, in_end date)

tax_form_summary_report_accrual(in_tax_form_id integer, in_begin date, in_end date)

Function Properties

Language Return Type
- SQL SET OF tax_form_report_item

This provides the total reportable value per vendor. As per 1099 forms, these are cash-basis documents and show amounts paid.

                  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

### template__get(in_template_name text, in_language_code character varying, in_format text)

template__get(in_template_name text, in_language_code character varying, in_format text)

Function Properties

Language Return Type
- SQL template

    SELECT * FROM template
     WHERE template_name = $1 AND format = $3 AND
           language_code IS NOT DISTINCT FROM $2;

### template__get_by_id(in_id integer)

template__get_by_id(in_id integer)

Function Properties

Language Return Type
- SQL template

    SELECT * FROM template WHERE id = $1;

### template__list(in_language_code character varying)

template__list(in_language_code character varying)

Function Properties

Language Return Type
- SQL SET OF template

    SELECT * FROM template WHERE language_code IS NOT DISTINCT FROM $1
    ORDER BY template_name, format;

### template__save(in_template_name text, in_language_code character varying, in_template text, in_format text)

template__save(in_template_name text, in_language_code character varying, in_template text, in_format text)

Function Properties

Language Return Type
- PLPGSQL template

    DECLARE retval template;
    BEGIN
       UPDATE template SET template = in_template,
                           last_modified = now()
        WHERE template_name = in_template_name AND format = in_format AND
              language_code IS NOT DISTINCT FROM in_language_code;

       IF FOUND THEN
          retval := template__get(in_template_name, in_language_code, in_format);
          RETURN retval;
       END IF;
       INSERT INTO template (template_name, language_code, template, format)
       VALUES (in_template_name, in_language_code, in_template, in_format);

       retval := template__get(in_template_name, in_language_code, in_format);
       RETURN retval;
    END;

### tg_enforce_perms_eclass()

tg_enforce_perms_eclass()

Function Properties

Language Return Type
- PLPGSQL trigger

    DECLARE
       r_eclass entity_class;
    BEGIN
    IF TG_OP = 'DELETE' THEN
       RETURN OLD;
    ELSE
       -- super user and database owner (group members)
       -- don't need access enforcement
       IF pg_has_role((select rolname
                         from pg_database db inner join pg_roles rol
                           on db.datdba = rol.oid
                        where db.datname = current_database()),
                      'USAGE') IS TRUE THEN
          RETURN NEW;
       END IF;

       SELECT * INTO r_eclass from entity_class WHERE id = NEW.entity_class;
       IF pg_has_role(SESSION_USER,
                      lsmb__role('contact_class_'
                                 || lower(regexp_replace(r_eclass.class,
                                                         ' ', '_'))),
                      'USAGE')
       THEN
          RETURN NEW;
       ELSE
          RAISE EXCEPTION 'Access Denied for class';
       END IF;
    END IF;
    END;

### timecard__allocate(in_id integer, in_amount numeric)

timecard__allocate(in_id integer, in_amount numeric)

Function Properties

Language Return Type
- PLPGSQL jcitems

    DECLARE retval jcitems;

    BEGIN

    UPDATE jcitems SET allocated = allocated + in_amount WHERE id = in_id;

    IF NOT FOUND THEN
       RAISE EXCEPTION 'timecard not found';
    END IF;

    SELECT * INTO retval FROM jcitems WHERE id = in_id;

    IF allocated > qty THEN
       RAISE EXCEPTION 'Too many allocated';
    END IF;

    RETURN retval;

    END;

### timecard__bu_class(in_id integer)

timecard__bu_class(in_id integer)

Function Properties

Language Return Type
- SQL business_unit_class

    SELECT * from business_unit_class
     where id in (select class_id from business_unit
                   WHERE id in (select business_unit_id from jcitems
                                 WHERE id = $1));

### timecard__get(id integer)

timecard__get(id integer)

Function Properties

Language Return Type
- SQL jcitems

     SELECT * FROM jcitems WHERE id = $1;

### timecard__parts(in_timecard boolean, in_service boolean, in_partnumber text)

timecard__parts(in_timecard boolean, in_service boolean, in_partnumber text)

Function Properties

Language Return Type
- SQL SET OF parts

    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))
           AND ($3 IS NULL OR partnumber like $3 || '%')
     ORDER BY partnumber;

### timecard__report(in_business_units integer[], in_partnumber text, in_person_id integer, in_date_from date, in_date_to date, in_open boolean, in_closed boolean, in_jctype integer)

timecard__report(in_business_units integer[], in_partnumber text, in_person_id integer, in_date_from date, in_date_to date, in_open boolean, in_closed boolean, in_jctype integer)

Function Properties

Language Return Type
- SQL SET OF timecard_report_line

    WITH RECURSIVE bu_tree (id, path) AS (
         SELECT id, id::text AS path, control_code, description
           FROM business_unit
          WHERE id = any(in_business_units) OR (in_business_units = '{}' OR in_business_units IS NULL and parent_id IS NULL)
          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,
           date_trunc('week', j.checkedin)::date as weekstarting,
           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 pr ON pr.id = j.person_id
      JOIN entity_employee ee ON ee.entity_id = pr.entity_id
      JOIN entity e ON ee.entity_id = e.id
      LEFT JOIN bu_tree bu ON bu.id = j.business_unit_id
     WHERE (p.partnumber = in_partnumber OR in_partnumber IS NULL)
           AND (j.person_id = in_person_id OR in_person_id IS NULL)
           AND (j.checkedin::date >= in_date_from OR in_date_from IS NULL)
           AND (j.checkedin::date <= in_date_to OR in_date_to IS NULL)
           AND (((j.qty > j.allocated or j.allocated is null)  AND in_open)
                OR (j.qty <= j.allocated AND in_closed))
           AND (j.jctype = in_jctype OR in_jctype is null)
           AND (bu.path IS NOT NULL OR in_business_units = '{}' OR in_business_units IS NULL)
      ORDER BY j.checkedin, bu.description, p.partnumber, e.name

### timecard__save(in_id integer, in_business_unit_id integer, in_parts_id integer, in_description text, in_qty numeric, in_allocated numeric, in_sellprice numeric, in_fxsellprice numeric, in_serialnumber text, in_checkedin timestamp with time zone, in_checkedout timestamp with time zone, in_person_id integer, in_notes text, in_total numeric, in_non_billable numeric, in_curr bpchar, in_jctype integer)

timecard__save(in_id integer, in_business_unit_id integer, in_parts_id integer, in_description text, in_qty numeric, in_allocated numeric, in_sellprice numeric, in_fxsellprice numeric, in_serialnumber text, in_checkedin timestamp with time zone, in_checkedout timestamp with time zone, in_person_id integer, in_notes text, in_total numeric, in_non_billable numeric, in_curr bpchar, in_jctype integer)

Function Properties

Language Return Type
- PLPGSQL jcitems

    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 = coalesce(in_person_id, person__get_my_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, jctype, curr)
    VALUES
    (in_business_unit_id, in_parts_id, in_description, in_qty, in_allocated,
      in_sellprice, in_fxsellprice, in_serialnumber, in_checkedin, in_checkedout,
      coalesce(in_person_id, person__get_my_id()), in_notes, in_total,
      in_non_billable, in_jctype, in_curr);

    SELECT * INTO retval FROM jcitems WHERE id = currval('jcitems_id_seq')::int;

    RETURN retval;

    END;

### timecard_type__get(in_id integer)

timecard_type__get(in_id integer)

Function Properties

Language Return Type
- SQL jctype

    SELECT * FROM jctype where id = $1;

### timecard_type__list()

timecard_type__list()

Function Properties

Language Return Type
- SQL SET OF jctype

    SELECT * FROM jctype ORDER BY label;

### to_args(in_base text[], in_args text[])

to_args(in_base text[], in_args text[])

Function Properties

Language Return Type
- SQL text[]

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;

### to_args(text[])

to_args(text[])

Function Properties

Language Return Type
- INTERNAL text[]

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

    aggregate_dummy

### track_global_sequence()

track_global_sequence()

Function Properties

Language Return Type
- PLPGSQL trigger

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;

### trial_balance__generate(in_from_date date, in_to_date date, in_heading integer, in_accounts integer[], in_ignore_yearend text, in_business_units integer[], in_balance_sign integer, in_all_accounts boolean, in_approved boolean)

trial_balance__generate(in_from_date date, in_to_date date, in_heading integer, in_accounts integer[], in_ignore_yearend text, in_business_units integer[], in_balance_sign integer, in_all_accounts boolean, in_approved boolean)

Function Properties

Language Return Type
- PLPGSQL SET OF tb_row

Returns a row for each account which has transactions or a starting or ending balance over the indicated period, except when in_all_accounts is true, in which case a record is returned for all accounts, even ones unused over the reporting period.

    DECLARE
            out_row         tb_row;
            t_roll_forward  date;
            t_cp            account_checkpoint;
            ignore_trans    int[];
            t_start_date    date;
            t_end_date      date;
            t_balance_sign  int;
    BEGIN
        IF in_balance_sign IS NULL OR in_balance_sign = 0 THEN
           t_balance_sign = null;
        ELSIF in_balance_sign = -1 OR in_balance_sign = 1 THEN
           t_balance_sign = in_balance_sign;
        ELSE
           RAISE EXCEPTION 'Invalid Balance Type';
        END IF;

        IF in_from_date IS NULL AND in_ignore_yearend = 'none' THEN
           SELECT max(end_date) INTO t_roll_forward
             FROM account_checkpoint;
        ELSIF in_from_date IS NULL AND in_ignore_yearend = 'last' THEN
           SELECT max(end_date) INTO t_roll_forward
             FROM account_checkpoint
            WHERE end_date < (select max(gl.transdate)
                                FROM gl JOIN yearend y ON y.trans_id = gl.id
                               WHERE y.transdate < coalesce(in_to_date, gl.transdate)
                             );
        ELSIF in_from_date IS NULL THEN
           SELECT min(transdate) - 1 INTO t_roll_forward
             FROM (select min(transdate) as transdate from ar
                    union ALL
                   select min(transdate) from ap
                    union all
                   select min(transdate) from gl
                    union all
                   select min(transdate) from acc_trans) gl;

        ELSE
          SELECT max(end_date) INTO t_roll_forward
             FROM account_checkpoint
            WHERE end_date < in_from_date;
        END IF;

        IF t_roll_forward IS NULL
           OR array_upper(in_business_units, 1) > 0
        THEN
           SELECT min(transdate) - '1 day'::interval
             INTO t_roll_forward
             FROM acc_trans;
        END IF;

        IF in_ignore_yearend = 'last' THEN
           SELECT ARRAY[trans_id] INTO ignore_trans FROM yearend
         ORDER BY transdate DESC LIMIT 1;
        ELSIF in_ignore_yearend = 'all' THEN
           SELECT array_agg(trans_id) INTO ignore_trans FROM yearend;
        ELSE
           ignore_trans := '{}';
        END IF;

        IF in_to_date IS NULL THEN
            SELECT max(transdate) INTO t_end_date FROM acc_trans;
        ELSE
            t_end_date := in_to_date;
        END IF;

        RETURN QUERY
           WITH ac (transdate, amount, chart_id) AS (
               WITH RECURSIVE bu_tree (id, path) AS (
                SELECT id, id::text AS path
                  FROM business_unit
                 WHERE parent_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 ac.transdate, ac.amount, ac.chart_id
             FROM acc_trans ac
             JOIN (SELECT id, approved FROM ar UNION ALL
                   SELECT id, approved FROM ap UNION ALL
                   SELECT id, approved FROM gl) gl
                       ON ac.trans_id = gl.id
                         AND (in_approved is null
                              OR (gl.approved = in_approved
                                 and (ac.approved OR in_approved is false)))
        LEFT JOIN business_unit_ac buac ON ac.entry_id = buac.entry_id
        LEFT JOIN bu_tree ON buac.bu_id = bu_tree.id
            WHERE ac.transdate BETWEEN t_roll_forward + '1 day'::interval
                                        AND t_end_date
                  AND (ignore_trans is null or ac.trans_id <> ALL(ignore_trans))
                  AND ((in_business_units = '{}' OR in_business_units IS NULL)
                   OR bu_tree.id IS NOT NULL)
           )
           SELECT a.id, a.accno,
             coalesce(at.description, a.description) as description, a.gifi_accno,
             case when in_from_date is null then 0 else
                  COALESCE(t_balance_sign,
                          CASE WHEN a.category IN ('A', 'E') THEN -1 ELSE 1 END )
                  * (coalesce(cp.amount, 0)
                  + sum(CASE WHEN ac.transdate < coalesce(in_from_date,
                                                          t_roll_forward)
                             THEN ac.amount ELSE 0 END)) end,
                  sum(CASE WHEN ac.transdate BETWEEN coalesce(in_from_date,
                                                             t_roll_forward)
                                                     AND coalesce(in_to_date,
                                                             ac.transdate)
                                 AND ac.amount < 0 THEN ac.amount * -1 ELSE 0 END) -
                  case when in_from_date is null then coalesce(cp.debits, 0) else 0 end,
                  sum(CASE WHEN ac.transdate BETWEEN coalesce(in_from_date,
                                                             t_roll_forward)
                                                     AND coalesce(in_to_date,
                                                             ac.transdate)
                                 AND ac.amount > 0 THEN ac.amount ELSE 0 END) +
                  case when in_from_date is null then coalesce(cp.credits, 0) else 0 end,
                  COALESCE(t_balance_sign,
                           CASE WHEN a.category IN ('A', 'E') THEN -1 ELSE 1 END)
                  * (coalesce(cp.amount, 0) + sum(coalesce(ac.amount, 0))),
                  CASE WHEN sum(ac.amount) + coalesce(cp.amount, 0) < 0
                       THEN (sum(ac.amount) + coalesce(cp.amount, 0)) * -1
                       ELSE NULL END,
                  CASE WHEN sum(ac.amount) + coalesce(cp.amount, 0) > 0
                       THEN sum(ac.amount) + coalesce(cp.amount, 0) ELSE NULL END
             FROM account a
        LEFT JOIN ac ON ac.chart_id = a.id
        LEFT JOIN account_checkpoint cp ON cp.account_id = a.id
                  AND end_date = t_roll_forward
        LEFT JOIN (SELECT trans_id, description
                     FROM account_translation at
                  INNER JOIN user_preference up ON up.language = at.language_code
                  INNER JOIN users ON up.id = users.id
                    WHERE users.username = SESSION_USER) at ON a.id = at.trans_id
            WHERE (in_accounts IS NULL OR in_accounts = '{}'
                   OR a.id = ANY(in_accounts))
                  AND (in_heading IS NULL OR in_heading = a.heading)
         GROUP BY a.id, a.accno, coalesce(at.description, a.description),
                  a.category, a.gifi_accno, cp.end_date, cp.account_id, cp.amount,
                  cp.debits, cp.credits
           HAVING abs(cp.amount) > 0 or count(ac) > 0 or in_all_accounts
         ORDER BY a.accno;
    END;

### trial_balance__heading_accounts(in_accounts integer[])

trial_balance__heading_accounts(in_accounts integer[])

Function Properties

Language Return Type
- SQL SET OF account

        SELECT * FROM account WHERE id in (SELECT unnest($1));

### trial_balance__list_headings()

trial_balance__list_headings()

Function Properties

Language Return Type
- SQL SET OF trial_balance__heading

        SELECT id, accno, description, ARRAY( SELECT id FROM account where heading = ah.id) FROM account_heading ah;

### trigger_parts_short()

trigger_parts_short()

Function Properties

Language Return Type
- PLPGSQL trigger

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

### unlock(in_id integer)

unlock(in_id integer)

Function Properties

Language Return Type
- PLPGSQL boolean

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;

### unlock_all()

unlock_all()

Function Properties

Language Return Type
- PLPGSQL boolean

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;

### user__change_password(in_new_password text)

user__change_password(in_new_password text)

Function Properties

Language Return Type
- PLPGSQL integer

Allows 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;

### user__check_my_expiration()

user__check_my_expiration()

Function Properties

Language Return Type
- PLPGSQL interval

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;

### user__expires_soon()

user__expires_soon()

Function Properties

Language Return Type
- SQL boolean

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';

### user__get_all_users()

user__get_all_users()

Function Properties

Language Return Type
- SQL SET OF user_listable

        select * from user_listable;

### user__get_preferences(in_user_id integer)

user__get_preferences(in_user_id integer)

Function Properties

Language Return Type
- PLPGSQL user_preference

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 v_row;
        END IF;
    END;

### user__save_preferences(in_dateformat text, in_numberformat text, in_language text, in_stylesheet text, in_printer text)

user__save_preferences(in_dateformat text, in_numberformat text, in_language text, in_stylesheet text, in_printer text)

Function Properties

Language Return Type
- PLPGSQL boolean

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;

### voucher__delete(in_voucher_id integer)

voucher__delete(in_voucher_id integer)

Function Properties

Language Return Type
- PLPGSQL integer

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;

### voucher__list(in_batch_id integer)

voucher__list(in_batch_id integer)

Function Properties

Language Return Type
- SQL SET OF voucher_list

Retrieves a list of vouchers and amounts attached to the batch.

                    SELECT v.id, a.invoice, a.invnumber, e.name,
                            v.batch_id, v.trans_id,
                            a.amount, a.transdate, 'Payable', v.batch_class
                    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', v.batch_class
                    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, v.batch_class
                    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, v.batch_class
                    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', v.batch_class
                    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

### voucher_get_batch(in_batch_id integer)

voucher_get_batch(in_batch_id integer)

Function Properties

Language Return Type
- PLPGSQL batch

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;

### wage__list_for_entity(in_entity_id integer)

wage__list_for_entity(in_entity_id integer)

Function Properties

Language Return Type
- SQL SET OF payroll_wage

    SELECT * FROM payroll_wage WHERE entity_id = $1;

### wage__list_types(in_country_id integer)

wage__list_types(in_country_id integer)

Function Properties

Language Return Type
- SQL SET OF payroll_income_type

    SELECT * FROM payroll_income_type where country_id = $1

### wage__save(in_rate numeric, in_entity_id integer, in_type_id integer)

wage__save(in_rate numeric, in_entity_id integer, in_type_id integer)

Function Properties

Language Return Type
- PLPGSQL payroll_wage

    DECLARE
      return_wage payroll_wage;
    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;

    SELECT * INTO return_wage FROM payroll_wage
                 WHERE entity_id = in_entity_id and in_type_id;

    RETURN return_wage;
    END;

### warehouse__list()

warehouse__list()

Function Properties

Language Return Type
- SQL SET OF warehouse

    SELECT * FROM warehouse ORDER BY DESCRIPTION;

### warehouse__list_all()

warehouse__list_all()

Function Properties

Language Return Type
- SQL SET OF warehouse

    SELECT * FROM warehouse order by description;