Dumped on 2012-12-27
standard public schema
Mapping acc_trans to country_tax_form for reporting purposes.
F-Key | Name | Type | Description |
---|---|---|---|
lsmb13.acc_trans.entry_id | entry_id | integer | PRIMARY KEY |
reportable | boolean |
This table stores line items for financial transactions. Please note that payments in 1.3 are not full-fledged transactions.
F-Key | Name | Type | Description |
---|---|---|---|
lsmb13.transactions.id | trans_id | integer | NOT NULL |
lsmb13.account.id | chart_id | integer | NOT NULL |
amount | numeric | ||
transdate | date | DEFAULT ('now'::text)::date | |
source | text |
Document Source identifier for individual line items, usually used for payments. |
|
cleared | boolean | DEFAULT false | |
fx_transaction | boolean | DEFAULT false | |
project_id | integer | ||
memo | text | ||
invoice_id | integer | ||
approved | boolean | DEFAULT true | |
cleared_on | date | ||
reconciled_on | date | ||
lsmb13.voucher.id | voucher_id | integer | |
entry_id | serial | PRIMARY KEY |
Tables referencing this one via Foreign Key Constraints:
ac_transdate_year_idx date_part('YEAR'::text, transdate) acc_trans_chart_id_key chart_id acc_trans_source_key lower(source) acc_trans_trans_id_key trans_id acc_trans_transdate_key transdate acc_trans_voucher_id_idx voucher_idThis table stores the main account info.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | UNIQUE NOT NULL | |
accno | text | PRIMARY KEY | |
description | text | ||
category | character(1) | NOT NULL | |
gifi_accno | text | ||
lsmb13.account_heading.id | heading | integer | NOT NULL |
contra | boolean | NOT NULL DEFAULT false | |
tax | boolean | NOT NULL DEFAULT false |
Tables referencing this one via Foreign Key Constraints:
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.
F-Key | Name | Type | Description |
---|---|---|---|
end_date | date | PRIMARY KEY | |
lsmb13.account.id | account_id | integer | PRIMARY KEY |
amount | numeric | NOT NULL | |
id | serial | UNIQUE NOT NULL | |
debits | numeric | ||
credits | numeric |
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.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | UNIQUE NOT NULL | |
accno | text | PRIMARY KEY | |
lsmb13.account_heading.id | parent_id | integer | |
description | text |
Tables referencing this one via Foreign Key Constraints:
F-Key | Name | Type | Description |
---|---|---|---|
lsmb13.account.id | account_id | integer | PRIMARY KEY |
lsmb13.account_link_description.description | description | text | PRIMARY KEY |
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.
F-Key | Name | Type | Description |
---|---|---|---|
description | text | PRIMARY KEY | |
summary | boolean | NOT NULL | |
custom | boolean | NOT NULL |
Tables referencing this one via Foreign Key Constraints:
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.
F-Key | Name | Type | Description |
---|---|---|---|
lsmb13.transactions.id | id | integer | PRIMARY KEY DEFAULT nextval('id'::regclass) |
invnumber | text |
Text identifier for the invoice. Must be unique. |
|
transdate | date | DEFAULT ('now'::text)::date | |
lsmb13.entity.id | entity_id | integer | |
taxincluded | boolean | DEFAULT false | |
amount | numeric |
This stores the total amount (including taxes) for the transaction. |
|
netamount | numeric |
Total amount excluding taxes for the transaction. |
|
paid | numeric | ||
datepaid | date | ||
duedate | date | ||
invoice | boolean |
DEFAULT false
True if the transaction tracks goods/services purchase using the invoice table. False otherwise. |
|
ordnumber | text |
Order Number |
|
curr | character(3) |
3 letters to identify the currency. |
|
notes | text |
These notes are displayed on the invoice when printed or emailed |
|
lsmb13.entity_employee.entity_id | person_id | integer |
Person who created the transaction |
till | character varying(20) | ||
quonumber | text |
Quotation Number |
|
intnotes | text |
These notes are not displayed when the invoice is printed or emailed and may be updated without reposting hte invocie. |
|
department_id | integer | ||
shipvia | text | ||
language_code | character varying(6) | ||
ponumber | text |
Purchase Order Number |
|
shippingpoint | text | ||
on_hold | boolean | DEFAULT false | |
approved | boolean |
DEFAULT true
Only show in financial reports if true. |
|
reverse | boolean |
DEFAULT false
If true numbers are displayed after multiplying by -1 |
|
terms | smallint | ||
description | text | ||
force_closed | boolean |
Not exposed to the UI, but can be set to prevent an invoice from showing up for payment or in outstanding reports. |
|
lsmb13.entity_credit_account.id | entity_credit_account | integer |
NOT NULL
reference for the vendor account used. |
Name | Constraint |
---|---|
ap_check | CHECK ((((amount IS NULL) AND (curr IS NULL)) OR ((amount IS NOT NULL) AND (curr IS NOT NULL)))) |
Tables referencing this one via Foreign Key Constraints:
ap_approved_idx approved ap_curr_idz curr ap_id_key id ap_invnumber_key invnumber ap_ordnumber_key ordnumber ap_quonumber_key quonumber ap_transdate_key transdateSummary/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.
F-Key | Name | Type | Description |
---|---|---|---|
lsmb13.transactions.id | id | integer | PRIMARY KEY DEFAULT nextval('id'::regclass) |
invnumber | text |
UNIQUE
Text identifier for the invoice. Must be unique. |
|
transdate | date | DEFAULT ('now'::text)::date | |
lsmb13.entity.id | entity_id | integer | |
taxincluded | boolean | ||
amount | numeric |
This stores the total amount (including taxes) for the transaction. |
|
netamount | numeric |
Total amount excluding taxes for the transaction. |
|
paid | numeric | ||
datepaid | date | ||
duedate | date | ||
invoice | boolean |
DEFAULT false
True if the transaction tracks goods/services purchase using the invoice table. False otherwise. |
|
shippingpoint | text | ||
terms | smallint | ||
notes | text |
These notes are displayed on the invoice when printed or emailed |
|
curr | character(3) |
3 letters to identify the currency. |
|
ordnumber | text |
Order Number |
|
lsmb13.entity_employee.entity_id | person_id | integer |
Person who created the transaction |
till | character varying(20) | ||
quonumber | text |
Quotation Number |
|
intnotes | text |
These notes are not displayed when the invoice is printed or emailed and may be updated without reposting hte invocie. |
|
department_id | integer | ||
shipvia | text | ||
language_code | character varying(6) | ||
ponumber | text |
Purchase Order Number |
|
on_hold | boolean | DEFAULT false | |
reverse | boolean |
DEFAULT false
If true numbers are displayed after multiplying by -1 |
|
approved | boolean |
DEFAULT true
Only show in financial reports if true. |
|
lsmb13.entity_credit_account.id | entity_credit_account | integer |
NOT NULL
reference for the customer account used. |
force_closed | boolean |
Not exposed to the UI, but can be set to prevent an invoice from showing up for payment or in outstanding reports. |
|
description | text |
Holds mapping for parts that are members of assemblies.
F-Key | Name | Type | Description |
---|---|---|---|
lsmb13.parts.id | id | integer |
PRIMARY KEY
This is the id of the assembly the part is being mapped to. |
lsmb13.parts.id | parts_id | integer |
PRIMARY KEY
ID of part that is a member of the assembly. |
qty | numeric | ||
bom | boolean | ||
adj | boolean |
The account fields here set the defaults for the individual asset items. They are non-authoritative.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | UNIQUE NOT NULL | |
label | text | PRIMARY KEY | |
lsmb13.account.id | asset_account_id | integer | |
lsmb13.account.id | dep_account_id | integer | |
lsmb13.asset_dep_method.id | method | integer |
Tables referencing this one via Foreign Key Constraints:
Stores asset depreciation methods, and their relevant stored procedures. The fixed asset system is such depreciation methods can be plugged in via this table.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | UNIQUE NOT NULL | |
method | text |
PRIMARY KEY
These are keyed to specific stored procedures. Currently only "straight_line" is supported |
|
sproc | text |
UNIQUE
NOT NULL
The sproc mentioned here is a stored procedure which must have the following arguments: (in_asset_ids int[], in_report_date date, in_report_id int). Here in_asset_ids are the assets to be depreciated, in_report_date is the date of the report, and in_report_id is the id of the report. The sproc MUST insert the relevant lines into asset_report_line. |
|
unit_label | text | NOT NULL | |
short_name | text | UNIQUE NOT NULL | |
lsmb13.asset_unit_class.id | unit_class | integer | NOT NULL |
Tables referencing this one via Foreign Key Constraints:
F-Key | Name | Type | Description |
---|---|---|---|
label | text | PRIMARY KEY | |
id | serial | UNIQUE NOT NULL | |
multiple | integer | ||
short_label | character(1) |
Name | Constraint |
---|---|
asset_disposal_method_multiple_check | CHECK ((multiple = ANY (ARRAY[1, 0, (-1)]))) |
Tables referencing this one via Foreign Key Constraints:
Stores details of asset items. The account fields here are authoritative, while the ones in the asset_class table are defaults.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
description | text | ||
tag | text |
UNIQUE#1
NOT NULL
This can be plugged into other routines to generate it automatically via ALTER TABLE .... SET DEFAULT..... |
|
purchase_value | numeric | ||
salvage_value | numeric | ||
usable_life | numeric | ||
purchase_date | date | NOT NULL | |
start_depreciation | date | NOT NULL | |
lsmb13.warehouse.id | location_id | integer | |
lsmb13.department.id | department_id | integer | |
lsmb13.ap.id | invoice_id | integer | |
lsmb13.account.id | asset_account_id | integer | |
lsmb13.account.id | dep_account_id | integer | |
lsmb13.account.id | exp_account_id | integer | |
lsmb13.asset_item.id | obsolete_by | integer | UNIQUE#1 |
lsmb13.asset_class.id | asset_class_id | integer |
Tables referencing this one via Foreign Key Constraints:
F-Key | Name | Type | Description |
---|---|---|---|
id | integer | NOT NULL DEFAULT nextval('note_id_seq'::regclass) | |
note_class | integer | NOT NULL DEFAULT 4 | |
note | text | NOT NULL | |
vector | tsvector | NOT NULL DEFAULT ''::pg_catalog.tsvector | |
created | timestamp without time zone | NOT NULL DEFAULT now() | |
created_by | text | DEFAULT "session_user"() | |
lsmb13.asset_item.id | ref_key | integer | NOT NULL |
subject | text |
Table lsmb13.asset_note Inherits note,
Name | Constraint |
---|---|
asset_note_note_class_check | CHECK ((note_class = 4)) |
Asset reports are discrete sets of depreciation or disposal transctions, and each one may be turned into no more than one GL transaction.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
report_date | date | ||
lsmb13.gl.id | gl_id | bigint | UNIQUE |
lsmb13.asset_class.id | asset_class | bigint | |
lsmb13.asset_report_class.id | report_class | integer | |
lsmb13.entity.id | entered_by | bigint | NOT NULL DEFAULT person__get_my_entity_id() |
lsmb13.entity.id | approved_by | bigint | |
entered_at | timestamp without time zone | DEFAULT now() | |
approved_at | timestamp without time zone | ||
depreciated_qty | numeric | ||
dont_approve | boolean | DEFAULT false | |
submitted | boolean | NOT NULL DEFAULT false |
Tables referencing this one via Foreign Key Constraints:
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.
F-Key | Name | Type | Description |
---|---|---|---|
id | integer | UNIQUE NOT NULL | |
class | text | PRIMARY KEY |
Tables referencing this one via Foreign Key Constraints:
F-Key | Name | Type | Description |
---|---|---|---|
lsmb13.asset_item.id | asset_id | bigint | PRIMARY KEY |
lsmb13.asset_report.id | report_id | bigint | PRIMARY KEY |
amount | numeric | ||
lsmb13.department.id | department_id | integer |
In case assets are moved between departments, we have to store this here. |
lsmb13.warehouse.id | warehouse_id | integer |
Maps disposal method to line items in the asset disposal report.
F-Key | Name | Type | Description |
---|---|---|---|
lsmb13.asset_report.id | report_id | integer | PRIMARY KEY |
lsmb13.asset_item.id | asset_id | integer | PRIMARY KEY |
lsmb13.asset_disposal_method.id | disposal_method_id | integer | PRIMARY KEY |
percent_disposed | numeric |
F-Key | Name | Type | Description |
---|---|---|---|
id | integer | UNIQUE NOT NULL | |
class | text | PRIMARY KEY |
Tables referencing this one via Foreign Key Constraints:
This stores information on who entered or updated rows in the ar, ap, or gl tables.
F-Key | Name | Type | Description |
---|---|---|---|
trans_id | integer | ||
tablename | text | ||
reference | text | ||
formname | text | ||
action | text | ||
transdate | timestamp without time zone | DEFAULT now() | |
lsmb13.person.entity_id | person_id | integer | NOT NULL |
entry_id | bigserial | PRIMARY KEY |
Stores batch header info. Batches are groups of vouchers that are posted together.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
lsmb13.batch_class.id | batch_class_id | integer |
NOT NULL
Note that this field is largely used for sorting the vouchers. A given batch is NOT restricted to this type. |
control_code | text | NOT NULL | |
description | text | ||
default_date | date | NOT NULL | |
approved_on | date | ||
lsmb13.entity_employee.entity_id | approved_by | integer | |
lsmb13.entity_employee.entity_id | created_by | integer | |
lsmb13.session.session_id | locked_by | integer | |
created_on | date | DEFAULT now() |
Name | Constraint |
---|---|
batch_control_code_check | CHECK ((length(control_code) > 0)) |
Tables referencing this one via Foreign Key Constraints:
These values are hard-coded. Please coordinate before adding standard values.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | UNIQUE NOT NULL | |
class | character varying | PRIMARY KEY |
Tables referencing this one via Foreign Key Constraints:
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | UNIQUE NOT NULL | |
start_date | date | NOT NULL | |
end_date | date | NOT NULL | |
reference | text | PRIMARY KEY | |
description | text | NOT NULL | |
lsmb13.entity.id | entered_by | integer | NOT NULL DEFAULT person__get_my_entity_id() |
lsmb13.entity.id | approved_by | integer | |
lsmb13.entity.id | obsolete_by | integer | |
entered_at | timestamp without time zone | NOT NULL DEFAULT now() | |
approved_at | timestamp without time zone | ||
obsolete_at | timestamp without time zone |
Name | Constraint |
---|---|
budget_info_check | CHECK ((start_date < end_date)) |
Tables referencing this one via Foreign Key Constraints:
F-Key | Name | Type | Description |
---|---|---|---|
lsmb13.budget_info.id | budget_id | integer | PRIMARY KEY |
lsmb13.account.id | account_id | integer | PRIMARY KEY |
description | text | ||
amount | numeric | NOT NULL |
F-Key | Name | Type | Description |
---|---|---|---|
id | integer | PRIMARY KEY DEFAULT nextval('note_id_seq'::regclass) | |
note_class | integer | NOT NULL DEFAULT 6 | |
note | text | NOT NULL | |
vector | tsvector | NOT NULL DEFAULT ''::pg_catalog.tsvector | |
created | timestamp without time zone | NOT NULL DEFAULT now() | |
created_by | text | DEFAULT "session_user"() | |
lsmb13.budget_info.id | ref_key | integer | NOT NULL |
subject | text |
Table lsmb13.budget_note Inherits note,
Name | Constraint |
---|---|
budget_note_note_class_check | CHECK ((note_class = 6)) |
F-Key | Name | Type | Description |
---|---|---|---|
lsmb13.budget_info.id | budget_id | integer | PRIMARY KEY |
lsmb13.department.id | department_id | integer | NOT NULL |
F-Key | Name | Type | Description |
---|---|---|---|
lsmb13.budget_info.id | budget_id | integer | PRIMARY KEY |
lsmb13.project.id | project_id | integer | NOT NULL |
Groups of Customers assigned joint discounts.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
description | text | ||
discount | numeric |
Compatibility chart for 1.2 and earlier.
F-Key | Name | Type | Description |
---|---|---|---|
id | integer | ||
accno | text | ||
description | text | ||
charttype | text | ||
category | bpchar | ||
link | text | ||
account_heading | integer | ||
gifi_accno | text | ||
contra | boolean | ||
tax | boolean |
SELECT account_heading.id , account_heading.accno , account_heading.description ,'H'::text AS charttype , NULL::bpchar AS category , NULL::text AS link , NULL::integer AS account_heading , NULL::text AS gifi_accno , false AS contra , false AS tax FROM lsmb13.account_heading UNIONSELECT c.id , c.accno , c.description ,'A'::text AS charttype , c.category , lsmb13.concat_colon (l.description) AS link , c.heading AS account_heading , c.gifi_accno , c.contra , c.tax FROM (lsmb13.account c LEFT JOIN lsmb13.account_link l ON ( (c.id = l.account_id) ) ) GROUP BY c.id , c.accno , c.description , c.category , c.heading , c.gifi_accno , c.contra , c.tax;
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | UNIQUE NOT NULL | |
lsmb13.entity.id | entity_id | integer | PRIMARY KEY |
legal_name | text | PRIMARY KEY | |
tax_id | text |
In the US this would be a EIN. |
|
sic_code | character varying | ||
created | date | NOT NULL DEFAULT ('now'::text)::date |
Name | Constraint |
---|---|
company_legal_name_check | CHECK ((legal_name ~ '[[:alnum:]_]'::text)) |
Tables referencing this one via Foreign Key Constraints:
company_name_gist__idx legal_name lsmb13.gist_trgm_opsF-Key | Name | Type | Description |
---|---|---|---|
lsmb13.company.id | company_id | integer | PRIMARY KEY |
lsmb13.contact_class.id | contact_class_id | integer | PRIMARY KEY |
contact | text | PRIMARY KEY | |
description | text |
Name | Constraint |
---|---|
company_to_contact_contact_check | CHECK ((contact ~ '[[:alnum:]_]'::text)) |
This provides a map so that entities can also be used like groups.
F-Key | Name | Type | Description |
---|---|---|---|
lsmb13.company.id | company_id | integer | PRIMARY KEY |
lsmb13.entity.id | entity_id | integer | PRIMARY KEY |
related_how | text | ||
created | date | NOT NULL DEFAULT ('now'::text)::date |
Name | Constraint |
---|---|
company_to_entity_check | CHECK ((company_id <> entity_id)) |
This table is used for locations generic to companies. For contract-bound addresses, use eca_to_location instead
F-Key | Name | Type | Description |
---|---|---|---|
lsmb13.location.id | location_id | integer | PRIMARY KEY |
lsmb13.location_class.id | location_class | integer | PRIMARY KEY |
lsmb13.company.id | company_id | integer | PRIMARY KEY |
Stores type of contact information attached to companies and persons. Please coordinate with others before adding new types.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | UNIQUE NOT NULL | |
class | text | PRIMARY KEY |
Name | Constraint |
---|---|
contact_class_class_check | CHECK ((class ~ '[[:alnum:]_]'::text)) |
Tables referencing this one via Foreign Key Constraints:
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
name | text | NOT NULL | |
short_name | text | NOT NULL | |
itu | text |
The ITU Telecommunication Standardization Sector code for calling internationally. For example, the US is 1, Great Britain is 44 |
Name | Constraint |
---|---|
country_name_check | CHECK ((name ~ '[[:alnum:]_]'::text)) |
country_short_name_check | CHECK ((short_name ~ '[[:alnum:]_]'::text)) |
Tables referencing this one via Foreign Key Constraints:
This table was designed for holding information relating to reportable sales or purchases, such as IRS 1099 forms and international equivalents.
F-Key | Name | Type | Description |
---|---|---|---|
lsmb13.country.id | country_id | integer | PRIMARY KEY |
form_name | text | PRIMARY KEY | |
id | serial | UNIQUE NOT NULL | |
default_reportable | boolean | NOT NULL DEFAULT false |
Tables referencing this one via Foreign Key Constraints:
F-Key | Name | Type | Description |
---|---|---|---|
lsmb13.account.id | chart_id | integer | NOT NULL |
account | text | NOT NULL |
F-Key | Name | Type | Description |
---|---|---|---|
id | bigserial | PRIMARY KEY | |
lsmb13.account.id | chart_id | integer | NOT NULL |
their_total | numeric | NOT NULL | |
approved | boolean | NOT NULL DEFAULT false | |
submitted | boolean | NOT NULL DEFAULT false | |
end_date | date | NOT NULL DEFAULT now() | |
updated | timestamp without time zone | NOT NULL DEFAULT now() | |
lsmb13.entity.id | entered_by | integer | NOT NULL DEFAULT person__get_my_entity_id() |
entered_username | text | NOT NULL DEFAULT "session_user"() | |
deleted | boolean | NOT NULL DEFAULT false | |
lsmb13.entity.id | deleted_by | integer | |
lsmb13.entity.id | approved_by | integer | |
approved_username | text | ||
recon_fx | boolean | DEFAULT false |
Name | Constraint |
---|---|
cr_report_check | CHECK (((deleted IS NOT TRUE) OR (approved IS NOT TRUE))) |
Tables referencing this one via Foreign Key Constraints:
F-Key | Name | Type | Description |
---|---|---|---|
id | bigserial | PRIMARY KEY | |
lsmb13.cr_report.id | report_id | integer | NOT NULL |
scn | text | ||
their_balance | numeric | ||
our_balance | numeric | ||
errorcode | integer | ||
lsmb13.entity.id | user | integer | NOT NULL |
clear_time | date | ||
insert_time | timestamp with time zone | NOT NULL DEFAULT now() | |
trans_type | text | ||
post_date | date | ||
lsmb13.acc_trans.entry_id lsmb13.acc_trans.entry_id | ledger_id | integer | |
lsmb13.voucher.id | voucher_id | integer | |
overlook | boolean | NOT NULL DEFAULT false | |
cleared | boolean | NOT NULL DEFAULT false |
Deprecated, use only with old code.
F-Key | Name | Type | Description |
---|---|---|---|
field_id | serial | PRIMARY KEY | |
lsmb13.custom_table_catalog.table_id | table_id | integer | |
field_name | text |
Deprecated, use only with old code.
F-Key | Name | Type | Description |
---|---|---|---|
table_id | serial | PRIMARY KEY | |
extends | text | ||
table_name | text |
Tables referencing this one via Foreign Key Constraints:
Mapping customer to taxes.
F-Key | Name | Type | Description |
---|---|---|---|
lsmb13.entity_credit_account.id | customer_id | integer | PRIMARY KEY |
lsmb13.account.id | chart_id | integer | PRIMARY KEY |
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.
F-Key | Name | Type | Description |
---|---|---|---|
setting_key | text | PRIMARY KEY | |
value | text |
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
description | text | ||
role | character(1) |
DEFAULT 'P'::bpchar
P for Profit Center, C for Cost Center |
Tables referencing this one via Foreign Key Constraints:
department_id_key idDepartment to Transaction Map
F-Key | Name | Type | Description |
---|---|---|---|
trans_id | integer | PRIMARY KEY | |
department_id | integer |
Notes for entity_credit_account entries.
F-Key | Name | Type | Description |
---|---|---|---|
id | integer | PRIMARY KEY DEFAULT nextval('note_id_seq'::regclass) | |
note_class | integer | NOT NULL | |
note | text | NOT NULL | |
vector | tsvector | NOT NULL DEFAULT ''::pg_catalog.tsvector | |
created | timestamp without time zone | NOT NULL DEFAULT now() | |
created_by | text | DEFAULT "session_user"() | |
lsmb13.entity_credit_account.id | ref_key | integer |
NOT NULL
references entity_credit_account.id |
subject | text |
Table lsmb13.eca_note Inherits note,
Name | Constraint |
---|---|
eca_note_note_class_check | CHECK ((note_class = 3)) |
To keep track of the relationship between multiple contact methods and a single vendor or customer account. For generic contacts, use company_to_contact or person_to_contact instead.
F-Key | Name | Type | Description |
---|---|---|---|
lsmb13.entity_credit_account.id | credit_id | integer | PRIMARY KEY |
lsmb13.contact_class.id | contact_class_id | integer | PRIMARY KEY |
contact | text | PRIMARY KEY | |
description | text |
Name | Constraint |
---|---|
eca_to_contact_contact_check | CHECK ((contact ~ '[[:alnum:]_]'::text)) |
This table is used for locations bound to contracts. For generic contact addresses, use company_to_location instead
F-Key | Name | Type | Description |
---|---|---|---|
lsmb13.location.id | location_id | integer | PRIMARY KEY |
lsmb13.location_class.id | location_class | integer | PRIMARY KEY |
lsmb13.entity_credit_account.id | credit_id | integer | PRIMARY KEY |
F-Key | Name | Type | Description |
---|---|---|---|
entity_id | integer | ||
startdate | date | ||
enddate | date | ||
role | character varying(20) | ||
ssn | text | ||
sales | boolean | ||
manager_id | integer | ||
employeenumber | character varying(32) | ||
dob | date | ||
manager | text | ||
note | text | ||
name | text |
SELECT e.entity_id , e.startdate , e.enddate , e.role , e.ssn , e.sales , e.manager_id , e.employeenumber , e.dob , em.name AS manager , emn.note , en.name FROM ( ( ( (lsmb13.entity_employee e LEFT JOIN lsmb13.entity en ON ( (e.entity_id = en.id) ) ) LEFT JOIN lsmb13.entity_employee m ON ( (e.manager_id = m.entity_id) ) ) LEFT JOIN lsmb13.entity em ON ( (em.id = m.entity_id) ) ) LEFT JOIN lsmb13.entity_note emn ON ( (emn.ref_key = em.id) ) );
F-Key | Name | Type | Description |
---|---|---|---|
salutation | text | ||
first_name | text | ||
last_name | text | ||
entity_id | integer | ||
startdate | date | ||
enddate | date | ||
role | character varying(20) | ||
ssn | text | ||
sales | boolean | ||
manager_id | integer | ||
employeenumber | character varying(32) | ||
dob | date |
SELECT s.salutation , p.first_name , p.last_name , ee.entity_id , ee.startdate , ee.enddate , ee.role , ee.ssn , ee.sales , ee.manager_id , ee.employeenumber , ee.dob FROM ( (lsmb13.person p JOIN lsmb13.entity_employee ee USING (entity_id) ) LEFT JOIN lsmb13.salutation s ON ( (p.salutation_id = s.id) ) );
The primary entity table to map to all contacts
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | UNIQUE NOT NULL | |
name | text |
This is the common name of an entity. If it was a person it may be Joshua Drake, a company Acme Corp. You may also choose to use a domain such as commandprompt.com |
|
lsmb13.entity_class.id lsmb13.entity_class.id | entity_class | integer | PRIMARY KEY |
created | date | NOT NULL DEFAULT ('now'::text)::date | |
control_code | text | UNIQUE PRIMARY KEY DEFAULT setting_increment('entity_control'::character varying) | |
lsmb13.country.id | country_id | integer | NOT NULL |
Name | Constraint |
---|---|
entity_name_check | CHECK ((name ~ '[[:alnum:]_]'::text)) |
Tables referencing this one via Foreign Key Constraints:
entity_name_gist_idx name lsmb13.gist_trgm_opsThis stores bank account information for both companies and persons.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | UNIQUE NOT NULL | |
lsmb13.entity.id | entity_id | integer | NOT NULL |
bic | character varying |
UNIQUE#1
Banking Institution Code, such as routing number of SWIFT code. |
|
iban | character varying |
UNIQUE#1
NOT NULL
International Bank Account Number. used to store the actual account number for the banking institution. |
Tables referencing this one via Foreign Key Constraints:
Defines the class type such as vendor, customer, contact, employee
F-Key | Name | Type | Description |
---|---|---|---|
id | serial |
PRIMARY KEY
The first 7 values are reserved and permanent. Individuals who create new classes, however, should coordinate with others for ranges to use. |
|
class | text | NOT NULL | |
lsmb13.country.id | country_id | integer | |
active | boolean | NOT NULL DEFAULT true |
Name | Constraint |
---|---|
entity_class_class_check | CHECK ((class ~ '[[:alnum:]_]'::text)) |
Tables referencing this one via Foreign Key Constraints:
entity_class_idx lower(class)Relation builder for classes to entity
F-Key | Name | Type | Description |
---|---|---|---|
lsmb13.entity_class.id | entity_class_id | integer | PRIMARY KEY |
lsmb13.entity.id | entity_id | integer | PRIMARY KEY |
This table stores informmation relating to general relationships regarding moneys owed on invoice. Invoices, whether AR or AP, must be attached to a record in this table.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | UNIQUE NOT NULL | |
lsmb13.entity.id | entity_id | integer | PRIMARY KEY |
lsmb13.entity_class.id | entity_class | integer | PRIMARY KEY |
pay_to_name | text | ||
discount | numeric | ||
description | text | ||
discount_terms | integer | ||
lsmb13.account.id | discount_account_id | integer | |
taxincluded | boolean | DEFAULT false | |
creditlimit | numeric | ||
terms | smallint | ||
meta_number | character varying(32) |
PRIMARY KEY
This stores the human readable control code for the customer/vendor record. This is typically called the customer/vendor "account" in the application. |
|
business_id | integer | ||
lsmb13.language.code | language_code | character varying(6) | |
lsmb13.pricegroup.id | pricegroup_id | integer | |
curr | character(3) | ||
startdate | date | DEFAULT ('now'::text)::date | |
enddate | date | ||
threshold | numeric | ||
lsmb13.entity_employee.entity_id | employee_id | integer | |
lsmb13.person.id | primary_contact | integer | |
lsmb13.account.id | ar_ap_account_id | integer | |
lsmb13.account.id | cash_account_id | integer | |
lsmb13.entity_bank_account.id | bank_account | integer | |
lsmb13.country_tax_form.id | taxform_id | integer |
Name | Constraint |
---|---|
entity_credit_account_check | CHECK (((ar_ap_account_id IS NOT NULL) OR (entity_id = 0))) |
entity_credit_account_entity_class_check | CHECK ((entity_class = ANY (ARRAY[1, 2]))) |
Tables referencing this one via Foreign Key Constraints:
This contains employee-specific extensions to person/entity.
F-Key | Name | Type | Description |
---|---|---|---|
lsmb13.entity.id | entity_id | integer | PRIMARY KEY |
startdate | date | NOT NULL DEFAULT ('now'::text)::date | |
enddate | date | ||
role | character varying(20) | ||
ssn | text | ||
sales | boolean | DEFAULT false | |
lsmb13.entity.id | manager_id | integer | |
employeenumber | character varying(32) | ||
dob | date |
Tables referencing this one via Foreign Key Constraints:
F-Key | Name | Type | Description |
---|---|---|---|
id | integer | PRIMARY KEY DEFAULT nextval('note_id_seq'::regclass) | |
note_class | integer | NOT NULL | |
note | text | NOT NULL | |
vector | tsvector | NOT NULL DEFAULT ''::pg_catalog.tsvector | |
created | timestamp without time zone | NOT NULL DEFAULT now() | |
created_by | text | DEFAULT "session_user"() | |
lsmb13.entity.id | ref_key | integer | NOT NULL |
subject | text | ||
lsmb13.entity.id | entity_id | integer |
Table lsmb13.entity_note Inherits note,
Name | Constraint |
---|---|
entity_note_note_class_check | CHECK ((note_class = 1)) |
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.
F-Key | Name | Type | Description |
---|---|---|---|
lsmb13.entity.id | entity_id | integer | PRIMARY KEY |
other_name | text | PRIMARY KEY |
Name | Constraint |
---|---|
entity_other_name_other_name_check | CHECK ((other_name ~ '[[:alnum:]_]'::text)) |
F-Key | Name | Type | Description |
---|---|---|---|
curr | character(3) | PRIMARY KEY | |
transdate | date | PRIMARY KEY | |
buy | numeric | ||
sell | numeric |
Abstract table, holds no records. Inheriting table store actual file attachment data. Can be queried however to retrieve lists of all files.
F-Key | Name | Type | Description |
---|---|---|---|
content | bytea | NOT NULL | |
lsmb13.mime_type.id | mime_type_id | integer | NOT NULL |
file_name | text | PRIMARY KEY | |
description | text | ||
lsmb13.entity.id | uploaded_by | integer | NOT NULL |
uploaded_at | timestamp without time zone | NOT NULL DEFAULT now() | |
id | serial | UNIQUE NOT NULL | |
ref_key | integer |
PRIMARY KEY
This column inheriting tables is used to reference the database row for the attachment. Inheriting tables MUST set the foreign key here appropriately. This can also be used to create classifications of other documents, such as by source of automatic import (where the file is not yet attached) or even standard, long-lived documents. |
|
lsmb13.file_class.id | file_class | integer | PRIMARY KEY |
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.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | UNIQUE NOT NULL | |
class | text | PRIMARY KEY |
Tables referencing this one via Foreign Key Constraints:
F-Key | Name | Type | Description |
---|---|---|---|
file_id | integer | ||
ref_key | integer | ||
reference | text | ||
type | text | ||
dest_class | integer | ||
source_class | integer | ||
dest_ref | integer |
SELECT file_tx_links.file_id , file_tx_links.ref_key , file_tx_links.reference , file_tx_links.type , file_tx_links.dest_class , file_tx_links.source_class , file_tx_links.dest_ref FROM lsmb13.file_tx_links UNIONSELECT file_order_links.file_id , file_order_links.ref_key , file_order_links.reference , file_order_links.oe_class AS type , file_order_links.dest_class , file_order_links.source_class , file_order_links.dest_ref FROM lsmb13.file_order_links;
F-Key | Name | Type | Description |
---|---|---|---|
content | bytea | NOT NULL | |
mime_type_id | integer | NOT NULL | |
file_name | text | PRIMARY KEY | |
description | text | ||
uploaded_by | integer | NOT NULL | |
uploaded_at | timestamp without time zone | NOT NULL DEFAULT now() | |
id | integer | UNIQUE NOT NULL DEFAULT nextval('file_base_id_seq'::regclass) | |
lsmb13.oe.id | ref_key | integer | PRIMARY KEY |
file_class | integer | PRIMARY KEY |
Table lsmb13.file_order Inherits file_base,
Name | Constraint |
---|---|
file_order_file_class_check | CHECK ((file_class = 2)) |
Tables referencing this one via Foreign Key Constraints:
F-Key | Name | Type | Description |
---|---|---|---|
file_id | integer | ||
ref_key | integer | ||
reference | text | ||
oe_class | text | ||
dest_class | integer | ||
source_class | integer | ||
dest_ref | integer |
SELECT sl.file_id , sl.ref_key , oe.ordnumber AS reference , oc.oe_class , sl.dest_class , sl.source_class , sl.ref_key AS dest_ref FROM ( (lsmb13.file_secondary_attachment sl JOIN lsmb13.oe ON ( (sl.ref_key = oe.id) ) ) JOIN lsmb13.oe_class oc ON ( (oe.oe_class_id = oc.id) ) ) WHERE (sl.source_class = 2);
Secondary links from one order to another, for example to support order consolidation.
F-Key | Name | Type | Description |
---|---|---|---|
lsmb13.file_order.id | file_id | integer | PRIMARY KEY |
source_class | integer | PRIMARY KEY | |
lsmb13.oe.id | ref_key | integer | PRIMARY KEY |
dest_class | integer | PRIMARY KEY | |
attached_by | integer | NOT NULL | |
attached_at | timestamp without time zone | NOT NULL DEFAULT now() |
Table lsmb13.file_order_to_order Inherits file_secondary_attachment,
Name | Constraint |
---|---|
file_order_to_order_dest_class_check | CHECK ((dest_class = 2)) |
file_order_to_order_source_class_check | CHECK ((source_class = 2)) |
Secondary links from orders to transactions, for example to track files when invoices are generated from orders.
F-Key | Name | Type | Description |
---|---|---|---|
lsmb13.file_order.id | file_id | integer | PRIMARY KEY |
source_class | integer | PRIMARY KEY | |
lsmb13.transactions.id | ref_key | integer | PRIMARY KEY |
dest_class | integer | PRIMARY KEY | |
attached_by | integer | NOT NULL | |
attached_at | timestamp without time zone | NOT NULL DEFAULT now() |
Table lsmb13.file_order_to_tx Inherits file_secondary_attachment,
Name | Constraint |
---|---|
file_order_to_tx_dest_class_check | CHECK ((dest_class = 1)) |
file_order_to_tx_source_class_check | CHECK ((source_class = 2)) |
File attachments primarily attached to orders and quotations.
F-Key | Name | Type | Description |
---|---|---|---|
content | bytea | NOT NULL | |
mime_type_id | integer | NOT NULL | |
file_name | text | PRIMARY KEY | |
description | text | ||
uploaded_by | integer | NOT NULL | |
uploaded_at | timestamp without time zone | NOT NULL DEFAULT now() | |
id | integer | UNIQUE NOT NULL DEFAULT nextval('file_base_id_seq'::regclass) | |
lsmb13.parts.id | ref_key | integer | PRIMARY KEY |
file_class | integer | PRIMARY KEY |
Table lsmb13.file_part Inherits file_base,
Name | Constraint |
---|---|
file_part_file_class_check | CHECK ((file_class = 3)) |
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.
F-Key | Name | Type | Description |
---|---|---|---|
file_id | integer | PRIMARY KEY | |
lsmb13.file_class.id | source_class | integer | PRIMARY KEY |
ref_key | integer | PRIMARY KEY | |
lsmb13.file_class.id | dest_class | integer | PRIMARY KEY |
lsmb13.entity.id | attached_by | integer | NOT NULL |
attached_at | timestamp without time zone | NOT NULL DEFAULT now() |
File attachments primarily attached to orders and quotatoins.
F-Key | Name | Type | Description |
---|---|---|---|
content | bytea | NOT NULL | |
mime_type_id | integer | NOT NULL | |
file_name | text | PRIMARY KEY | |
description | text | ||
uploaded_by | integer | NOT NULL | |
uploaded_at | timestamp without time zone | NOT NULL DEFAULT now() | |
id | integer | UNIQUE NOT NULL DEFAULT nextval('file_base_id_seq'::regclass) | |
lsmb13.transactions.id | ref_key | integer | PRIMARY KEY |
file_class | integer | PRIMARY KEY |
Table lsmb13.file_transaction Inherits file_base,
Name | Constraint |
---|---|
file_transaction_file_class_check | CHECK ((file_class = 1)) |
Tables referencing this one via Foreign Key Constraints:
F-Key | Name | Type | Description |
---|---|---|---|
file_id | integer | ||
ref_key | integer | ||
reference | text | ||
type | text | ||
dest_class | integer | ||
source_class | integer | ||
dest_ref | integer |
SELECT sl.file_id , sl.ref_key , gl.reference , gl.type , sl.dest_class , sl.source_class , sl.ref_key AS dest_ref FROM (lsmb13.file_secondary_attachment sl JOIN ( ( SELECT gl.id , gl.reference ,'gl'::text AS type FROM lsmb13.gl UNIONSELECT ar.id , ar.invnumber , CASE WHEN ar.invoice THEN 'is'::text ELSE 'ar'::text END AS type FROM lsmb13.ar ) UNIONSELECT ap.id , ap.invnumber , CASE WHEN ap.invoice THEN 'ir'::text ELSE 'ap'::text END AS type FROM lsmb13.ap ) gl ON ( ( (sl.ref_key = gl.id) AND (sl.source_class = 1) ) ) );
Secondary links from transactions to orders.
F-Key | Name | Type | Description |
---|---|---|---|
lsmb13.file_transaction.id | file_id | integer | PRIMARY KEY |
source_class | integer | PRIMARY KEY | |
lsmb13.oe.id | ref_key | integer | PRIMARY KEY |
dest_class | integer | PRIMARY KEY | |
attached_by | integer | NOT NULL | |
attached_at | timestamp without time zone | NOT NULL DEFAULT now() |
Table lsmb13.file_tx_to_order Inherits file_secondary_attachment,
Name | Constraint |
---|---|
file_tx_to_order_dest_class_check | CHECK ((dest_class = 2)) |
file_tx_to_order_source_class_check | CHECK ((source_class = 1)) |
F-Key | Name | Type | Description |
---|---|---|---|
lsmb13.file_class.id | file_class | integer | PRIMARY KEY |
view_name | text | UNIQUE NOT NULL |
GIFI labels for accounts, used in Canada and some EU countries for tax reporting
F-Key | Name | Type | Description |
---|---|---|---|
accno | text | PRIMARY KEY | |
description | text |
This table holds summary information for entries in the general journal. Does not hold summary information in 1.3 for AR or AP entries.
F-Key | Name | Type | Description |
---|---|---|---|
lsmb13.transactions.id | id | integer | PRIMARY KEY DEFAULT nextval('id'::regclass) |
reference | text | ||
description | text | ||
transdate | date | DEFAULT ('now'::text)::date | |
lsmb13.person.id | person_id | integer |
the person_id of the employee who created the entry. |
notes | text | ||
approved | boolean | DEFAULT true | |
department_id | integer |
Tables referencing this one via Foreign Key Constraints:
gl_approved_idx approved gl_description_key lower(description) gl_id_key id gl_reference_key reference gl_transdate_key transdateThis table contains inventory mappings to warehouses, not general inventory management data.
F-Key | Name | Type | Description |
---|---|---|---|
lsmb13.entity_employee.entity_id | entity_id | integer | |
warehouse_id | integer | ||
parts_id | integer | ||
trans_id | integer | ||
orderitems_id | integer | ||
qty | numeric | ||
shippingdate | date | ||
entry_id | serial | PRIMARY KEY |
Line items of invoices with goods/services attached.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
lsmb13.transactions.id | trans_id | integer | |
lsmb13.parts.id | parts_id | integer | |
description | text | ||
qty | numeric |
Positive is normal for sales invoices, negative for vendor invoices. |
|
allocated | integer |
Number of allocated items, negative relative to qty. When qty + allocated = 0, then the item is fully used for purposes of COGS calculations. |
|
sellprice | numeric | ||
precision | integer | ||
fxsellprice | numeric | ||
discount | numeric | ||
assemblyitem | boolean | DEFAULT false | |
unit | character varying(5) | ||
project_id | integer | ||
deliverydate | date | ||
serialnumber | text | ||
notes | text |
Tables referencing this one via Foreign Key Constraints:
invoice_id_key id invoice_trans_id_key trans_idF-Key | Name | Type | Description |
---|---|---|---|
id | integer | PRIMARY KEY DEFAULT nextval('note_id_seq'::regclass) | |
note_class | integer | NOT NULL | |
note | text | NOT NULL | |
vector | tsvector | NOT NULL DEFAULT ''::pg_catalog.tsvector | |
created | timestamp without time zone | NOT NULL DEFAULT now() | |
created_by | text | DEFAULT "session_user"() | |
lsmb13.invoice.id | ref_key | integer | NOT NULL |
subject | text |
Table lsmb13.invoice_note Inherits note,
invoice_note_id_idx id invoice_note_vectors_idx vectorMaping invoice to country_tax_form.
F-Key | Name | Type | Description |
---|---|---|---|
lsmb13.invoice.id | invoice_id | integer | PRIMARY KEY |
reportable | boolean |
Time and materials cards. Materials cards not implemented.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
project_id | integer | ||
parts_id | integer | ||
description | text | ||
qty | numeric | ||
allocated | numeric | ||
sellprice | numeric | ||
fxsellprice | numeric | ||
serialnumber | text | ||
checkedin | timestamp with time zone | ||
checkedout | timestamp with time zone | ||
lsmb13.person.id | person_id | integer | NOT NULL |
notes | text | ||
total | numeric | NOT NULL | |
non_billable | numeric | NOT NULL |
Languages for manual translations and so forth.
F-Key | Name | Type | Description |
---|---|---|---|
code | character varying(6) | PRIMARY KEY | |
description | text |
Tables referencing this one via Foreign Key Constraints:
This table stores addresses, such as shipto and bill to addresses.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
line_one | text | NOT NULL | |
line_two | text | ||
line_three | text | ||
city | text | NOT NULL | |
state | text | ||
lsmb13.country.id | country_id | integer | NOT NULL |
mail_code | text | NOT NULL | |
created | date | NOT NULL DEFAULT now() | |
inactive_date | timestamp without time zone | ||
active | boolean | NOT NULL DEFAULT true |
Name | Constraint |
---|---|
location_city_check | CHECK ((city ~ '[[:alnum:]_]'::text)) |
location_line_one_check | CHECK ((line_one ~ '[[:alnum:]_]'::text)) |
location_mail_code_check | CHECK ((mail_code ~ '[[:alnum:]_]'::text)) |
location_state_check | CHECK ((state ~ '[[:alnum:]_]'::text)) |
Tables referencing this one via Foreign Key Constraints:
location_address_one_gist__idx line_one lsmb13.gist_trgm_ops location_address_three_gist__idx line_three lsmb13.gist_trgm_ops location_address_two_gist__idx line_two lsmb13.gist_trgm_ops location_city_prov_gist_idx city lsmb13.gist_trgm_opsIndividuals seeking to add new location classes should coordinate with others.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | UNIQUE NOT NULL | |
class | text | PRIMARY KEY | |
authoritative | boolean | PRIMARY KEY |
Name | Constraint |
---|---|
location_class_class_check | CHECK ((class ~ '[[:alnum:]_]'::text)) |
Tables referencing this one via Foreign Key Constraints:
F-Key | Name | Type | Description |
---|---|---|---|
role_name | text | PRIMARY KEY |
Tables referencing this one via Foreign Key Constraints:
F-Key | Name | Type | Description |
---|---|---|---|
lsmb13.lsmb_group.role_name | group_name | text | PRIMARY KEY |
granted_role | text | PRIMARY KEY |
A beginning of a group tracking system. Not exposed through the front end yet.
F-Key | Name | Type | Description |
---|---|---|---|
lsmb13.users.id | user_id | integer | NOT NULL |
role | text | NOT NULL |
A single parts entry can have multiple make/model entries. These store manufacturer/model number info.
F-Key | Name | Type | Description |
---|---|---|---|
parts_id | integer | PRIMARY KEY | |
make | text | PRIMARY KEY | |
model | text | PRIMARY KEY |
Provides access control list entries for menu nodes.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | NOT NULL | |
role_name | character varying | PRIMARY KEY | |
acl_type | character varying |
Nodes are hidden unless a role is found of which the user is a member, and where the acl_type for that role type and node is set to 'allow' and no acl is found for any role of which the user is a member, where the acl_type is set to 'deny'. |
|
lsmb13.menu_node.id | node_id | integer | PRIMARY KEY |
Name | Constraint |
---|---|
menu_acl_acl_type_check | CHECK ((((acl_type)::text = 'allow'::text) OR ((acl_type)::text = 'deny'::text))) |
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.
F-Key | Name | Type | Description |
---|---|---|---|
lsmb13.menu_node.id | node_id | integer | PRIMARY KEY |
attribute | character varying | PRIMARY KEY | |
value | character varying | NOT NULL | |
id | serial | NOT NULL |
A nice human-readable view for investigating the menu tree. Does not show menu attributes or acls.
F-Key | Name | Type | Description |
---|---|---|---|
level | integer | ||
path | text | ||
label | text | ||
id | integer | ||
position | integer |
WITH RECURSIVE tree (path , id , parent , level , positions ) AS ( SELECT (menu_node.id)::text AS path , menu_node.id , menu_node.parent , 0 AS level , (menu_node."position")::text AS "position" FROM lsmb13.menu_node WHERE (menu_node.parent IS NULL) UNIONSELECT ( (t.path || ','::text ) || (n.id)::text ) , n.id , n.parent , (t.level + 1) , ( (t.positions || ','::text ) || n."position" ) FROM (lsmb13.menu_node n JOIN tree t ON ( (t.id = n.parent) ) ) ) SELECT t.level , t.path , (repeat (' '::text , (2 * t.level) ) || (n.label)::text ) AS label , n.id , n."position" FROM (tree t JOIN lsmb13.menu_node n USING (id) ) ORDER BY (string_to_array (t.positions ,','::text ) )::integer[];
This table stores the tree structure of the menu.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
label | character varying | NOT NULL | |
lsmb13.menu_node.id | parent | integer | UNIQUE#1 |
position | integer | UNIQUE#1 NOT NULL |
Tables referencing this one via Foreign Key Constraints:
This is a lookup table for storing MIME types.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | UNIQUE NOT NULL | |
mime_type | text | PRIMARY KEY | |
invoice_include | boolean | DEFAULT false |
Tables referencing this one via Foreign Key Constraints:
Tracks ship_to information for orders and invoices.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
lsmb13.transactions.id | trans_id | integer | |
lsmb13.oe.id | oe_id | integer | |
lsmb13.location.id | location_id | integer |
This is an abstract table which should have zero rows. It is inherited by other tables for specific notes.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
lsmb13.note_class.id | note_class | integer | NOT NULL |
note | text |
NOT NULL
Body of note. |
|
vector | tsvector |
NOT NULL
DEFAULT ''::pg_catalog.tsvector
tsvector for full text indexing, requires both setting up tsearch dictionaries and adding triggers to use at present. |
|
created | timestamp without time zone | NOT NULL DEFAULT now() | |
created_by | text | DEFAULT "session_user"() | |
ref_key | integer |
NOT NULL
Subclassed tables use this column as a foreign key against the table storing the record a note is attached to. |
|
subject | text |
Coordinate with others before adding entries.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
class | text | NOT NULL |
Name | Constraint |
---|---|
note_class_class_check | CHECK ((class ~ '[[:alnum:]_]'::text)) |
Tables referencing this one via Foreign Key Constraints:
Header information for: * Sales orders * Purchase Orders * Quotations * Requests for Quotation
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
ordnumber | text | ||
transdate | date | DEFAULT ('now'::text)::date | |
lsmb13.entity.id | entity_id | integer | |
amount | numeric | ||
netamount | numeric | ||
reqdate | date | ||
taxincluded | boolean | ||
shippingpoint | text | ||
notes | text | ||
curr | character(3) | ||
lsmb13.person.id | person_id | integer | |
closed | boolean | DEFAULT false | |
quotation | boolean | DEFAULT false | |
quonumber | text | ||
intnotes | text | ||
department_id | integer | ||
shipvia | text | ||
language_code | character varying(6) | ||
ponumber | text | ||
terms | smallint | ||
lsmb13.entity_credit_account.id | entity_credit_account | integer | NOT NULL |
lsmb13.oe_class.id | oe_class_id | integer | NOT NULL |
Tables referencing this one via Foreign Key Constraints:
oe_id_key id oe_ordnumber_key ordnumber oe_transdate_key transdateHardwired classifications for orders and quotations. Coordinate before adding.
F-Key | Name | Type | Description |
---|---|---|---|
id | smallint | UNIQUE | |
oe_class | text | PRIMARY KEY |
Name | Constraint |
---|---|
oe_class_id_check | CHECK ((id = ANY (ARRAY[1, 2, 3, 4]))) |
Tables referencing this one via Foreign Key Constraints:
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.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
lsmb13.session.session_id | session_id | integer |
Line items for sales/purchase orders and quotations.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
trans_id | integer | ||
parts_id | integer | ||
description | text | ||
qty | numeric | ||
sellprice | numeric | ||
precision | integer | ||
discount | numeric | ||
unit | character varying(5) | ||
project_id | integer | ||
reqdate | date | ||
ship | numeric | ||
serialnumber | text | ||
notes | text |
F-Key | Name | Type | Description |
---|---|---|---|
payment_id | integer | ||
payment_reference | text | ||
payment_class | integer | ||
payment_closed | boolean | ||
payment_date | date | ||
chart_id | integer | ||
accno | text | ||
chart_description | text | ||
available | numeric | ||
legal_name | text | ||
entity_credit_id | integer | ||
entity_id | integer | ||
discount | numeric | ||
meta_number | character varying(32) |
SELECT p.id AS payment_id , p.reference AS payment_reference , p.payment_class , p.closed AS payment_closed , p.payment_date , ac.chart_id , c.accno , c.description AS chart_description , abs (sum (ac.amount) ) AS available , cmp.legal_name , eca.id AS entity_credit_id , eca.entity_id , eca.discount , eca.meta_number FROM ( ( ( ( (lsmb13.payment p JOIN lsmb13.payment_links pl ON ( (pl.payment_id = p.id) ) ) JOIN lsmb13.acc_trans ac ON ( (ac.entry_id = pl.entry_id) ) ) JOIN lsmb13.chart c ON ( (c.id = ac.chart_id) ) ) JOIN lsmb13.entity_credit_account eca ON ( (eca.id = p.entity_credit_id) ) ) JOIN lsmb13.company cmp ON ( (cmp.entity_id = eca.entity_id) ) ) WHERE ( ( (p.gl_id IS NOT NULL) AND ( (pl.type = 2) OR (pl.type = 0) ) ) AND (c.link ~~ '%overpayment%'::text) ) GROUP BY p.id , c.accno , p.reference , p.payment_class , p.closed , p.payment_date , ac.chart_id , c.description , cmp.legal_name , eca.id , eca.entity_id , eca.discount , eca.meta_number;
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.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
partnumber | text | ||
description | text | ||
unit | character varying(5) | ||
listprice | numeric | ||
sellprice | numeric | ||
lastcost | numeric | ||
priceupdate | date | DEFAULT ('now'::text)::date | |
weight | numeric | ||
onhand | numeric | ||
notes | text | ||
makemodel | boolean | DEFAULT false | |
assembly | boolean | DEFAULT false | |
alternate | boolean | DEFAULT false | |
rop | numeric |
Re-order point. Used to select parts for short inventory report. |
|
inventory_accno_id | integer | ||
income_accno_id | integer | ||
expense_accno_id | integer | ||
bin | text |
Text identifier for where a part is stored. |
|
obsolete | boolean | DEFAULT false | |
bom | boolean |
DEFAULT false
Show on Bill of Materials. |
|
image | text |
Hyperlink to product image. |
|
drawing | text | ||
microfiche | text | ||
partsgroup_id | integer | ||
project_id | integer | ||
avgcost | numeric |
Tables referencing this one via Foreign Key Constraints:
parts_description_key lower(description) parts_id_key id parts_partnumber_key lower(partnumber)Translation information for parts.
F-Key | Name | Type | Description |
---|---|---|---|
lsmb13.parts.id | trans_id | integer | PRIMARY KEY |
language_code | character varying(6) | PRIMARY KEY | |
description | text |
Table lsmb13.parts_translation Inherits translation,
Tracks per-customer pricing. Discounts can be offered for periods of time and for pricegroups as well as per customer
F-Key | Name | Type | Description |
---|---|---|---|
parts_id | integer | NOT NULL | |
lsmb13.entity_credit_account.id | credit_id | integer | |
lsmb13.pricegroup.id | pricegroup_id | integer | |
pricebreak | numeric | ||
sellprice | numeric | NOT NULL | |
validfrom | date | ||
validto | date | ||
curr | character(3) | ||
entry_id | serial | PRIMARY KEY |
Groups of parts for Point of Sale screen.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
partsgroup | text |
Tables referencing this one via Foreign Key Constraints:
partsgroup_id_key idTranslation information for partsgroups.
F-Key | Name | Type | Description |
---|---|---|---|
lsmb13.partsgroup.id | trans_id | integer | PRIMARY KEY |
language_code | character varying(6) | PRIMARY KEY | |
description | text |
Table lsmb13.partsgroup_translation Inherits translation,
Mapping of parts to taxes.
F-Key | Name | Type | Description |
---|---|---|---|
lsmb13.parts.id | parts_id | integer | PRIMARY KEY |
lsmb13.account.id | chart_id | integer | PRIMARY KEY |
lsmb13.taxcategory.taxcategory_id | taxcategory_id | integer |
Tracks vendor's pricing, as well as vendor's part number, lead time required and currency.
F-Key | Name | Type | Description |
---|---|---|---|
lsmb13.entity_credit_account.id | credit_id | integer | NOT NULL |
parts_id | integer | ||
partnumber | text | ||
leadtime | smallint | ||
lastcost | numeric | ||
curr | character(3) | ||
entry_id | serial | PRIMARY KEY |
This table will store the main data on a payment, prepayment, overpayment, et
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
reference | text |
NOT NULL
This field will store the code for both receipts and payment order |
|
lsmb13.gl.id | gl_id | integer |
A payment should always be linked to a GL movement |
payment_class | integer | NOT NULL | |
payment_date | date | DEFAULT ('now'::text)::date | |
closed | boolean |
DEFAULT false
This will store the current state of a payment/receipt order |
|
lsmb13.entity_credit_account.id | entity_credit_id | integer | |
lsmb13.person.id | employee_id | integer | |
currency | character(3) | ||
notes | text | ||
department_id | integer |
Tables referencing this one via Foreign Key Constraints:
payment_id_idx idAn 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.
F-Key | Name | Type | Description |
---|---|---|---|
lsmb13.payment.id | payment_id | integer | |
lsmb13.acc_trans.entry_id | entry_id | integer | |
type | integer |
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | UNIQUE NOT NULL | |
label | text | PRIMARY KEY |
This is a holding table and hence not a candidate for normalization. Jobs should be deleted from this table when they complete successfully.
F-Key | Name | Type | Description |
---|---|---|---|
transactions | numeric[] | ||
batch_id | integer | ||
source | text | ||
total | numeric | ||
ar_ap_accno | text | ||
cash_accno | text | ||
payment_date | date | ||
account_class | integer | ||
lsmb13.pending_job.id | job_id | integer | DEFAULT currval('pending_job_id_seq'::regclass) |
Purpose: This table stores pending/queued jobs to be processed async. Additionally, this functions as a log of all such processing for purposes of internal audits, performance tuning, and the like.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | UNIQUE NOT NULL | |
lsmb13.batch_class.id | batch_class | integer | |
lsmb13.users.username | entered_by | text | NOT NULL DEFAULT "session_user"() |
entered_at | timestamp without time zone | DEFAULT now() | |
lsmb13.batch.id | batch_id | integer | |
completed_at | timestamp without time zone | ||
success | boolean | ||
error_condition | text |
Name | Constraint |
---|---|
pending_job_check | CHECK (((completed_at IS NULL) OR (success IS NOT NULL))) |
pending_job_check1 | CHECK (((success IS NOT FALSE) OR (error_condition IS NOT NULL))) |
Tables referencing this one via Foreign Key Constraints:
pending_job_batch_id_pending batch_id) WHERE (success IS NULL pending_job_entered_by entered_byF-Key | Name | Type | Description |
---|---|---|---|
id | text | ||
label | text | ||
date_to | date | ||
date_from | date |
SELECT'ytd'::text AS id ,'Year to Date'::text AS label , (now () )::date AS date_to , ( ( (date_part ('year'::text , now () ) )::text || '-01-01'::text ) )::date AS date_from UNIONSELECT'last_year'::text AS id ,'Last Year'::text AS label , ( ( ( (date_part ('YEAR'::text , now () ) - (1)::double precision ) )::text || '-12-31'::text ) )::date AS date_to , ( ( ( (date_part ('YEAR'::text , now () ) - (1)::double precision ) )::text || '-01-01'::text ) )::date AS date_from;
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.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
lsmb13.entity.id | entity_id | integer | UNIQUE NOT NULL |
lsmb13.salutation.id | salutation_id | integer | |
first_name | text | NOT NULL | |
middle_name | text | ||
last_name | text | NOT NULL | |
created | date | NOT NULL DEFAULT ('now'::text)::date |
Name | Constraint |
---|---|
person_first_name_check | CHECK ((first_name ~ '[[:alnum:]_]'::text)) |
person_last_name_check | CHECK ((last_name ~ '[[:alnum:]_]'::text)) |
Tables referencing this one via Foreign Key Constraints:
currently unused in the front-end, but can be used to map persons to companies.
F-Key | Name | Type | Description |
---|---|---|---|
lsmb13.location.id | location_id | integer | PRIMARY KEY |
lsmb13.person.id | person_id | integer | PRIMARY KEY |
lsmb13.company.id | company_id | integer | NOT NULL |
This table stores contact information for companies
F-Key | Name | Type | Description |
---|---|---|---|
lsmb13.person.id | person_id | integer | PRIMARY KEY |
lsmb13.contact_class.id | contact_class_id | integer | PRIMARY KEY |
contact | text | PRIMARY KEY | |
description | text |
Name | Constraint |
---|---|
person_to_contact_contact_check | CHECK ((contact ~ '[[:alnum:]_]'::text)) |
This provides a map so that entities can also be used like groups.
F-Key | Name | Type | Description |
---|---|---|---|
lsmb13.person.id | person_id | integer | PRIMARY KEY |
lsmb13.entity.id | entity_id | integer | PRIMARY KEY |
related_how | text | ||
created | date | NOT NULL DEFAULT ('now'::text)::date |
Name | Constraint |
---|---|
person_to_entity_check | CHECK ((entity_id <> person_id)) |
F-Key | Name | Type | Description |
---|---|---|---|
lsmb13.location.id | location_id | integer | PRIMARY KEY |
lsmb13.location_class.id | location_class | integer | NOT NULL |
lsmb13.person.id | person_id | integer | PRIMARY KEY |
Pricegroups are groups of customers who are assigned prices and discounts together.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
pricegroup | text |
Tables referencing this one via Foreign Key Constraints:
pricegroup_id_key id pricegroup_pricegroup_key pricegroupF-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
projectnumber | text | ||
description | text | ||
startdate | date | ||
enddate | date | ||
parts_id | integer |
Job costing/manufacturing here not implemented. |
|
production | numeric | ||
completed | numeric | ||
lsmb13.entity_credit_account.id | credit_id | integer |
Tables referencing this one via Foreign Key Constraints:
project_id_key idTranslation information for projects.
F-Key | Name | Type | Description |
---|---|---|---|
lsmb13.project.id | trans_id | integer | PRIMARY KEY |
language_code | character varying(6) | PRIMARY KEY | |
description | text |
Table lsmb13.project_translation Inherits translation,
F-Key | Name | Type | Description |
---|---|---|---|
payee | text | ||
id | bigint | ||
report_id | integer | ||
scn | text | ||
their_balance | numeric | ||
our_balance | numeric | ||
errorcode | integer | ||
user | integer | ||
clear_time | date | ||
insert_time | timestamp with time zone | ||
trans_type | text | ||
post_date | date | ||
ledger_id | integer | ||
voucher_id | integer | ||
overlook | boolean | ||
cleared | boolean |
SELECT n.name AS payee , rr.id , rr.report_id , rr.scn , rr.their_balance , rr.our_balance , rr.errorcode , rr."user" , rr.clear_time , rr.insert_time , rr.trans_type , rr.post_date , rr.ledger_id , ac.voucher_id , rr.overlook , rr.cleared FROM ( ( (lsmb13.cr_report_line rr LEFT JOIN lsmb13.acc_trans ac ON ( (rr.ledger_id = ac.entry_id) ) ) LEFT JOIN lsmb13.gl ON ( (ac.trans_id = gl.id) ) ) LEFT JOIN ( ( SELECT ap.id , e.name FROM ( (lsmb13.ap JOIN lsmb13.entity_credit_account eca ON ( (ap.entity_credit_account = eca.id) ) ) JOIN lsmb13.entity e ON ( (eca.entity_id = e.id) ) ) UNIONSELECT ar.id , e.name FROM ( (lsmb13.ar JOIN lsmb13.entity_credit_account eca ON ( (ar.entity_credit_account = eca.id) ) ) JOIN lsmb13.entity e ON ( (eca.entity_id = e.id) ) ) ) UNIONSELECT gl.id , gl.description FROM lsmb13.gl ) n ON ( (n.id = ac.trans_id) ) );
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.
F-Key | Name | Type | Description |
---|---|---|---|
id | integer | PRIMARY KEY DEFAULT nextval('id'::regclass) | |
reference | text | ||
startdate | date | ||
nextdate | date | ||
enddate | date | ||
repeat | smallint | ||
unit | character varying(6) | ||
howmany | integer | ||
payment | boolean | DEFAULT false |
Email to be sent out when recurring transaction is posted.
F-Key | Name | Type | Description |
---|---|---|---|
id | integer | PRIMARY KEY | |
formname | text | PRIMARY KEY | |
format | text | ||
message | text |
Template, printer etc. to print to when recurring transaction posts.
F-Key | Name | Type | Description |
---|---|---|---|
id | integer | PRIMARY KEY | |
formname | text | PRIMARY KEY | |
format | text | ||
printer | text |
F-Key | Name | Type | Description |
---|---|---|---|
roleid | oid | ||
member | oid | ||
grantor | oid | ||
admin_option | boolean | ||
rolname | name | ||
rolsuper | boolean | ||
rolinherit | boolean | ||
rolcreaterole | boolean | ||
rolcreatedb | boolean | ||
rolcatupdate | boolean | ||
rolcanlogin | boolean | ||
rolconnlimit | integer | ||
rolpassword | text | ||
rolvaliduntil | timestamp with time zone | ||
rolconfig | text[] | ||
oid | oid |
SELECT m.roleid , m.member , m.grantor , m.admin_option , a.rolname , a.rolsuper , a.rolinherit , a.rolcreaterole , a.rolcreatedb , a.rolcatupdate , a.rolcanlogin , a.rolconnlimit , a.rolpassword , a.rolvaliduntil , a.rolconfig , a.oid FROM (pg_auth_members m JOIN pg_roles a ON ( (m.roleid = a.oid) ) );
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | UNIQUE NOT NULL | |
salutation | text | PRIMARY KEY |
Tables referencing this one via Foreign Key Constraints:
This table is used to track sessions on a database level across page requests. Because of the way LedgerSMB authentication works currently we do not time out authentication when the session times out. We do time out highly pessimistic locks used for large batch payment workflows.
F-Key | Name | Type | Description |
---|---|---|---|
session_id | serial | PRIMARY KEY | |
token | character varying(32) | ||
last_used | timestamp without time zone | DEFAULT now() | |
ttl | integer | NOT NULL DEFAULT 3600 | |
lsmb13.users.id | users_id | integer | NOT NULL |
notify_pasword | interval | NOT NULL DEFAULT '7 days'::interval |
Name | Constraint |
---|---|
session_token_check | CHECK ((length((token)::text) = 32)) |
Tables referencing this one via Foreign Key Constraints:
This can be used SIC codes or any equivalent, such as ISIC, NAICS, etc.
F-Key | Name | Type | Description |
---|---|---|---|
code | character varying(6) | PRIMARY KEY | |
sictype | character(1) | ||
description | text |
Whether AR/AP transactions and invoices have been emailed and/or printed
F-Key | Name | Type | Description |
---|---|---|---|
trans_id | integer | PRIMARY KEY | |
formname | text | PRIMARY KEY | |
printed | boolean | DEFAULT false | |
emailed | boolean | DEFAULT false | |
spoolfile | text |
Information on tax rates.
F-Key | Name | Type | Description |
---|---|---|---|
lsmb13.account.id lsmb13.account.id | chart_id | integer | PRIMARY KEY |
rate | numeric | ||
taxnumber | text | ||
validto | timestamp without time zone | PRIMARY KEY DEFAULT 'infinity'::timestamp without time zone | |
pass | integer |
NOT NULL
This is an integer indicating the pass of the tax. This is to support cumultative sales tax rules (for example, Quebec charging taxes on the federal taxes collected). |
|
lsmb13.taxmodule.taxmodule_id | taxmodule_id | integer | NOT NULL DEFAULT 1 |
minvalue | numeric | ||
maxvalue | numeric |
This stores extended information for manual tax calculations.
F-Key | Name | Type | Description |
---|---|---|---|
tax_basis | numeric | ||
rate | numeric | ||
lsmb13.acc_trans.entry_id | entry_id | integer | PRIMARY KEY |
F-Key | Name | Type | Description |
---|---|---|---|
taxcategory_id | serial | PRIMARY KEY | |
taxcategoryname | text | NOT NULL | |
lsmb13.taxmodule.taxmodule_id | taxmodule_id | integer | NOT NULL |
Tables referencing this one via Foreign Key Constraints:
This is used to store information on tax modules. the module name is used to determine the Perl class for the taxes.
F-Key | Name | Type | Description |
---|---|---|---|
taxmodule_id | serial | PRIMARY KEY | |
taxmodulename | text | NOT NULL |
Tables referencing this one via Foreign Key Constraints:
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.
F-Key | Name | Type | Description |
---|---|---|---|
id | integer | PRIMARY KEY | |
table_name | text | ||
lsmb13.session.session_id | locked_by | integer |
This should only be used in pessimistic locking measures as required by large batch work flows. |
lsmb13.entity.id | approved_by | integer | |
approved_at | timestamp without time zone |
Tables referencing this one via Foreign Key Constraints:
transactions_locked_by_i locked_byabstract table for manual translation data. Should have zero rows.
F-Key | Name | Type | Description |
---|---|---|---|
trans_id | integer | PRIMARY KEY | |
language_code | character varying(6) | PRIMARY KEY | |
description | text |
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
date_from | date | ||
date_to | date | ||
description | text | NOT NULL | |
lsmb13.trial_balance__yearend_types.type | yearend | text | NOT NULL |
Tables referencing this one via Foreign Key Constraints:
F-Key | Name | Type | Description |
---|---|---|---|
lsmb13.trial_balance.id | report_id | integer | NOT NULL |
lsmb13.account.id | account_id | integer | NOT NULL |
F-Key | Name | Type | Description |
---|---|---|---|
lsmb13.trial_balance.id | report_id | integer | NOT NULL |
lsmb13.account_heading.id | heading_id | integer | NOT NULL |
F-Key | Name | Type | Description |
---|---|---|---|
type | text | PRIMARY KEY |
Tables referencing this one via Foreign Key Constraints:
F-Key | Name | Type | Description |
---|---|---|---|
id | integer | ||
username | character varying(30) | ||
created | date |
SELECT u.id , u.username , e.created FROM (lsmb13.entity e JOIN lsmb13.users u ON ( (u.entity_id = e.id) ) );
This table sets the basic preferences for formats, languages, printers, and user-selected stylesheets.
F-Key | Name | Type | Description |
---|---|---|---|
lsmb13.users.id | id | integer | PRIMARY KEY |
lsmb13.language.code | language | character varying(6) | |
stylesheet | text | NOT NULL DEFAULT 'ledgersmb.css'::text | |
printer | text | ||
dateformat | text | NOT NULL DEFAULT 'yyyy-mm-dd'::text | |
numberformat | text | NOT NULL DEFAULT '1000.00'::text |
username is the actual primary key here because we do not want duplicate users
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | UNIQUE NOT NULL | |
username | character varying(30) | PRIMARY KEY | |
notify_password | interval | NOT NULL DEFAULT '7 days'::interval | |
lsmb13.entity.id | entity_id | integer | NOT NULL |
Tables referencing this one via Foreign Key Constraints:
Mapping vendor to taxes.
F-Key | Name | Type | Description |
---|---|---|---|
lsmb13.entity_credit_account.id | vendor_id | integer | PRIMARY KEY |
lsmb13.account.id | chart_id | integer | PRIMARY KEY |
Mapping transactions to batches for batch approval.
F-Key | Name | Type | Description |
---|---|---|---|
lsmb13.transactions.id | trans_id | integer | NOT NULL |
lsmb13.batch.id | batch_id | integer | NOT NULL |
id | serial |
PRIMARY KEY
This is simply a surrogate key for easy reference. |
|
lsmb13.batch_class.id | batch_class | integer |
NOT NULL
This is the authoritative class of the voucher. |
Tables referencing this one via Foreign Key Constraints:
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
description | text |
Tables referencing this one via Foreign Key Constraints:
An extension to the gl table to track transactionsactions which close out the books at yearend.
F-Key | Name | Type | Description |
---|---|---|---|
lsmb13.gl.id | trans_id | integer | PRIMARY KEY |
reversed | boolean | DEFAULT false | |
transdate | date |
Private method for storing locations to an entity. Do not call directly. Returns the location id that was inserted or updated.
DECLARE l_row location; l_id INT; t_company_id int; BEGIN SELECT id INTO t_company_id FROM company WHERE entity_id = in_entity_id; DELETE FROM company_to_location WHERE company_id = t_company_id AND location_class = in_location_class AND location_id = in_location_id; SELECT location_save(NULL, in_line_one, in_line_two, in_line_three, in_city, in_state, in_mail_code, in_country_code) INTO l_id; INSERT INTO company_to_location (company_id, location_class, location_id) VALUES (t_company_id, in_location_class, l_id); RETURN l_id; END;
select prsname::text from pg_catalog.pg_ts_parser p join pg_ts_config c on cfgparser = p.oid where c.oid = show_curcfg();
This deletes an account with the id specified. If the account has transactions associated with it, it will fail and raise a foreign key constraint.
BEGIN DELETE FROM account_link WHERE account_id = in_id; DELETE FROM account WHERE id = in_id; RETURN FOUND; END;
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);
Returns the account where the accno field matches (excatly) the in_accno provided.
select * from account where accno = $1;
Returns set of accounts where the tax attribute is true.
SELECT * FROM account WHERE tax is true ORDER BY accno;
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;
SELECT * FROM account ORDER BY heading;
Returns the account balance at a given point in time, calculating forward from most recent check point.
DECLARE balance numeric; BEGIN SELECT coalesce(sum(ac.amount) + cp.amount, sum(ac.amount)) INTO balance FROM acc_trans ac JOIN (select id, approved from ar union select id, approved from ap union select id, approved from gl) a ON (a.id = ac.trans_id) LEFT JOIN (select account_id, end_date, amount from account_checkpoint WHERE account_id = in_account_id AND end_date < in_transdate ORDER BY end_date desc limit 1 ) cp ON (cp.account_id = ac.chart_id) WHERE ac.chart_id = in_account_id AND ac.transdate > coalesce(cp.end_date, ac.transdate - '1 day'::interval) and ac.approved and a.approved and ac.transdate <= in_transdate GROUP BY cp.amount, ac.chart_id; RETURN balance; END;
This deletes existing account_link entries, where the account_link.description is not designated as a custom one in the account_link_description table. If no account heading is provided, the account heading which has an accno field closest to but prior (by collation order) is used. Then it saves the account information, and rebuilds the account_link records based on the in_link array.
DECLARE t_heading_id int; t_link record; t_id int; t_tax bool; BEGIN SELECT count(*) > 0 INTO t_tax FROM tax WHERE in_id = chart_id; t_tax := t_tax OR in_tax; -- check to ensure summary accounts are exclusive -- necessary for proper handling by legacy code FOR t_link IN SELECT description FROM account_link_description WHERE summary='t' LOOP IF t_link.description = ANY (in_link) and array_upper(in_link, 1) > 1 THEN RAISE EXCEPTION 'Invalid link settings: Summary'; END IF; END LOOP; -- heading settings IF in_heading IS NULL THEN SELECT id INTO t_heading_id FROM account_heading WHERE accno < in_accno order by accno desc limit 1; ELSE t_heading_id := in_heading; END IF; -- don't remove custom links. DELETE FROM account_link WHERE account_id = in_id and description in ( select description from account_link_description where custom = 'f'); UPDATE account SET accno = in_accno, description = in_description, category = in_category, gifi_accno = in_gifi_accno, heading = t_heading_id, contra = in_contra, obsolete = in_obsolete, tax = t_tax, is_temp = in_is_temp WHERE id = in_id; IF FOUND THEN t_id := in_id; ELSE -- can't obsolete on insert, but this can be changed if users -- request it --CT INSERT INTO account (accno, description, category, gifi_accno, heading, contra, tax, is_temp) VALUES (in_accno, in_description, in_category, in_gifi_accno, t_heading_id, in_contra, in_tax, in_is_temp); t_id := currval('account_id_seq'); END IF; FOR t_link IN select in_link[generate_series] AS val FROM generate_series(array_lower(in_link, 1), array_upper(in_link, 1)) LOOP INSERT INTO account_link (account_id, description) VALUES (t_id, t_link.val); END LOOP; RETURN t_id; END;
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;
Returns an entry from the chart view which matches the id requested, and which is an account, not a heading.
SELECT * from chart where id = $1 and charttype = 'A';
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;
Returns a list of all account headings, currently ordered by account number.
SELECT * FROM account_heading order by accno;
Returns an entry from the chart view which matches the id requested, and which is a heading, not an account.
DECLARE account chart%ROWTYPE; BEGIN SELECT * INTO account FROM chart WHERE id = in_id AND charttype = 'H'; RETURN account; END;
Lists all existing account headings.
SELECT * FROM account_heading order by accno;
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;
This deletes existing account_link entries, where the account_link.description is not designated as a custom one in the account_link_description table. If no account heading is provided, the account heading which has an accno field closest to but prior (by collation order) is used. Then it saves the account information, and rebuilds the account_link records based on the in_link array.
DECLARE t_heading_id int; t_link record; t_id int; t_tax bool; BEGIN SELECT count(*) > 0 INTO t_tax FROM tax WHERE in_id = chart_id; t_tax := t_tax OR in_tax; -- check to ensure summary accounts are exclusive -- necessary for proper handling by legacy code FOR t_link IN SELECT description FROM account_link_description WHERE summary='t' LOOP IF t_link.description = ANY (in_link) and array_upper(in_link, 1) > 1 THEN RAISE EXCEPTION 'Invalid link settings: Summary'; END IF; END LOOP; -- heading settings IF in_heading IS NULL THEN SELECT id INTO t_heading_id FROM account_heading WHERE accno < in_accno order by accno desc limit 1; ELSE t_heading_id := in_heading; END IF; -- don't remove custom links. DELETE FROM account_link WHERE account_id = in_id and description in ( select description from account_link_description where custom = 'f'); UPDATE account SET accno = in_accno, description = in_description, category = in_category, gifi_accno = in_gifi_accno, heading = t_heading_id, contra = in_contra, tax = t_tax WHERE id = in_id; IF FOUND THEN t_id := in_id; ELSE INSERT INTO account (accno, description, category, gifi_accno, heading, contra, tax) VALUES (in_accno, in_description, in_category, in_gifi_accno, t_heading_id, in_contra, in_tax); t_id := currval('account_id_seq'); END IF; FOR t_link IN select in_link[generate_series] AS val FROM generate_series(array_lower(in_link, 1), array_upper(in_link, 1)) LOOP INSERT INTO account_link (account_id, description) VALUES (t_id, t_link.val); END LOOP; RETURN t_id; END;
DECLARE table_name ALIAS FOR $1; new_field_name ALIAS FOR $2; field_datatype ALIAS FOR $3; BEGIN perform TABLE_ID FROM custom_table_catalog WHERE extends = table_name; IF NOT FOUND THEN BEGIN INSERT INTO custom_table_catalog (extends) VALUES (table_name); EXECUTE 'CREATE TABLE ' || quote_ident('custom_' ||table_name) || ' (row_id INT PRIMARY KEY)'; EXCEPTION WHEN duplicate_table THEN -- do nothing END; END IF; INSERT INTO custom_field_catalog (field_name, table_id) values (new_field_name, (SELECT table_id FROM custom_table_catalog WHERE extends = table_name)); EXECUTE 'ALTER TABLE '|| quote_ident('custom_'||table_name) || ' ADD COLUMN ' || quote_ident(new_field_name) || ' ' || quote_ident(field_datatype); RETURN TRUE; END;
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;
declare stmt TEXT; a_role name; a_user name; BEGIN -- Issue the grant select rolname into a_role from pg_roles where rolname = in_role; IF NOT FOUND THEN RAISE EXCEPTION 'Cannot grant permissions of a non-existant role.'; END IF; select rolname into a_user from pg_roles where rolname = in_username; IF NOT FOUND THEN RAISE EXCEPTION 'Cannot grant permissions to a non-existant user.'; END IF; stmt := 'GRANT '|| quote_ident(in_role) ||' to '|| quote_ident(in_username); EXECUTE stmt; insert into lsmb_roles (user_id, role) SELECT id, in_role from users where username = in_username; return 1; END;
DECLARE stmt text; t_dbname text; BEGIN t_dbname := current_database(); stmt := 'create role lsmb_'|| quote_ident(t_dbname || '__' || in_group_name); execute stmt; return 1; END;
Deletes the input group from the database. Not designed to be used to remove a login-capable user.
DECLARE stmt text; a_role role_view; t_dbname text; BEGIN t_dbname := current_database(); select * into a_role from role_view where rolname = in_group_name; if not found then return 'f'::bool; else stmt := 'drop role lsmb_' || quote_ident(t_dbname || '__' || in_group_name); execute stmt; return 't'::bool; end if; END;
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;
Drops the session identified, releasing all locks held.
BEGIN DELETE FROM "session" WHERE session_id = in_session_id; RETURN FOUND; END;
DECLARE v_rol record; t_dbname text; BEGIN t_dbname := current_database(); FOR v_rol in SELECT * from pg_roles where rolname ~ ('^lsmb_' || t_dbname || '__') and rolcanlogin is false order by rolname ASC LOOP RETURN NEXT v_rol; END LOOP; END;
Returns a set of roles that a user is a part of.
declare u_role record; a_user users; begin select * into a_user from admin__get_user(in_user_id); FOR u_role IN select r.rolname from pg_roles r, (select m.roleid from pg_auth_members m, pg_roles b where m.member = b.oid and b.rolname = a_user.username ) as ar where r.oid = ar.roleid LOOP RETURN NEXT u_role.rolname::text; END LOOP; RETURN; end;
Returns a set of (only one) user specified by the id.
DECLARE a_user users; BEGIN select * into a_user from users where id = in_user_id; return next a_user; return; END;
-- 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;
Returns true if user is set up in LedgerSMB. False otherwise.
BEGIN PERFORM * from users where username = in_user; RETURN found; END;
DECLARE out_rolename RECORD; BEGIN FOR out_rolename IN SELECT rolname FROM pg_roles WHERE oid IN (SELECT id FROM connectby ( '(SELECT m.member, m.roleid, r.oid FROM pg_roles r LEFT JOIN pg_auth_members m ON (r.oid = m.roleid)) a', 'oid', 'member', 'oid', '320461', '0', ',' ) c(id integer, parent integer, "level" integer, path text, list_order integer) ) LOOP RETURN NEXT out_rolename.rolname; END LOOP; END;
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;
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;
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;
Creates a user and relevant records in LedgerSMB and PostgreSQL.
DECLARE a_user users; v_user_id int; p_id int; l_id int; stmt text; t_is_role bool; t_is_user bool; BEGIN -- WARNING TO PROGRAMMERS: This function runs as the definer and runs -- utility statements via EXECUTE. -- PLEASE BE VERY CAREFUL ABOUT SQL-INJECTION INSIDE THIS FUNCTION. PERFORM rolname FROM pg_roles WHERE rolname = in_username; t_is_role := found; t_is_user := admin__is_user(in_username); IF t_is_role is true and t_is_user is false and in_import is false THEN RAISE EXCEPTION 'Duplicate user'; END IF; if t_is_role and in_password is not null then execute 'ALTER USER ' || quote_ident( in_username ) || ' WITH ENCRYPTED PASSWORD ' || quote_literal (in_password) || $e$ valid until $e$ || quote_literal(now() + '1 day'::interval); elsif in_import is false AND t_is_user is false AND in_password IS NULL THEN RAISE EXCEPTION 'No password'; elsif t_is_role is false THEN -- create an actual user execute 'CREATE USER ' || quote_ident( in_username ) || ' WITH ENCRYPTED PASSWORD ' || quote_literal (in_password) || $e$ valid until $e$ || quote_literal(now() + '1 day'::interval); END IF; select * into a_user from users lu where lu.id = in_id; IF FOUND THEN return a_user.id; ELSE -- Insert cycle --- The entity is expected to already BE created. See admin.pm. v_user_id := nextval('users_id_seq'); insert into users (id, username, entity_id) VALUES ( v_user_id, in_username, in_entity_id ); insert into user_preference (id) values (v_user_id); IF NOT exists(SELECT * FROM entity_employee WHERE entity_id = in_entity_id) THEN INSERT into entity_employee (entity_id) values (in_entity_id); END IF; -- Finally, issue the create user statement return v_user_id ; END IF; END;
Returns a list of users matching search criteria. Nulls match all values. only username is not an exact match.
DECLARE t_return_row user_result; BEGIN FOR t_return_row IN SELECT u.id, u.username, p.first_name, p.last_name, e.ssn, e.dob FROM users u JOIN person p ON (u.entity_id = p.entity_id) JOIN entity_employee e ON (e.entity_id = p.entity_id) WHERE u.username LIKE '%' || coalesce(in_username,'') || '%' AND (p.first_name = in_first_name or in_first_name is null) AND (p.last_name = in_last_name or in_last_name is null) AND (in_ssn is NULL or in_ssn = e.ssn) AND (e.dob = in_dob::date or in_dob is NULL) LOOP RETURN NEXT t_return_row; END LOOP; END;
A basic array aggregate to take elements and return a one-dimensional array. Example: SELECT as_array(id) from entity_class;
aggregate_dummy
Retrieves a given asset either by id or tag. Both are complete matches. Note that the behavior is undefined if both id and tag are provided.
DECLARE ret_val asset_item; BEGIN SELECT * into ret_val from asset_item WHERE id = in_id OR in_tag = tag ORDER BY id desc limit 1; return ret_val; END;
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;
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;
Searches for assets. Nulls match all records. Asset class is exact, as is purchase date, purchase value, and salvage value. Tag and description are partial matches.
DECLARE out_val asset_item; BEGIN FOR out_val IN SELECT * FROM asset_item WHERE (in_asset_class is null or asset_class_id = in_asset_class) AND (in_description is null or description LIKE '%' || in_description || '%') and (in_tag is not null or tag like '%'||in_tag||'%') AND (in_purchase_date is null or purchase_date = in_purchase_date) AND (in_purchase_value is null or in_purchase_value = purchase_value) AND (in_usable_life is null or in_usable_life = usable_life) AND (in_salvage_value is null OR in_salvage_value = salvage_value) LOOP RETURN NEXT out_val; END LOOP; END;
returns the row from asset_class identified by in_id.
DECLARE ret_val asset_class; BEGIN SELECT * INTO ret_val FROM asset_class WHERE id = in_id; RETURN ret_val; END;
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;
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;
Returns the depreciation method associated with the asset class.
SELECT * from asset_dep_method WHERE id = (select method from asset_class where id = $1);
Returns a set of asset_dep_methods ordered by the method label.
SELECT * FROM asset_dep_method ORDER BY method;
Returns an alphabetical list of asset classes.
SELECT * FROM asset_class ORDER BY label;
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;
Returns a list of matching asset classes. The account id's are exact matches as is the method, but the label is a partial match. NULL's match all.
DECLARE out_var asset_class_result; BEGIN FOR out_var IN SELECT ac.id, ac.asset_account_id, aa.accno, aa.description, ad.accno, ad.description, m.method, ac.method, ac.label FROM asset_class ac JOIN account aa ON (aa.id = ac.asset_account_id) JOIN account ad ON (ad.id = ac.dep_account_id) JOIN asset_dep_method m ON (ac.method = m.id) WHERE (in_asset_account_id is null or in_asset_account_id = ac.asset_account_id) AND (in_dep_account_id is null OR in_dep_account_id = ac.dep_account_id) AND (in_method is null OR in_method = ac.method) AND (in_label IS NULL OR ac.label LIKE '%' || in_label || '%') ORDER BY label LOOP RETURN NEXT out_var; END LOOP; END;
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;
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;
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;
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;
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;
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;
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;
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;
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');
Returns a list of matching asset items. Nulls match all records. Tag and description allow for partial match. All other matches are exact.
DECLARE retval asset_item; BEGIN FOR retval IN SELECT * FROM asset_item WHERE (id = in_id or in_id is null) and (asset_class_id = in_asset_class or in_asset_class is null) and (description like '%'||in_description||'%' or in_description is null) and (tag like '%' || in_tag || '%' or in_tag is null) and (purchase_value = in_purchase_value or in_purchase_value is null) and (in_purchase_date = purchase_date or in_purchase_date is null) and (start_depreciation = in_start_depreciation or in_start_depreciation is null) and (in_warehouse_id = location_id OR in_warehouse_id is null) and (department_id = in_department_id or in_department_id is null) and (in_invoice_id = invoice_id OR in_invoice_id IS NULL) and (asset_account_id = in_asset_account_id or in_asset_account_id is null) and (dep_account_id = in_dep_account_id or in_dep_account_id is null) LOOP return next retval; end loop; END;
Returns the current net book value report.
SELECT ai.id, ai.tag, ai.description, coalesce(ai.start_depreciation, ai.purchase_date), adm.short_name, ai.usable_life - months_passed(coalesce(ai.start_depreciation, ai.purchase_date), coalesce(max(r.report_date), ai.start_depreciation, ai.purchase_date))/ 12, ai.purchase_value - ai.salvage_value, ai.salvage_value, max(r.report_date), sum(rl.amount), ai.purchase_value - sum(rl.amount) FROM asset_item ai JOIN asset_class ac ON (ai.asset_class_id = ac.id) JOIN asset_dep_method adm ON (adm.id = ac.method) LEFT JOIN asset_report_line rl ON (ai.id = rl.asset_id) LEFT JOIN asset_report r on (rl.report_id = r.id) WHERE r.id IS NULL OR r.approved_at IS NOT NULL GROUP BY ai.id, ai.tag, ai.description, ai.start_depreciation, ai.purchase_date, adm.short_name, ai.usable_life, ai.purchase_value, salvage_value HAVING (NOT 2 = ANY(as_array(r.report_class))) AND (NOT 4 = ANY(as_array(r.report_class))) OR max(r.report_class) IS NULL ORDER BY ai.id, ai.tag, ai.description;
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;
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;
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');
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;
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;
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;
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;
Returns the asset_report line identified by id.
select * from asset_report where id = $1;
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;
Returns a list of asset_disposal_method items ordered by label.
SELECT * FROM asset_disposal_method order by label;
Lists all asset expense reports.
SELECT * FROM account__get_by_link_desc('asset_expense');
Returns a list of gain accounts for asset depreciation and disposal reports.
SELECT * FROM account__get_by_link_desc('asset_gain');
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;
Returns a list of loss accounts for asset depreciation and disposal reports.
SELECT * FROM account__get_by_link_desc('asset_loss');
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;
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;
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;
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;
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;
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;
Inserts the batch into the table.
BEGIN INSERT INTO batch (batch_class_id, default_date, description, control_code, created_by) VALUES ((SELECT id FROM batch_class WHERE class = in_batch_class), in_batch_date, in_description, in_batch_number, (select entity_id FROM users WHERE username = session_user)); return currval('batch_id_seq'); END;
If the batch is found and unapproved, deletes it and returns 1. Otherwise raises an exception.
DECLARE t_transaction_ids int[]; BEGIN -- Adjust AR/AP tables for payment and payment reversal vouchers -- voucher_id is only set in acc_trans on payment/receipt vouchers and -- their reversals. -CT perform * from batch where id = in_batch_id and approved_on IS NULL; IF NOT FOUND THEN RAISE EXCEPTION 'Batch not found'; END IF; update ar set paid = amount + (select sum(amount) from acc_trans join chart ON (acc_trans.chart_id = chart.id) where link = 'AR' AND trans_id = ar.id AND (voucher_id IS NULL OR voucher_id NOT IN (select id from voucher WHERE batch_id = in_batch_id))) where id in (select trans_id from acc_trans where voucher_id IN (select id from voucher where batch_id = in_batch_id)); update ap set paid = amount - (select sum(amount) from acc_trans join chart ON (acc_trans.chart_id = chart.id) where link = 'AP' AND trans_id = ap.id AND (voucher_id IS NULL OR voucher_id NOT IN (select id from voucher WHERE batch_id = in_batch_id))) where id in (select trans_id from acc_trans where voucher_id IN (select id from voucher where batch_id = in_batch_id)); DELETE FROM ac_tax_form WHERE entry_id IN (select entry_id from acc_trans where voucher_id in (select id from voucher where batch_id = in_batch_id) ); DELETE FROM acc_trans WHERE voucher_id IN (select id FROM voucher where batch_id = in_batch_id); -- The rest of this function involves the deletion of actual -- transactions, vouchers, and batches, and jobs which are in progress. -- -CT SELECT as_array(trans_id) INTO t_transaction_ids FROM voucher WHERE batch_id = in_batch_id AND batch_class IN (1, 2, 5); DELETE FROM ac_tax_form WHERE entry_id in (select entry_id from acc_trans where trans_id = any(t_transaction_ids)); DELETE FROM acc_trans WHERE trans_id = ANY(t_transaction_ids); DELETE FROM ap WHERE id = ANY(t_transaction_ids); DELETE FROM gl WHERE id = ANY(t_transaction_ids); DELETE FROM voucher WHERE batch_id = in_batch_id; DELETE FROM batch WHERE id = in_batch_id; DELETE FROM transactions WHERE id = ANY(t_transaction_ids); RETURN 1; END;
returns the batch class id associated with the in_type label provided.
SELECT id FROM batch_class WHERE class = $1;
Returns a sim[ple set of user objects. This should be renamed so that it is more obvious it is a general purpose function.
DECLARE out_record users%ROWTYPE; BEGIN FOR out_record IN SELECT * from users WHERE entity_id IN (select created_by from batch) LOOP RETURN NEXT out_record; END LOOP; END;
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;
Posts the specified batch to the books. Only posted batches should show up on standard financial reports.
BEGIN UPDATE ar SET approved = true WHERE id IN (select trans_id FROM voucher WHERE batch_id = in_batch_id AND batch_class = 2); UPDATE ap SET approved = true WHERE id IN (select trans_id FROM voucher WHERE batch_id = in_batch_id AND batch_class = 1); UPDATE gl SET approved = true WHERE id IN (select trans_id FROM voucher WHERE batch_id = in_batch_id AND batch_class = 5); UPDATE acc_trans SET approved = true WHERE voucher_id IN (select id FROM voucher WHERE batch_id = in_batch_id AND batch_class IN (3, 4, 6, 7)); UPDATE batch SET approved_on = now(), approved_by = (select entity_id FROM users WHERE username = SESSION_USER) WHERE id = in_batch_id; RETURN now()::date; END;
Returns a list of batches and amounts processed on the batch. Nulls match all values. in_date_from and in_date_to specify date ranges. in_description is a partial match. All other criteria are exact matches.
DECLARE out_value batch_list_item; BEGIN FOR out_value IN SELECT b.id, c.class, b.control_code, b.description, u.username, b.created_on, b.default_date, sum( CASE WHEN vc.id = 5 AND al.amount < 0 -- GL THEN al.amount WHEN vc.id = 1 THEN ap.amount WHEN vc.id = 2 THEN ap.amount ELSE 0 END) AS transaction_total, sum( CASE WHEN alc.link = 'AR' AND vc.id IN (6, 7) THEN al.amount WHEN alc.link = 'AP' AND vc.id IN (3, 4) THEN al.amount * -1 ELSE 0 END ) AS payment_total FROM batch b JOIN batch_class c ON (b.batch_class_id = c.id) LEFT JOIN users u ON (u.entity_id = b.created_by) LEFT JOIN voucher v ON (v.batch_id = b.id) LEFT JOIN batch_class vc ON (v.batch_class = vc.id) LEFT JOIN ar ON (vc.id = 2 AND v.trans_id = ar.id) LEFT JOIN ap ON (vc.id = 1 AND v.trans_id = ap.id) LEFT JOIN acc_trans al ON ((vc.id = 5 AND v.trans_id = al.trans_id) OR (vc.id IN (3, 4, 6, 7) AND al.voucher_id = v.id)) LEFT JOIN chart alc ON (al.chart_id = alc.id) WHERE (c.id = in_class_id OR in_class_id IS NULL) AND (b.description LIKE '%' || in_description || '%' OR in_description IS NULL) AND (in_created_by_eid = b.created_by OR in_created_by_eid IS NULL) AND ((in_approved = false OR in_approved IS NULL AND approved_on IS NULL) OR (in_approved = true AND approved_on IS NOT NULL) ) and (in_date_from IS NULL or b.default_date >= in_date_from) and (in_date_to IS NULL or b.default_date <= in_date_to) GROUP BY b.id, c.class, b.description, u.username, b.created_on, b.control_code, b.default_date HAVING (in_amount_gt IS NULL OR sum(coalesce(ar.amount - ar.paid, ap.amount - ap.paid, al.amount)) >= in_amount_gt) AND (in_amount_lt IS NULL OR sum(coalesce(ar.amount - ar.paid, ap.amount - ap.paid, al.amount)) <= in_amount_lt) ORDER BY b.control_code, b.description LOOP RETURN NEXT out_value; END LOOP; END;
This is a full search for the batches, listing them by amount processed. in_amount_gt and in_amount_lt provide a range to search for. in_description is a partial match field. Other fields are exact matches. NULLs match all values.
DECLARE out_value batch_list_item; BEGIN FOR out_value IN SELECT b.id, c.class, b.control_code, b.description, u.username, b.created_on, b.default_date, 0, 0 FROM batch b JOIN batch_class c ON (b.batch_class_id = c.id) JOIN users u ON (u.entity_id = b.created_by) LEFT JOIN voucher v ON (v.batch_id = b.id) where v.id is null and(u.entity_id = in_created_by_eid or in_created_by_eid is null) and (in_description is null or b.description like '%' || in_description || '%') and (in_class_id is null or c.id = in_class_id) GROUP BY b.id, c.class, b.description, u.username, b.created_on, b.control_code, b.default_date ORDER BY b.control_code, b.description LOOP RETURN NEXT out_value; END LOOP; END;
This performs a simple search of open batches created by the entity_id in question. This is used to pull up batches that were currently used so that they can be picked up and more vouchers added. NULLs match all values. in_description is a partial match All other inouts are exact matches.
DECLARE out_value batch_list_item; BEGIN FOR out_value IN SELECT b.id, c.class, b.control_code, b.description, u.username, b.created_on, b.default_date, NULL FROM batch b JOIN batch_class c ON (b.batch_class_id = c.id) LEFT JOIN users u ON (u.entity_id = b.created_by) WHERE (c.id = in_class_id OR in_class_id IS NULL) AND (b.description LIKE '%' || in_description || '%' OR in_description IS NULL) AND (in_created_by_eid = b.created_by OR in_created_by_eid IS NULL) AND ((in_approved = false OR in_approved IS NULL AND approved_on IS NULL) OR (in_approved = true AND approved_on IS NOT NULL) ) GROUP BY b.id, c.class, b.description, u.username, b.created_on, b.control_code, b.default_date LOOP RETURN NEXT out_value; END LOOP; END;
UPDATE budget_info set approved_at = now(), approved_by = person__get_my_entity_id() WHERE id = $1; SELECT budget__get_info($1);
This retrieves the budget lines associated with a budget.
SELECT * FROM budget_line where budget_id = $1;
Selects the budget info.
select bi.id, bi.start_date, bi.end_date, bi.reference, bi.description, bi.entered_by, bi.approved_by, bi.obsolete_by, bi.entered_at, bi.approved_at, bi.obsolete_at, ee.name, ae.name, oe.name, bd.department_id, d.description, bp.project_id, p.projectnumber from budget_info bi JOIN entity ee ON bi.entered_by = ee.id LEFT JOIN budget_to_department bd ON bd.budget_id = bi.id LEFT JOIN entity ae ON bi.approved_by = ae.id LEFT JOIN entity oe ON bi.obsolete_by = oe.id LEFT JOIN budget_to_project bp ON bp.budget_id = bi.id LEFT JOIN department d ON d.id = bd.department_id LEFT JOIN project p ON bp.project_id = p.id where bi.id = $1;
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;
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)
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;
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;
Saves the extended budget info passed through to the function. See the comment on type budget_info_ext for more information.
DECLARE retval budget_info_ext; t_id int; BEGIN UPDATE budget_info SET start_date = in_start_date, end_date = in_end_date, reference = in_reference, description = in_description WHERE id = in_id and approved_by is null; IF FOUND THEN t_id := in_id; ELSE PERFORM * FROM budget_info WHERE id = in_id and approved_by is not null; IF FOUND THEN RAISE EXCEPTION 'report approved'; END IF; INSERT INTO budget_info (start_date, end_date, reference, description) VALUES (in_start_date, in_end_date, in_reference, in_description); t_id = currval('budget_info_id_seq'); END IF; IF in_project_id IS NOT NULL THEN UPDATE budget_to_project SET project_id = in_project_id WHERE budget_id = t_id; IF NOT FOUND THEN INSERT INTO budget_to_project(budget_id, project_id) VALUES (t_id, in_project_id); END IF; END IF; IF in_department_id IS NOT NULL THEN UPDATE budget_to_department SET department_id = in_department_id WHERE budget_id = t_id; IF NOT FOUND THEN INSERT INTO budget_to_department(budget_id, department_id) VALUES (t_id, in_department_id); END IF; END IF; retval := budget__get_info(t_id); return retval; END;
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);
This is a general search for budgets
select bi.id, bi.start_date, bi.end_date, bi.reference, bi.description, bi.entered_by, bi.approved_by, bi.obsolete_by, bi.entered_at, bi.approved_at, bi.obsolete_at, ee.name, ae.name, oe.name, bd.department_id, d.description, bp.project_id, p.projectnumber from budget_info bi JOIN entity ee ON bi.entered_by = ee.id LEFT JOIN budget_to_department bd ON bd.budget_id = bi.id LEFT JOIN entity ae ON bi.approved_by = ae.id LEFT JOIN entity oe ON bi.obsolete_by = oe.id LEFT JOIN budget_to_project bp ON bp.budget_id = bi.id LEFT JOIN department d ON d.id = bd.department_id LEFT JOIN project p ON bp.project_id = p.id WHERE (start_date = $1 or $1 is null) AND ($2 = end_date or $2 is null) AND ($3 BETWEEN start_date AND end_date or $2 is null) AND ($4 ilike reference || '%' or $4 is null) AND (bi.description @@ plainto_tsquery($5) or $5 is null) AND ($6 = entered_by or $6 is null) AND ($7 = approved_by or $7 is null) AND ($8 = obsolete_by or $8 is null) AND ($9 = department_id OR $9 is null) AND ($10 = project_id OR $10 IS NULL) AND ($11 IS NULL OR ($11 = (approved_by IS NOT NULL))) AND ($12 IS NULL OR ($12 = (obsolete_by IS NOT NULL))) ORDER BY department_id, project_id, reference;
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;
Returns a list of all business types. Ordered by description by default.
DECLARE out_row business%ROWTYPE; BEGIN FOR out_row IN SELECT * FROM business ORDER BY description LOOP RETURN NEXT out_row; END LOOP; END;
This function returns the cash account acording with in_account_class which must be 1 or 2. If in_account_class is 1 then it returns a list of AP accounts, and if in_account_class is 2, then a list of AR accounts.
DECLARE out_row chart%ROWTYPE; BEGIN IF in_account_class NOT IN (1, 2) THEN RAISE EXCEPTION 'Bad Account Type'; END IF; FOR out_row IN SELECT * FROM chart WHERE link = CASE WHEN in_account_class = 1 THEN 'AP' WHEN in_account_class = 2 THEN 'AR' END ORDER BY accno LOOP RETURN NEXT out_row; END LOOP; END;
Generates a list of chart view entries.
DECLARE out_row chart%ROWTYPE; BEGIN FOR out_row IN SELECT * FROM chart ORDER BY accno LOOP RETURN next out_row; END LOOP; END;
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;
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;
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;
This returns a list of account entries where the description or account number begins with in_search. If in_link_desc is provided, the list is further filtered by which accounts are set to an account_link.description equal to that provided.
DECLARE out_row account%ROWTYPE; BEGIN FOR out_row IN SELECT * FROM account WHERE (accno ~* ('^'||in_search) OR description ~* ('^'||in_search)) AND (in_link_desc IS NULL or id in (select account_id from account_link where description = in_link_desc)) AND not obsolete ORDER BY accno LOOP RETURN next out_row; END LOOP; END;
declare dpt_id int; begin if new.department_id = 0 then delete from dpt_trans where trans_id = new.id; return NULL; end if; select into dpt_id trans_id from dpt_trans where trans_id = new.id; if dpt_id > 0 then update dpt_trans set department_id = new.department_id where trans_id = dpt_id; else insert into dpt_trans (trans_id, department_id) values (new.id, new.department_id); end if; return NULL; end;
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;
DECLARE t_alloc numeric := 0; t_cogs numeric := 0; t_inv invoice; t_cp account_checkpoint; t_ar ar; t_avail numeric; BEGIN IF in_qty > 0 THEN return cogs__reverse_ap(in_parts_id, in_qty * -1) * in_lastcost; END IF; SELECT * INTO t_cp FROM account_checkpoint ORDER BY end_date DESC LIMIT 1; FOR t_inv IN SELECT i.* FROM invoice i JOIN ar a ON a.id = i.trans_id WHERE qty + allocated > 0 and parts_id = in_parts_id ORDER BY a.transdate, a.id, i.id LOOP t_avail := t_inv.qty + t_inv.allocated; SELECT * INTO t_ar FROM ar WHERE id = t_inv.trans_id; IF t_alloc < in_qty THEN RAISE EXCEPTION 'TOO MANY ALLOCATED'; ELSIF t_alloc = in_qty THEN return t_alloc; ELSIF (in_qty + t_alloc) * -1 <= t_avail THEN UPDATE invoice SET allocated = allocated + (in_qty + t_alloc) WHERE id = t_inv.id; INSERT INTO acc_trans (chart_id, transdate, amount, invoice_id, approved, trans_id) SELECT expense_accno_id, CASE WHEN t_ar.transdate > t_cp.end_date THEN t_ar.transdate ELSE t_cp.end_date + '1 day'::interval END, (in_qty + t_alloc) * in_lastcost, t_inv.id, true, t_inv.trans_id FROM parts WHERE id = t_inv.parts_id AND inventory_accno_id IS NOT NULL AND expense_accno_id IS NOT NULL UNION SELECT income_accno_id, CASE WHEN t_ar.transdate > t_cp.end_date THEN t_ar.transdate ELSE t_cp.end_date + '1 day'::interval END, -1 * (in_qty + t_alloc) * in_lastcost, t_inv.id, true, t_inv.trans_id FROM parts WHERE id = t_inv.parts_id AND inventory_accno_id IS NOT NULL AND expense_accno_id IS NOT NULL; t_cogs := t_cogs + (in_qty + t_alloc) * in_lastcost; return in_qty * -1; ELSE UPDATE invoice SET allocated = qty * -1 WHERE id = t_inv.id; t_cogs := t_cogs + t_avail * in_lastcost; INSERT INTO acc_trans (chart_id, transdate, amount, invoice_id, approved, trans_id) SELECT expense_accno_id, CASE WHEN t_ar.transdate > t_cp.end_date THEN t_ar.transdate ELSE t_cp.end_date + '1 day'::interval END, -1 * t_avail * in_lastcost, t_inv.id, true, t_inv.trans_id FROM parts WHERE id = t_inv.parts_id AND inventory_accno_id IS NOT NULL AND expense_accno_id IS NOT NULL UNION SELECT income_accno_id, CASE WHEN t_ar.transdate > t_cp.end_date THEN t_ar.transdate ELSE t_cp.end_date + '1 day'::interval END, -t_avail * in_lastcost, t_inv.id, true, t_inv.trans_id FROM parts WHERE id = t_inv.parts_id AND inventory_accno_id IS NOT NULL AND expense_accno_id IS NOT NULL; t_alloc := t_alloc + t_avail; t_cogs := t_cogs + t_avail * in_lastcost; END IF; END LOOP; RETURN t_alloc; END;
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;
This function accepts a parts_id and a quantity, and iterates through AP records in order, calculating COGS on a FIFO basis and returning it to the application to attach to the current transaction. Return values are an array of {allocated, cogs}.
DECLARE t_alloc numeric := 0; t_cogs numeric := 0; t_inv invoice; t_avail numeric; BEGIN FOR t_inv IN SELECT i.* FROM invoice i JOIN (select id, approved, transdate from ap union select id, approved, transdate from gl) a ON a.id = i.trans_id WHERE qty + allocated < 0 AND i.parts_id = in_parts_id ORDER BY a.transdate asc, a.id asc, i.id asc LOOP t_avail := (t_inv.qty + t_inv.allocated) * -1; IF t_alloc > in_qty THEN RAISE EXCEPTION 'TOO MANY ALLOCATED'; ELSIF t_alloc = in_qty THEN return ARRAY[t_alloc, t_cogs]; ELSIF (in_qty + t_alloc) <= t_avail THEN UPDATE invoice SET allocated = allocated + (in_qty - t_alloc) WHERE id = t_inv.id; t_cogs := t_cogs + (in_qty - t_alloc) * t_inv.sellprice; t_alloc := in_qty; return ARRAY[t_alloc, t_cogs]; ELSE UPDATE invoice SET allocated = qty * -1 WHERE id = t_inv.id; t_cogs := t_cogs + (t_avail * t_inv.sellprice); t_alloc := t_alloc + t_avail; END IF; END LOOP; RETURN ARRAY[t_alloc, t_cogs]; END;
DECLARE t_cogs numeric[]; t_inv invoice; t_part parts; t_ar ar; t_transdate date; BEGIN SELECT * INTO t_inv FROM invoice WHERE id = in_invoice_id; SELECT * INTO t_part FROM parts WHERE id = t_inv.parts_id; SELECT * INTO t_ar FROM ar WHERE id = t_inv.trans_id; IF t_inv.qty + t_inv.allocated = 0 THEN return 0; END IF; IF t_inv.qty > 0 THEN t_cogs := cogs__add_for_ar(t_inv.parts_id, t_inv.qty + t_inv.allocated); ELSE t_cogs := cogs__reverse_ar(t_inv.parts_id, t_inv.qty + t_inv.allocated); END IF; UPDATE invoice set allocated = allocated - t_cogs[1] WHERE id = in_invoice_id; SELECT CASE WHEN t_ar.transdate > max(end_date) THEN t_ar.transdate ELSE max(end_date) + '1 day'::interval END INTO t_transdate from account_checkpoint td; INSERT INTO acc_trans (trans_id, chart_id, approved, amount, transdate, invoice_id) VALUES (t_inv.trans_id, CASE WHEN t_inv.qty < 0 AND t_ar.is_return THEN t_part.returns_accno_id ELSE t_part.expense_accno_id END, TRUE, t_cogs[2] * -1, t_transdate, t_inv.id), (t_inv.trans_id, t_part.inventory_accno_id, TRUE, t_cogs[2], t_transdate, t_inv.id); RETURN t_cogs[1]; END;
This function iterates through invoice rows attached to ap transactions and allocates them on a first-in first-out basis. The sort of pseudo-"COGS" value is returned to the application for further handling.
DECLARE t_alloc numeric :=0; t_inv invoice; t_cogs numeric :=0; retval numeric[]; BEGIN RAISE NOTICE 'reversing AP: parts_id %, qty %', in_parts_id, in_qty; FOR t_inv IN SELECT i.* FROM invoice i JOIN ap a ON a.id = i.trans_id WHERE qty + allocated < 0 AND parts_id = in_parts_id ORDER BY a.transdate, a.id, i.id LOOP RAISE NOTICE 'id %, avail %, allocated %, requesting %', t_inv.id, t_inv.qty + t_inv.allocated, t_alloc, in_qty - t_alloc; IF t_alloc > in_qty THEN RAISE EXCEPTION 'TOO MANY ALLOCATED'; ELSIF t_alloc = in_qty THEN return ARRAY[t_alloc, t_cogs]; ELSIF (in_qty - t_alloc) <= -1 * (t_inv.qty + t_inv.allocated) THEN raise notice 'partial reversal'; UPDATE invoice SET allocated = allocated + (in_qty - t_alloc) WHERE id = t_inv.id; return ARRAY[in_qty * -1, t_cogs + (in_qty - t_alloc) * t_inv.sellprice]; ELSE raise notice 'total reversal'; UPDATE invoice SET allocated = qty * -1 WHERE id = t_inv.id; t_alloc := t_alloc - (t_inv.qty + t_inv.allocated); t_cogs := t_cogs - (t_inv.qty + t_inv.allocated) * t_inv.sellprice; END IF; END LOOP; RETURN ARRAY[t_alloc, t_cogs]; RAISE EXCEPTION 'TOO FEW TO ALLOCATE'; END;
This function accepts a part id and quantity to reverse. It then iterates backwards over AP related records, calculating COGS. This does not save COGS but rather returns it to the application to save. Return values are an array of {allocated, cogs}.
DECLARE t_alloc numeric := 0; t_cogs numeric := 0; t_inv invoice; t_avail numeric; BEGIN FOR t_inv IN SELECT i.* FROM invoice i JOIN (select id, approved, transdate from ap union select id, approved, transdate from gl) a ON a.id = i.trans_id WHERE allocated > 0 and a.approved and parts_id = in_parts_id ORDER BY a.transdate DESC, a.id DESC, i.id DESC LOOP t_avail := t_inv.allocated; IF t_alloc < in_qty THEN RAISE EXCEPTION 'TOO MANY ALLOCATED'; ELSIF t_alloc = in_qty THEN RETURN ARRAY[t_alloc, t_cogs]; ELSIF (in_qty - t_alloc) * -1 <= t_inv.allocated THEN raise notice 'partial reversal'; UPDATE invoice SET allocated = allocated + (in_qty - t_alloc) WHERE id = t_inv.id; t_cogs := t_cogs + (in_qty - t_alloc) * t_inv.sellprice; return ARRAY[t_alloc + (in_qty - t_alloc), t_cogs]; ELSE raise notice 'full reversal'; UPDATE invoice SET allocated = 0 WHERE id = t_inv.id; t_alloc := t_alloc + t_inv.allocated * -1; t_cogs := t_cogs + -1 * (t_inv.allocated) * t_inv.sellprice; END IF; END LOOP; RAISE EXCEPTION 'TOO FEW TO REVERSE'; END;
Returns true if at least one record was deleted. False if no records were affected.
BEGIN DELETE FROM company_to_contact WHERE company_id = in_company_id and contact_class_id = in_contact_class_id and contact= in_contact; RETURN FOUND; END;
Deletes the record identified. Returns true if successful, false if no record found.
BEGIN DELETE FROM eca_to_location WHERE company_id = in_company_id AND location_id = in_location_id AND location_class = in_location_class; RETURN FOUND; END;
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;
Lists all bank accounts for the entity.
DECLARE out_row entity_bank_account%ROWTYPE; BEGIN FOR out_row IN SELECT * from entity_bank_account where entity_id = in_entity_id LOOP RETURN NEXT out_row; END LOOP; END;
Lists all contact info for the entity.
DECLARE out_row contact_list; BEGIN FOR out_row IN SELECT cl.class, cl.id, c.description, c.contact FROM company_to_contact c JOIN contact_class cl ON (c.contact_class_id = cl.id) WHERE company_id = (select id FROM company WHERE entity_id = in_entity_id) LOOP return next out_row; END LOOP; END;
Returns a set of notes (including content) attached to the entity.
DECLARE out_row record; BEGIN FOR out_row IN SELECT * FROM entity_note WHERE ref_key = in_entity_id ORDER BY created LOOP RETURN NEXT out_row; END LOOP; END;
Saves a location to a company. Returns the location id.
BEGIN return _entity_location_save( in_entity_id, in_location_id, in_location_class, in_line_one, in_line_two, '', in_city , in_state, in_mail_code, in_country_code); END;
select nextval('company_id_seq');
Saves company contact information. The return value is meaningless.
DECLARE out_id int; BEGIN INSERT INTO company_to_contact(company_id, contact_class_id, description, contact) SELECT id, in_contact_class, in_description, in_contact FROM company WHERE entity_id = in_entity_id; RETURN 1; END;
DECLARE out_row company_search_result; loop_count int; t_contact_info text[]; BEGIN t_contact_info = in_contact_info; FOR out_row IN SELECT e.id, e.control_code, c.id, ec.id, ec.meta_number, ec.description, ec.entity_class, c.legal_name, c.sic_code, b.description , ec.curr::text FROM (select * from entity where in_control_code = control_code union select * from entity where in_control_code is null) e JOIN (SELECT * FROM company WHERE legal_name ilike '%' || in_legal_name || '%' UNION ALL SELECT * FROM company WHERE in_legal_name IS NULL) c ON (e.id = c.entity_id) LEFT JOIN entity_credit_account ec ON (ec.entity_id = e.id) LEFT JOIN business b ON (ec.business_id = b.id) WHERE coalesce(ec.entity_class,e.entity_class) = in_account_class AND (c.id IN (select company_id FROM company_to_contact WHERE contact ILIKE ALL(t_contact_info)) OR '' ILIKE ALL(t_contact_info) OR t_contact_info IS NULL) AND (c.legal_name ilike '%' || in_legal_name || '%' OR in_legal_name IS NULL) AND ((in_address IS NULL AND in_city IS NULL AND in_state IS NULL AND in_country IS NULL) OR (c.id IN (select company_id FROM company_to_location WHERE location_id IN (SELECT id FROM location WHERE line_one ilike '%' || coalesce(in_address, '') || '%' AND city ILIKE '%' || coalesce(in_city, '') || '%' AND state ILIKE '%' || coalesce(in_state, '') || '%' AND mail_code ILIKE '%' || coalesce(in_mail_code, '') || '%' AND country_id IN (SELECT id FROM country WHERE name ILIKE '%' || in_country ||'%' OR short_name ilike in_country))))) AND (ec.business_id = coalesce(in_business_id, ec.business_id) OR (ec.business_id IS NULL AND in_business_id IS NULL)) AND (ec.startdate <= coalesce(in_date_to, ec.startdate) OR (ec.startdate IS NULL)) AND (ec.enddate >= coalesce(in_date_from, ec.enddate) OR (ec.enddate IS NULL)) AND (ec.meta_number = in_meta_number OR in_meta_number IS NULL) LOOP RETURN NEXT out_row; END LOOP; END;
Returns billing information (billing name and address) for a given credit account.
DECLARE out_var company_billing_info; t_id INT; BEGIN select coalesce(eca.pay_to_name, c.legal_name), eca.meta_number, e.control_code, c.tax_id, a.line_one, a.line_two, a.line_three, a.city, a.state, a.mail_code, cc.name into out_var FROM company c JOIN entity e ON (c.entity_id = e.id) JOIN entity_credit_account eca ON (eca.entity_id = e.id) LEFT JOIN eca_to_location cl ON (eca.id = cl.credit_id) LEFT JOIN location a ON (a.id = cl.location_id) LEFT JOIN country cc ON (cc.id = a.country_id) WHERE eca.id = in_id AND (location_class = 1 or location_class is null); RETURN out_var; END;
Returns all attributes for the company attached to the entity.
DECLARE t_company company; BEGIN SELECT * INTO t_company FROM company WHERE entity_id = in_entity_id; RETURN t_company; END;
Saves a company. Returns the id number of the record stored.
DECLARE t_entity_id INT; t_company_id INT; t_control_code TEXT; BEGIN t_company_id := in_id; IF in_control_code IS NULL THEN t_control_code := setting_increment('company_control'); ELSE t_control_code := in_control_code; END IF; UPDATE entity SET name = in_name, entity_class = in_entity_class, control_code = in_control_code WHERE id = in_entity_id; IF FOUND THEN t_entity_id = in_entity_id; ELSE INSERT INTO entity (name, entity_class, control_code,country_id) VALUES (in_name, in_entity_class, t_control_code,in_country_id); t_entity_id := currval('entity_id_seq'); END IF; UPDATE company SET legal_name = in_name, tax_id = in_tax_id, sic_code = in_sic_code WHERE id = t_company_id; IF NOT FOUND THEN INSERT INTO company(entity_id, legal_name, tax_id, sic_code) VALUES (t_entity_id, in_name, in_tax_id, in_sic_code); END IF; RETURN t_entity_id; END;
Returns an n dimensional array. Example: SELECT as_array(ARRAY[id::text, class]) from contact_class
aggregate_dummy
tsvector_concat
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
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;
connectby_text
connectby_text
connectby_text_serial
connectby_text_serial
Provides default rules for setting reconciliation labels. Currently saves a label of accno ||'--' || description.
DECLARE v_chart_id int; BEGIN -- Check for existence of the account already PERFORM * FROM cr_coa_to_account WHERE account = in_accno; IF NOT FOUND THEN -- This is a new account. Insert the relevant data. SELECT id INTO v_chart_id FROM chart WHERE accno = in_accno; INSERT INTO cr_coa_to_account (chart_id, account) VALUES (v_chart_id, in_accno||'--'||in_description); END IF; -- Already found, no need to do anything. =) END;
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;
crosstab
crosstab
crosstab_hash
crosstab
crosstab
crosstab
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;
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;
This function return each year inside transdate in transactions. Currently it uses a sparse index scan because the number of rows returned is very small and the table can be very large.
DECLARE next_record int; BEGIN SELECT MIN(EXTRACT ('YEAR' FROM transdate))::INT INTO next_record FROM acc_trans; LOOP EXIT WHEN next_record IS NULL; RETURN NEXT next_record; SELECT MIN(EXTRACT ('YEAR' FROM transdate))::INT AS YEAR INTO next_record FROM acc_trans WHERE EXTRACT ('YEAR' FROM transdate) > next_record; END LOOP; END;
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;
This function return the default currency asigned by the program.
DECLARE defaultcurrency defaults.value%TYPE; BEGIN SELECT INTO defaultcurrency substr(value,1,3) FROM defaults WHERE setting_key = 'curr'; RETURN NEXT defaultcurrency; END;
begin delete from dpt_trans where trans_id = old.id; return NULL; end;
declare t_transdate date; t_curr char(3); t_id int; d_curr text; begin select into d_curr substr(value,1,3) from defaults where setting_key = 'curr'; if TG_RELNAME = 'ar' then select into t_curr, t_transdate curr, transdate from ar where id = old.id; end if; if TG_RELNAME = 'ap' then select into t_curr, t_transdate curr, transdate from ap where id = old.id; end if; if TG_RELNAME = 'oe' then select into t_curr, t_transdate curr, transdate from oe where id = old.id; end if; if d_curr != t_curr then select into t_id a.id from acc_trans ac join ar a on (a.id = ac.trans_id) where a.curr = t_curr and ac.transdate = t_transdate except select a.id from ar a where a.id = old.id union select a.id from acc_trans ac join ap a on (a.id = ac.trans_id) where a.curr = t_curr and ac.transdate = t_transdate except select a.id from ap a where a.id = old.id union select o.id from oe o where o.curr = t_curr and o.transdate = t_transdate except select o.id from oe o where o.id = old.id; if not found then delete from exchangerate where curr = t_curr and transdate = t_transdate; end if; end if; return old; end;
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;
begin delete from yearend where trans_id = old.id; return NULL; end;
SELECT * FROM department order by description;
SELECT * FROM department ORDER BY description
This function returns all department that match the role provided as the argument.
DECLARE out_department department%ROWTYPE; BEGIN FOR out_department IN SELECT * from department WHERE role = coalesce(in_role, role) LOOP return next out_department; END LOOP; END;
tsa_dex_init
tsa_dex_lexize
Searches for drafts. in_type may be any of 'ar', 'ap', or 'gl'.
DECLARE out_row RECORD; BEGIN FOR out_row IN SELECT trans.id, trans.transdate, trans.invoice, trans.reference, trans.description, sum(case when lower(in_type) = 'ap' AND chart.link = 'AP' THEN line.amount WHEN lower(in_type) = 'ar' AND chart.link = 'AR' THEN line.amount * -1 WHEN lower(in_type) = 'gl' AND line.amount > 0 THEN line.amount ELSE 0 END) as amount FROM ( SELECT id, transdate, reference, description, false as invoice, approved from gl WHERE lower(in_type) = 'gl' UNION SELECT id, transdate, invnumber as reference, (SELECT name FROM eca__get_entity(entity_credit_account)), invoice, approved from ap WHERE lower(in_type) = 'ap' UNION SELECT id, transdate, invnumber as reference, description, invoice, approved from ar WHERE lower(in_type) = 'ar' ) trans JOIN acc_trans line ON (trans.id = line.trans_id) JOIN chart ON (line.chart_id = chart.id and charttype = 'A') LEFT JOIN voucher v ON (v.trans_id = trans.id) WHERE (in_from_date IS NULL or trans.transdate >= in_from_date) AND (in_to_date IS NULL or trans.transdate <= in_to_date) AND trans.approved IS FALSE AND v.id IS NULL GROUP BY trans.id, trans.transdate, trans.description, trans.reference, trans.invoice HAVING (in_with_accno IS NULL or in_with_accno = ANY(as_array(chart.accno))) ORDER BY trans.reference LOOP RETURN NEXT out_row; END LOOP; END;
Deletes the draft from the book. Only will delete unapproved transactions. Otherwise an exception is raised and the transaction terminated.
declare t_table text; begin SELECT table_name into t_table FROM transactions where id = in_id; IF (t_table = 'ar') THEN PERFORM cogs__add_for_ar_line(id) FROM invoice WHERE trans_id = in_id; UPDATE ar set approved = true where id = in_id; ELSIF (t_table = 'ap') THEN PERFORM cogs__add_for_ap_line(id) FROM invoice WHERE trans_id = in_id; UPDATE ap set approved = true where id = in_id; ELSIF (t_table = 'gl') THEN UPDATE gl set approved = true where id = in_id; ELSE raise exception 'Invalid table % in draft_approve for transaction %', t_table, in_id; END IF; IF NOT FOUND THEN RETURN FALSE; END IF; UPDATE transactions SET approved_by = (select entity_id FROM users WHERE username = SESSION_USER), approved_at = now() WHERE id = in_id; RETURN TRUE; END;
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;
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;
Returns true if at least one record was deleted. False if no records were affected.
BEGIN DELETE FROM eca_to_contact WHERE credit_id = in_credit_id and contact_class_id = in_contact_class_id and contact= in_contact; RETURN FOUND; END;
Deletes the record identified. Returns true if successful, false if no record found.
BEGIN DELETE FROM eca_to_location WHERE credit_id = in_credit_id AND location_id = in_location_id AND location_class = in_location_class; RETURN FOUND; END;
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;
Returns a set of (only one) entity to which the entity credit account is attached.
declare v_row entity; BEGIN SELECT entity.* INTO v_row FROM entity_credit_account JOIN entity ON entity_credit_account.entity_id = entity.id WHERE entity_credit_account.id = in_credit_id; IF NOT FOUND THEN raise exception 'Could not find entity with ID %', in_credit_id; ELSE return next v_row; END IF; END;
This returns the pricematrix for the customer or vendor (entity_credit_account identified by in_id), orderd by partnumber, validfrom
SELECT pc.parts_id, p.partnumber, p.description, pc.credit_id, pc.pricebreak, pc.sellprice, NULL, NULL::int, NULL, pc.validfrom, pc.validto, pc.curr, pc.entry_id FROM partscustomer pc JOIN parts p on pc.parts_id = p.id JOIN entity_credit_account eca ON pc.credit_id = eca.id WHERE pc.credit_id = $1 AND eca.entity_class = 2 UNION SELECT pv.parts_id, p.partnumber, p.description, pv.credit_id, NULL, NULL, pv.lastcost, pv.leadtime::int, pv.partnumber, NULL, NULL, pv.curr, pv.entry_id FROM partsvendor pv JOIN parts p on pv.parts_id = p.id JOIN entity_credit_account eca ON pv.credit_id = eca.id WHERE pv.credit_id = $1 and eca.entity_class = 1 ORDER BY partnumber, validfrom
SELECT pc.parts_id, p.partnumber, p.description, pc.credit_id, pc.pricebreak, pc.sellprice, NULL::numeric, NULL::int, NULL::text, pc.validfrom, pc.validto, pc.curr, pc.entry_id FROM partscustomer pc JOIN parts p on pc.parts_id = p.id JOIN entity_credit_account eca ON pc.pricegroup_id = eca.pricegroup_id WHERE eca.id = $1 AND eca.entity_class = 2
Returns a set of taxable account id's.
select * from customertax where customer_id = $1 union select * from vendortax where vendor_id = $1;
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;
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;
Saves a location to an entity credit account. Returns id of saved record.
DECLARE l_row location; l_id INT; l_orig_id INT; BEGIN UPDATE eca_to_location SET location_class = in_location_class WHERE credit_id = in_credit_id AND location_class = in_old_location_class AND location_id = in_location_id; IF FOUND THEN SELECT location_save( in_location_id, in_line_one, in_line_two, in_line_three, in_city, in_state, in_mail_code, in_country_code ) INTO l_id; ELSE SELECT location_save( NULL, in_line_one, in_line_two, in_line_three, in_city, in_state, in_mail_code, in_country_code ) INTO l_id; INSERT INTO eca_to_location (credit_id, location_class, location_id) VALUES (in_credit_id, in_location_class, l_id); END IF; RETURN l_id; END;
Saves bank account to the credit account.
DECLARE out_id int; BEGIN UPDATE entity_bank_account SET bic = in_bic, iban = in_iban WHERE id = in_bank_account_id; IF FOUND THEN out_id = in_bank_account_id; ELSE INSERT INTO entity_bank_account(entity_id, bic, iban) VALUES(in_entity_id, in_bic, in_iban); SELECT CURRVAL('entity_bank_account_id_seq') INTO out_id ; END IF; IF in_credit_id IS NOT NULL THEN UPDATE entity_credit_account SET bank_account = out_id WHERE id = in_credit_id; END IF; RETURN out_id; END;
Saves the contact record at the entity credit account level. Returns 1.
DECLARE out_id int; BEGIN PERFORM * FROM eca_to_contact WHERE credit_id = in_credit_id AND contact_class_id = in_old_contact_class AND contact = in_old_contact; IF FOUND THEN UPDATE eca_to_contact SET contact = in_contact, description = in_description, contact_class_id = in_contact_class WHERE credit_id = in_credit_id AND contact_class_id = in_old_contact_class AND contact = in_old_contact; ELSE INSERT INTO eca_to_contact(credit_id, contact_class_id, description, contact) VALUES (in_credit_id, in_contact_class, in_description, in_contact); END IF; RETURN 1; END;
Saves an entity credit account-level note. Such a note is valid for only one credit account. Returns the id of the note.
DECLARE out_id int; BEGIN -- TODO, change this to create vector too INSERT INTO eca_note (ref_key, note_class, note, vector, subject) VALUES (in_credit_id, 3, in_note, '', in_subject); SELECT currval('note_id_seq') INTO out_id; RETURN out_id; END;
DECLARE retval eca__pricematrix; t_insert bool; BEGIN t_insert := false; PERFORM * FROM entity_credit_account WHERE id = in_credit_id AND entity_class = 1; IF FOUND THEN -- VENDOR UPDATE partsvendor SET lastcost = in_price, leadtime = in_lead_time, partnumber = in_partnumber, curr = in_curr WHERE credit_id = in_credit_id AND entry_id = in_entry_id; IF NOT FOUND THEN INSERT INTO partsvendor (parts_id, credit_id, lastcost, leadtime, partnumber, curr) VALUES (in_parts_id, in_credit_id, in_price, in_leadtime::int2, in_partnumber, in_curr); END IF; SELECT pv.parts_id, p.partnumber, p.description, pv.credit_id, NULL, NULL, pv.lastcost, pv.leadtime::int, pv.partnumber, NULL, NULL, pv.curr, pv.entry_id INTO retval FROM partsvendor pv JOIN parts p ON p.id = pv.parts_id WHERE parts_id = in_parts_id and credit_id = in_credit_id; RETURN retval; END IF; PERFORM * FROM entity_credit_account WHERE id = in_credit_id AND entity_class = 2; IF FOUND THEN -- CUSTOMER UPDATE partscustomer SET pricebreak = in_pricebreak, sellprice = in_price, validfrom = in_validfrom, validto = in_validto, curr = in_curr WHERE entry_id = in_entry_id and credit_id = in_credit_id; IF NOT FOUND THEN INSERT INTO partscustomer (parts_id, credit_id, sellprice, validfrom, validto, curr) VALUES (in_parts_id, in_credit_id, in_price, in_validfrom, in_validto, in_curr); t_insert := true; END IF; SELECT pc.parts_id, p.partnumber, p.description, pc.credit_id, pc.pricebreak, pc.sellprice, NULL, NULL, NULL, pc.validfrom, pc.validto, pc.curr, pc.entry_id INTO retval FROM partscustomer pc JOIN parts p on pc.parts_id = p.id WHERE entry_id = CASE WHEN t_insert THEN currval('partscustomer_entry_id_seq') ELSE in_entry_id END; RETURN retval; END IF; RAISE EXCEPTION 'No valid entity credit account found'; END;
Sets the tax values for the customer or vendor. The entity credit account must exist before calling this function, and must have a type of either 1 or 2.
DECLARE eca entity_credit_account; iter int; BEGIN SELECT * FROM entity_credit_account into eca WHERE id = in_credit_id; IF eca.entity_class = 1 then DELETE FROM vendortax WHERE vendor_id = in_credit_id; FOR iter in array_lower(in_tax_ids, 1) .. array_upper(in_tax_ids, 1) LOOP INSERT INTO vendortax (vendor_id, chart_id) values (in_credit_id, in_tax_ids[iter]); END LOOP; ELSIF eca.entity_class = 2 then DELETE FROM customertax WHERE customer_id = in_credit_id; FOR iter in array_lower(in_tax_ids, 1) .. array_upper(in_tax_ids, 1) LOOP INSERT INTO customertax (customer_id, chart_id) values (in_credit_id, in_tax_ids[iter]); END LOOP; ELSE RAISE EXCEPTION 'Wrong entity class or credit account not found!'; END IF; RETURN TRUE; end;
This produces a history detail report, i.e. a list of all products purchased by a customer over a specific date range. meta_number is an exact match, as are in_open and inc_closed. All other fields allow for partial matches. NULL matches all values.
SELECT eca.id, e.name, eca.meta_number, a.id as invoice_id, a.invnumber, a.curr::text, p.id AS parts_id, p.partnumber, i.description, i.qty, i.unit::text, i.sellprice, i.discount, i.deliverydate, pr.id as project_id, pr.projectnumber, i.serialnumber, case when $16 = 1 then xr.buy else xr.sell end as exchange_rate, ee.id as salesperson_id, ep.last_name || ', ' || ep.first_name as salesperson_name FROM (select * from entity_credit_account where meta_number = $2 UNION select * from entity_credit_account WHERE $2 is null ) eca -- broken into unions for performance join entity e on eca.entity_id = e.id JOIN (select invnumber, curr, transdate, entity_credit_account, id, person_id FROM ar where $16 = 2 and $13 = 'i' and (($17 and amount = paid) or ($18 and amount <> paid)) UNION select invnumber, curr, transdate, entity_credit_account, id, person_id FROM ap where $16 = 1 and $13 = 'i' and (($17 and amount = paid) or ($18 and amount <> paid)) union select ordnumber, curr, transdate, entity_credit_account, id, person_id from oe where ($16= 1 and oe.oe_class_id = 2 and $13 = 'o' and quotation is not true) and (($17 and not closed) or ($18 and closed)) union select ordnumber, curr, transdate, entity_credit_account, id, person_id from oe where ($16= 2 and oe.oe_class_id = 1 and $13 = 'o' and quotation is not true) and (($17 and not closed) or ($18 and closed)) union select quonumber, curr, transdate, entity_credit_account, id, person_id from oe where($16= 1 and oe.oe_class_id = 4 and $13 = 'q' and quotation is true) and (($17 and not closed) or ($18 and closed)) union select quonumber, curr, transdate, entity_credit_account, id, person_id from oe where($16= 2 and oe.oe_class_id = 4 and $13 = 'q' and quotation is true) and (($17 and not closed) or ($18 and closed)) ) a ON (a.entity_credit_account = eca.id) -- broken into unions -- for performance JOIN ( select trans_id, parts_id, qty, description, unit, discount, deliverydate, serialnumber, project_id, sellprice FROM invoice where $13 = 'i' union select trans_id, parts_id, qty, description, unit, discount, reqdate, serialnumber, project_id, sellprice FROM orderitems where $13 <> 'i' ) i on i.trans_id = a.id JOIN parts p ON (p.id = i.parts_id) LEFT JOIN exchangerate ex ON (ex.transdate = a.transdate) LEFT JOIN project pr ON (pr.id = i.project_id) LEFT JOIN entity ee ON (a.person_id = ee.id) LEFT JOIN person ep ON (ep.entity_id = ee.id) JOIN exchangerate xr ON a.transdate = xr.transdate -- these filters don't perform as well on large databases WHERE (e.name ilike '%' || $1 || '%' or $1 is null) and ($3 is null or eca.id in (select credit_id from eca_to_contact where contact ilike '%' || $3 || '%')) and (($4 is null and $5 is null and $6 is null and $7 is null) or eca.id in (select credit_id from eca_to_location where location_id in (select id from location where ($4 is null or line_one ilike '%' || $4 || '%' or line_two ilike '%' || $4 || '%') and ($5 is null or city ilike '%' || $5 || '%') and ($6 is null or state ilike '%' || $6 || '%') and ($7 is null or mail_code ilike '%' || $7 || '%') and ($10 is null or country_id = $10)) ) ) and (a.transdate >= $11 or $11 is null) and (a.transdate <= $12 or $12 is null) and (eca.startdate >= $14 or $14 is null) and (eca.startdate <= $15 or $15 is null) ORDER BY eca.meta_number;
Creates a summary account (no quantities, just parts group by invoice). meta_number must match exactly or be NULL. inc_open and inc_closed are exact matches too. All other values specify ranges or may match partially.
SELECT id, name, meta_number, null::int, null::text, curr, parts_id, partnumber, description, sum(qty), unit, null::numeric, null::numeric, null::date, null::int, null::text, null::text, null::numeric, null::int, null::text FROM eca_history($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18) group by id, name, meta_number, curr, parts_id, partnumber, description, unit order by meta_number;
SELECT p.entity_id, p.id, s.salutation, p.first_name, p.middle_name, p.last_name, ee.startdate, ee.enddate, ee.role, ee.ssn, ee.sales, ee.manager_id, mp.first_name, mp.last_name, ee.employeenumber, ee.dob, e.country_id FROM person p JOIN entity_employee ee on (ee.entity_id = p.entity_id) JOIN entity e ON (p.entity_id = e.id) LEFT JOIN salutation s on (p.salutation_id = s.id) LEFT JOIN person mp ON ee.manager_id = p.entity_id WHERE ee.role = 'manager' ORDER BY ee.employeenumber;
Returns an employee_result tuple with information specified by the entity_id.
SELECT p.entity_id, p.id, s.salutation, p.first_name, p.middle_name, p.last_name, ee.startdate, ee.enddate, ee.role, ee.ssn, ee.sales, ee.manager_id, mp.first_name, mp.last_name, ee.employeenumber, ee.dob, e.country_id FROM person p JOIN entity_employee ee on (ee.entity_id = p.entity_id) JOIN entity e ON (p.entity_id = e.id) LEFT JOIN salutation s on (p.salutation_id = s.id) LEFT JOIN person mp ON ee.manager_id = p.entity_id WHERE p.entity_id = $1;
Returns username, user_id, etc. information if the employee is a user.
SELECT * FROM users WHERE entity_id = $1;
Returns a list of managers, that is employees with the 'manager' role set.
DECLARE emp employees%ROWTYPE; BEGIN FOR emp IN SELECT e.salutation, e.first_name, e.last_name, ee.* FROM entity_employee ee JOIN entity e on e.id = ee.entity_id WHERE ee.sales = 't'::bool AND ee.role='manager' AND ee.entity_id <> coalesce(in_id, -1) ORDER BY name LOOP RETURN NEXT emp; END LOOP; END;
Saves an employeerecord with the specified information.
DECLARE out_id INT; BEGIN UPDATE entity_employee SET startdate = coalesce(in_start_date, now()::date), enddate = in_end_date, dob = in_dob, role = in_role, ssn = in_ssn, manager_id = in_manager_id, employeenumber = in_employee_number WHERE entity_id = in_entity_id; out_id = in_entity_id; IF NOT FOUND THEN INSERT INTO entity_employee (startdate, enddate, dob, role, ssn, manager_id, employeenumber, entity_id) VALUES (coalesce(in_start_date, now()::date), in_end_date, in_dob, in_role, in_ssn, in_manager_id, in_employee_number, in_entity_id); RETURN in_entity_id; END IF; RETURN out_id; END;
Returns a list of employee_result records matching the search criteria. employeenumber is an exact match. stardate_from and startdate_to specify the start dates for employee searches All others are partial matches. NULLs match all values.
SELECT p.entity_id, p.id, s.salutation, p.first_name, p.middle_name, p.last_name, ee.startdate, ee.enddate, ee.role, ee.ssn, ee.sales, ee.manager_id, mp.first_name, mp.last_name, ee.employeenumber, ee.dob, e.country_id FROM person p JOIN entity e ON p.entity_id = e.id JOIN entity_employee ee on (ee.entity_id = p.entity_id) LEFT JOIN salutation s on (p.salutation_id = s.id) LEFT JOIN person mp ON ee.manager_id = p.entity_id WHERE ($7 is null or p.entity_id in (select ref_key from entity_note WHERE note ilike '%' || $7 || '%')) and ($1 is null or $1 = ee.employeenumber) and ($2 is null or $2 <= ee.startdate) and ($3 is null or $3 >= ee.startdate) and ($4 is null or p.first_name ilike '%' || $4 || '%') and ($5 is null or p.middle_name ilike '%' || $5 || '%') and ($6 is null or p.last_name ilike '%' || $6 || '%');
BEGIN DELETE FROM employee WHERE entity_id = in_id; RETURN; END;
DECLARE emp employee_search%ROWTYPE; BEGIN FOR emp IN SELECT * FROM employee_search WHERE coalesce(startdate, 'infinity'::timestamp) >= coalesce(in_startdateto, '-infinity'::timestamp) AND coalesce(startdate, '-infinity'::timestamp) <= coalesce(in_startdatefrom, 'infinity'::timestamp) AND coalesce(enddate, '-infinity'::timestamp) <= coalesce(in_enddateto, 'infinity'::timestamp) AND coalesce(enddate, 'infinity'::timestamp) >= coalesce(in_enddatefrom, '-infinity'::timestamp) AND (name % in_name OR note % in_notes) AND (sales = 't' OR coalesce(in_sales, 'f') = 'f') LOOP RETURN NEXT emp; END LOOP; return; END;
INSERT INTO person_to_location (person_id,location_id) VALUES ($1, $2);
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;
Returns a set of (only one) entity record with the entity id.
declare v_row entity; BEGIN -- Removing the exception when not found handling. Applications are -- perfectly capable of handling whether an entity was not found. No need -- for a database-level exception here. Moreover such results may be useful -- --CT SELECT * INTO v_row FROM entity WHERE id = in_entity_id; return next v_row; END;
Returns the entity row attached to the control code.
SELECT * FROM entity WHERE control_code = $1
Returns a set of (only one) entity record with the entity id.
declare v_row entity; BEGIN -- Removing the exception when not found handling. Applications are -- perfectly capable of handling whether an entity was not found. No need -- for a database-level exception here. Moreover such results may be useful -- --CT SELECT * INTO v_row FROM entity WHERE id = in_entity_id; return next v_row; END;
Returns a list of entity classes, ordered by assigned ids
DECLARE out_row entity_class; BEGIN FOR out_row IN SELECT * FROM entity_class LEFT JOIN defaults ON setting_key = 'roll_prefix' WHERE active and pg_has_role(SESSION_USER, coalesce(defaults.value, 'lsmb_' || current_database() || '__') || 'contact_class_' || lower(regexp_replace(class, ' ', '_')), 'USAGE') ORDER BY id LOOP RETURN NEXT out_row; END LOOP; END;
Returns a list of entity credit account entries for the entity and of the entity class.
DECLARE out_row entity_credit_retrieve; BEGIN FOR out_row IN SELECT c.id, e.id, ec.entity_class, ec.discount, ec.discount_terms, ec.taxincluded, ec.creditlimit, ec.terms, ec.meta_number, ec.description, ec.business_id, ec.language_code, ec.pricegroup_id, ec.curr, ec.startdate, ec.enddate, ec.ar_ap_account_id, ec.cash_account_id, ec.discount_account_id, ec.threshold, e.control_code, ec.id, ec.pay_to_name, ec.taxform_id FROM company c JOIN entity e ON (c.entity_id = e.id) JOIN entity_credit_account ec ON (c.entity_id = ec.entity_id) WHERE e.id = in_entity_id AND ec.entity_class = CASE WHEN in_entity_class = 3 THEN 2 WHEN in_entity_class IS NULL THEN ec.entity_class ELSE in_entity_class END LOOP RETURN NEXT out_row; END LOOP; END;
Saves a bank account to the entity.
DECLARE out_id int; BEGIN UPDATE entity_bank_account SET bic = in_bic, iban = in_iban WHERE id = in_bank_account_id; IF FOUND THEN out_id = in_bank_account_id; ELSE INSERT INTO entity_bank_account(entity_id, bic, iban) VALUES(in_entity_id, in_bic, in_iban); SELECT CURRVAL('entity_bank_account_id_seq') INTO out_id ; END IF; RETURN out_id; END;
Saves an entity-level note. Such a note is valid for all credit accounts attached to that entity. Returns the id of the note.
DECLARE out_id int; BEGIN -- TODO, change this to create vector too INSERT INTO entity_note (ref_key, note_class, entity_id, note, vector, subject) VALUES (in_entity_id, 1, in_entity_id, in_note, '', in_subject); SELECT currval('note_id_seq') INTO out_id; RETURN out_id; END;
Returns the entity credit account info.
SELECT * FROM entity_credit_account WHERE id = $1;
Returns an entity credit id, based on entity_id, entity_class, and meta_number. This is the preferred way to locate an account if all three of these are known
DECLARE out_var int; BEGIN SELECT id INTO out_var FROM entity_credit_account WHERE entity_id = in_entity_id AND in_entity_class = entity_class AND in_meta_number = meta_number; RETURN out_var; END;
Returns the credit id from the meta_number and entity_class.
DECLARE out_credit_id int; BEGIN SELECT id INTO out_credit_id FROM entity_credit_account WHERE meta_number = in_meta_number AND entity_class = in_account_class; RETURN out_credit_id; END;
Saves an entity credit account. Returns the id of the record saved.
DECLARE t_entity_class int; l_id int; t_meta_number text; t_mn_default_key text; BEGIN -- TODO: Move to mapping table. IF in_entity_class = 1 THEN t_mn_default_key := 'vendornumber'; ELSIF in_entity_class = 2 THEN t_mn_default_key := 'customernumber'; END IF; IF in_meta_number IS NULL THEN t_meta_number := setting_increment(t_mn_default_key); ELSE t_meta_number := in_meta_number; END IF; update entity_credit_account SET discount = in_discount, taxincluded = in_taxincluded, creditlimit = in_creditlimit, description = in_description, terms = in_terms, ar_ap_account_id = in_ar_ap_account_id, cash_account_id = in_cash_account_id, discount_account_id = in_discount_account_id, meta_number = t_meta_number, business_id = in_business_id, language_code = in_language_code, pricegroup_id = in_pricegroup_id, curr = in_curr, startdate = in_startdate, enddate = in_enddate, threshold = in_threshold, discount_terms = in_discount_terms, pay_to_name = in_pay_to_name, taxform_id = in_taxform_id where id = in_credit_id; IF FOUND THEN RETURN in_credit_id; ELSE INSERT INTO entity_credit_account ( entity_id, entity_class, discount, description, taxincluded, creditlimit, terms, meta_number, business_id, language_code, pricegroup_id, curr, startdate, enddate, discount_terms, threshold, ar_ap_account_id, pay_to_name, taxform_id, cash_account_id, discount_account_id ) VALUES ( in_entity_id, in_entity_class, in_discount, in_description, in_taxincluded, in_creditlimit, in_terms, t_meta_number, in_business_id, in_language_code, in_pricegroup_id, in_curr, in_startdate, in_enddate, in_discount_terms, in_threshold, in_ar_ap_account_id, in_pay_to_name, in_taxform_id, in_cash_account_id, in_discount_account_id ); RETURN currval('entity_credit_account_id_seq'); END IF; END;
Returns a list of contact classes ordered by ID.
DECLARE out_row RECORD; BEGIN FOR out_row IN SELECT * FROM contact_class ORDER BY id LOOP RETURN NEXT out_row; END LOOP; END;
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;
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;
Creates checkpoints for each account at a specific date. Books are considered closed when they occur before the latest checkpoint timewise. This means that balances (and credit/debit amounts) can be calculated starting at a checkpoint and moving forward (thus providing a mechanism for expunging old data while keeping balances correct at some future point).
DECLARE ret_val int; approval_check int; cp_date date; BEGIN IF in_end_date > now()::date THEN RAISE EXCEPTION 'Invalid date: Must be earlier than present'; END IF; SELECT count(*) into approval_check FROM acc_trans ac JOIN ( select id, approved, transdate FROM ar UNION SELECT id, approved, transdate FROM gl UNION SELECT id, approved, transdate FROM ap ) gl ON (gl.id = ac.trans_id) WHERE (ac.approved IS NOT TRUE AND ac.transdate <= in_end_date) OR (gl.approved IS NOT TRUE AND gl.transdate <= in_end_date); if approval_check > 0 THEN RAISE EXCEPTION 'Unapproved transactions in closed period'; END IF; SELECT max(end_date) INTO cp_date FROM account_checkpoint WHERE end_date < in_end_date; INSERT INTO account_checkpoint (end_date, account_id, amount, debits, credits) SELECT in_end_date, COALESCE(a.chart_id, cp.account_id), COALESCE(SUM (a.amount),0) + coalesce(MAX (cp.amount), 0), COALESCE(SUM (CASE WHEN (a.amount < 0) THEN a.amount ELSE 0 END), 0) + COALESCE( MIN (cp.debits), 0), COALESCE(SUM (CASE WHEN (a.amount > 0) THEN a.amount ELSE 0 END), 0) + COALESCE( MAX (cp.credits), 0) FROM (SELECT * FROM acc_trans WHERE transdate <= in_end_date AND transdate > COALESCE(cp_date, '1200-01-01')) a FULL OUTER JOIN ( select account_id, end_date, amount, debits, credits from account_checkpoint WHERE end_date = cp_date ) cp on (a.chart_id = cp.account_id) group by COALESCE(a.chart_id, cp.account_id); SELECT count(*) INTO ret_val FROM account_checkpoint where end_date = in_end_date; return ret_val; END;
Lists equity accounts for the retained earnings dropdown.
SELECT * FROM account WHERE category = 'Q' ORDER BY accno;
Removes checkpoints and reverses yearend transactions on in_end_date
BEGIN PERFORM count(*) FROM account_checkpoint WHERE end_date = in_end_date; IF NOT FOUND THEN RETURN FALSE; END IF; DELETE FROM account_checkpoint WHERE end_date = in_end_date; PERFORM count(*) FROM yearend WHERE transdate = in_end_date and reversed is not true; IF FOUND THEN INSERT INTO gl (reference, description, approved) SELECT 'Reversing ' || reference, 'Reversing ' || description, true FROM gl WHERE id = (select trans_id from yearend where transdate = in_end_date and reversed is not true); INSERT INTO acc_trans (chart_id, amount, transdate, trans_id, approved) SELECT chart_id, amount * -1, currval('id'), true FROM acc_trans where trans_id = (select trans_id from yearend where transdate = in_end_date and reversed is not true); UPDATE yearend SET reversed = true where transdate = in_end_date and reversed is not true; END IF; DELETE FROM account_checkpoint WHERE end_date = in_end_date; RETURN TRUE; END;
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;
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;
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;
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;
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;
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;
Retrieves the file information specified including content.
SELECT * FROM file_base where id = $1 and file_class = $2;
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)
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);
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;
This function retrieves a list of file attachments on a specified object.
select * from file_links where ref_key = $1 and dest_class = $2;
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;
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;
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;
This opens a form, and returns the id of the form opened.
DECLARE usertest bool; BEGIN SELECT count(*) = 1 INTO usertest FROM session WHERE session_id = in_session_id AND users_id IN (select id from users WHERE username = SESSION_USER); IF usertest is not true THEN RAISE EXCEPTION 'Invalid session'; END IF; INSERT INTO open_forms (session_id) VALUES (in_session_id); RETURN currval('open_forms_id_seq'); END;
tsa_get_covers
SELECT coalesce((select description FROM language WHERE code = (SELECT substring(value, 1, 2) FROM defaults WHERE setting_key = 'default_language')), 'english');
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;
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;
Gets a set of all valid account_link descriptions.
SELECT * FROM account_link_description;
gin_extract_trgm
gin_extract_trgm
gin_trgm_consistent
This provides centralized support for insertions into audittrail.
DECLARE t_reference text; t_row RECORD; BEGIN IF TG_OP = 'INSERT' then t_row := NEW; ELSE t_row := OLD; END IF; IF TG_RELNAME IN ('ar', 'ap') THEN t_reference := t_row.invnumber; ELSE t_reference := t_row.reference; END IF; INSERT INTO audittrail (trans_id, reference, action, person_id) values (t_row.id, t_reference, TG_OP, person__get_my_entity_id()); return null; -- AFTER TRIGGER ONLY, SAFE END;
gtrgm_compress
gtrgm_consistent
gtrgm_decompress
gtrgm_in
gtrgm_out
gtrgm_penalty
gtrgm_picksplit
gtrgm_same
gtrgm_union
ts_headline_byid
ts_headline_byid_opt
tsa_headline_byname
tsa_headline_byname
ts_headline
ts_headline_opt
SELECT CASE WHEN count(*) > 0 THEN true ELSE false END FROM unnest($2) r WHERE t @> array[$1];
SELECT bool_and(in_tree(e, $2)) FROM unnest($1) e;
DECLARE retval ap; BEGIN SELECT * INTO retval FROM ap WHERE entity_credit_id = (select id from entity_credit_account where entity_class = 1 AND meta_number = in_meta_number) AND invnumber = in_invoice_number; RETURN retval; END;
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;
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);
Returns true if the two dates are in the same year, false otherwise.
SELECT extract ('YEAR' from $1) = extract ('YEAR' from $2);
SELECT value FROM menu_attribute where node_id = 74 and attribute = 'rowcount';
BEGIN UPDATE menu_attribute set value = $1 where node_id = 74 and attribute='rowcount'; IF NOT FOUND THEN INSERT INTO menu_attribute (node_id, attribute, value) values (74, 'rowcount', $1); END IF; RETURN $1; END;
BEGIN INSERT INTO pending_job (batch_class, batch_id) VALUES (coalesce(in_batch_class, 3), in_batch_id); RETURN currval('pending_job_id_seq'); END;
DECLARE out_row job__status; BEGIN SELECT (completed_at IS NULL)::INT, success::int, completed_at, error_condition INTO out_row FROM pending_job WHERE id = in_job_id; RETURN out_row; END;
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;
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);
tsvector_length
ts_lexize
tsa_lexize_bycurrent
tsa_lexize_byname
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;
UPDATE location set active = false, inactive_date = now() WHERE id = $1; SELECT * FROM location WHERE id = 1;
Returns the location specified by in_id.
DECLARE out_location location%ROWTYPE; BEGIN SELECT * INTO out_location FROM location WHERE id = in_id; RETURN out_location; END;
DELETES the location specified by in_id. Does not return a value.
BEGIN DELETE FROM location WHERE id = in_id; END;
Returns all locations, ordered by country, state, and city.
DECLARE out_location location%ROWTYPE; BEGIN FOR out_location IN SELECT * FROM location ORDER BY country, state, city LOOP RETURN NEXT out_location; END LOOP; END;
Lists location classes, by default in order entered.
DECLARE out_row RECORD; BEGIN FOR out_row IN SELECT * FROM location_class ORDER BY id LOOP RETURN NEXT out_row; END LOOP; END;
Lists countries, by default in alphabetical order.
DECLARE out_row RECORD; BEGIN FOR out_row IN SELECT * FROM country ORDER BY name LOOP RETURN NEXT out_row; END LOOP; END;
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;
Returns matching locations. All matches may be partial.
DECLARE out_location location%ROWTYPE; BEGIN FOR out_location IN SELECT * FROM location WHERE address1 ilike '%' || in_address1 || '%' AND address2 ilike '%' || in_address2 || '%' AND in_city ilike '%' || in_city || '%' AND in_state ilike '%' || in_state || '%' AND in_zipcode ilike '%' || in_zipcode || '%' AND in_country ilike '%' || in_country || '%' LOOP RETURN NEXT out_location; END LOOP; END;
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;
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;
This function returns the complete menu tree. It is used to generate nested menus for the web interface.
DECLARE item menu_item; arg menu_attribute%ROWTYPE; BEGIN FOR item IN WITH RECURSIVE tree (path, id, parent, level, positions) AS (select id::text as path, id, parent, 0 as level, position::text FROM menu_node where parent is null UNION select path || ',' || n.id::text, n.id, n.parent, t.level + 1, t.positions || ',' || n.position FROM menu_node n JOIN tree t ON t.id = n.parent) SELECT n.position, n.id, c.level, n.label, c.path, n.parent, to_args(array[ma.attribute, ma.value]) FROM tree c JOIN menu_node n USING(id) JOIN menu_attribute ma ON (n.id = ma.node_id) WHERE n.id IN (select node_id FROM menu_acl acl LEFT JOIN pg_roles pr on pr.rolname = acl.role_name WHERE CASE WHEN role_name ilike 'public' THEN true WHEN rolname IS NULL THEN FALSE ELSE pg_has_role(rolname, 'USAGE') END GROUP BY node_id HAVING bool_and(CASE WHEN acl_type ilike 'DENY' THEN FALSE WHEN acl_type ilike 'ALLOW' THEN TRUE END)) or exists (select cn.id, cc.path FROM tree cc JOIN menu_node cn USING(id) WHERE cn.id IN (select node_id FROM menu_acl acl LEFT JOIN pg_roles pr on pr.rolname = acl.role_name WHERE CASE WHEN rolname ilike 'public' THEN true WHEN rolname IS NULL THEN FALSE ELSE pg_has_role(rolname, 'USAGE') END GROUP BY node_id HAVING bool_and(CASE WHEN acl_type ilike 'DENY' THEN false WHEN acl_type ilike 'ALLOW' THEN TRUE END)) and cc.path::text like c.path::text || ',%') GROUP BY n.position, n.id, c.level, n.label, c.path, c.positions, n.parent ORDER BY string_to_array(c.positions, ',')::int[] LOOP RETURN NEXT item; END LOOP; END;
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;
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 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;
normal_rand
tsquery_numnode
ts_parse_byid
tsa_parse_current
ts_parse_byname
Simple way to cast a Perl string to a date format of known type.
select $1;
SELECT * FROM parts WHERE id = $1;
SELECT * FROM PARTS WHERE partnumber = $1 and obsolete is not true;
SELECT * FROM parts WHERE ($1 IS NULL OR (partnumber like $1 || '%')) AND ($2 IS NULL OR (description @@ plainto_tsquery(get_default_lang()::regconfig, $2))) AND not obsolete ORDER BY partnumber;
Reverses a payment. All fields are mandatory except batch_id and voucher_id because they determine the identity of the payment to be reversed.
DECLARE pay_row record; t_voucher_id int; t_voucher_inserted bool; t_currs text[]; t_rev_fx numeric; t_fxgain_id int; t_fxloss_id int; t_paid_fx numeric; BEGIN SELECT * INTO t_rev_fx FROM currency_get_exchangerate( in_currency, in_date_reversed, in_account_class); SELECT * INTO t_paid_fx FROM currency_get_exchangerate( in_currency, in_date_paid, in_account_class); select value::int INTO t_fxgain_id FROM setting_get('fxgain_accno_id'); select value::int INTO t_fxloss_id FROM setting_get('fxloss_accno_id'); SELECT string_to_array(value, ':') into t_currs from defaults where setting_key = 'curr'; IF in_currency IS NULL OR in_currency = t_currs[1] THEN t_rev_fx := 1; t_paid_fx := 1; ELSIF t_rev_fx IS NULL THEN t_rev_fx := in_exchangerate; PERFORM payments_set_exchangerate(in_account_class, in_exchangerate, in_currency, in_date_reversed); ELSIF t_rev_fx <> in_exchangerate THEN RAISE EXCEPTION 'Exchange rate different than on file'; END IF; IF t_rev_fx IS NULL THEN RAISE EXCEPTION 'No exchangerate provided and not default currency'; END IF; IF in_batch_id IS NOT NULL THEN t_voucher_id := nextval('voucher_id_seq'); t_voucher_inserted := FALSE; END IF; FOR pay_row IN SELECT a.*, c.ar_ap_account_id, arap.curr, arap.fxrate FROM acc_trans a JOIN (select id, curr, entity_credit_account, CASE WHEN curr = t_currs[1] THEN 1 ELSE buy END as fxrate FROM ar LEFT JOIN exchangerate USING (transdate, curr) WHERE in_account_class = 2 UNION SELECT id, curr, entity_credit_account, CASE WHEN curr = t_currs[1] THEN 1 ELSE sell END as fxrate FROM ap LEFT JOIN exchangerate USING (transdate, curr) WHERE in_account_class = 1 ) arap ON (a.trans_id = arap.id) JOIN entity_credit_account c ON (arap.entity_credit_account = c.id) JOIN account ch ON (a.chart_id = ch.id) WHERE a.source IS NOT DISTINCT FROM in_source AND a.transdate = in_date_paid AND in_credit_id = arap.entity_credit_account AND in_cash_accno = ch.accno and in_voucher_id IS NOT DISTINCT FROM voucher_id LOOP IF pay_row.curr = t_currs[1] THEN pay_row.fxrate = 1; END IF; IF in_batch_id IS NOT NULL AND t_voucher_inserted IS NOT TRUE THEN INSERT INTO voucher (id, trans_id, batch_id, batch_class) VALUES (t_voucher_id, pay_row.trans_id, in_batch_id, CASE WHEN in_account_class = 1 THEN 4 WHEN in_account_class = 2 THEN 7 END); t_voucher_inserted := TRUE; END IF; INSERT INTO acc_trans (trans_id, chart_id, amount, transdate, source, memo, approved, voucher_id) VALUES (pay_row.trans_id, pay_row.chart_id, pay_row.amount / t_paid_fx * -1 * t_rev_fx, in_date_reversed, in_source, 'Reversing ' || COALESCE(in_source, ''), case when in_batch_id is not null then false else true end, t_voucher_id), (pay_row.trans_id, pay_row.ar_ap_account_id, pay_row.amount / t_paid_fx * pay_row.fxrate, in_date_reversed, in_source, 'Reversing ' || COALESCE(in_source, ''), case when in_batch_id is not null then false else true end, t_voucher_id), (pay_row.trans_id, case when pay_row.fxrate > t_rev_fx THEN t_fxloss_id ELSE t_fxgain_id END, pay_row.amount / t_paid_fx * (t_rev_fx - pay_row.fxrate), in_date_reversed, in_source, 'Reversing ' || COALESCE(in_source, ''), case when in_batch_id is not null then false else true end, t_voucher_id); END LOOP; RETURN 1; END;
This searches for payments. in_date_to and _date_from specify the acceptable date range. All other matches are exact except that null matches all values. Currently (and to support earlier data) we define a payment as a collection of acc_trans records against the same credit account and cash account, on the same day with the same source number, and optionally the same voucher id.
DECLARE out_row payment_record; BEGIN FOR out_row IN select sum(CASE WHEN c.entity_class = 1 then a.amount ELSE a.amount * -1 END), c.meta_number, c.id, co.legal_name, compound_array(ARRAY[ARRAY[ch.id::text, ch.accno, ch.description]]), a.source, b.control_code, b.description, a.voucher_id, a.transdate FROM entity_credit_account c JOIN ( select entity_credit_account, id, curr FROM ar WHERE in_account_class = 2 UNION SELECT entity_credit_account, id, curr FROM ap WHERE in_account_class = 1 ) arap ON (arap.entity_credit_account = c.id) JOIN acc_trans a ON (arap.id = a.trans_id) JOIN chart ch ON (ch.id = a.chart_id) JOIN company co ON (c.entity_id = co.entity_id) LEFT JOIN voucher v ON (v.id = a.voucher_id) LEFT JOIN batch b ON (b.id = v.batch_id) WHERE (ch.accno = in_cash_accno) AND (in_currency IS NULL OR in_currency = arap.curr) AND (c.id = in_credit_id OR in_credit_id IS NULL) AND (a.transdate >= in_date_from OR in_date_from IS NULL) AND (a.transdate <= in_date_to OR in_date_to IS NULL) AND (source = in_source OR in_source IS NULL) GROUP BY c.meta_number, c.id, co.legal_name, a.transdate, a.source, a.memo, b.id, b.control_code, b.description, voucher_id ORDER BY a.transdate, c.meta_number, a.source LOOP RETURN NEXT out_row; END LOOP; END;
This posts the payments for large batch workflows. Note that in_transactions is a two-dimensional numeric array. Of each sub-array, the first element is the (integer) transaction id, and the second is the amount for that transaction.
DECLARE out_count int; t_voucher_id int; t_trans_id int; t_amount numeric; t_ar_ap_id int; t_cash_id int; t_currs text[]; t_exchangerate numeric; BEGIN IF in_batch_id IS NULL THEN -- t_voucher_id := NULL; RAISE EXCEPTION 'Bulk Post Must be from Batch!'; ELSE INSERT INTO voucher (batch_id, batch_class, trans_id) values (in_batch_id, (SELECT batch_class_id FROM batch WHERE id = in_batch_id), in_transactions[1][1]); t_voucher_id := currval('voucher_id_seq'); END IF; SELECT string_to_array(value, ':') into t_currs from defaults where setting_key = 'curr'; IF (in_curr IS NULL OR in_curr = t_currs[0]) THEN t_exchangerate := 1; ELSE t_exchangerate := in_exchangerate; END IF; CREATE TEMPORARY TABLE bulk_payments_in (id int, amount numeric); select id into t_ar_ap_id from chart where accno = in_ar_ap_accno; select id into t_cash_id from chart where accno = in_cash_accno; FOR out_count IN array_lower(in_transactions, 1) .. array_upper(in_transactions, 1) LOOP EXECUTE $E$ INSERT INTO bulk_payments_in(id, amount) VALUES ($E$ || quote_literal(in_transactions[out_count][1]) || $E$, $E$ || quote_literal(in_transactions[out_count][2]) || $E$)$E$; END LOOP; EXECUTE $E$ INSERT INTO acc_trans (trans_id, chart_id, amount, approved, voucher_id, transdate, source, payment_type) SELECT id, case when $E$ || quote_literal(in_account_class) || $E$ = 1 THEN $E$ || t_cash_id || $E$ WHEN $E$ || quote_literal(in_account_class) || $E$ = 2 THEN $E$ || t_ar_ap_id || $E$ ELSE -1 END, amount * $E$|| quote_literal(t_exchangerate) || $E$, CASE WHEN $E$|| t_voucher_id || $E$ IS NULL THEN true ELSE false END, $E$ || t_voucher_id || $E$, $E$|| quote_literal(in_payment_date) ||$E$ , $E$ ||COALESCE(quote_literal(in_source), 'NULL') || $E$ , $E$ || coalesce(quote_literal(in_payment_type), 'NULL') || $E$ FROM bulk_payments_in where amount <> 0 $E$; EXECUTE $E$ INSERT INTO acc_trans (trans_id, chart_id, amount, approved, voucher_id, transdate, source, payment_type) SELECT id, case when $E$ || quote_literal(in_account_class) || $E$ = 1 THEN $E$ || t_ar_ap_id || $E$ WHEN $E$ || quote_literal(in_account_class) || $E$ = 2 THEN $E$ || t_cash_id || $E$ ELSE -1 END, amount * -1 * $E$|| quote_literal(t_exchangerate) || $E$, CASE WHEN $E$|| t_voucher_id || $E$ IS NULL THEN true ELSE false END, $E$ || t_voucher_id || $E$, $E$|| quote_literal(in_payment_date) ||$E$ , $E$ ||COALESCE(quote_literal(in_source), 'null') ||$E$ , $E$ || coalesce(quote_literal(in_payment_type), 'NULL') || $E$ FROM bulk_payments_in where amount <> 0 $E$; IF in_account_class = 1 THEN EXECUTE $E$ UPDATE ap set paid = paid + (select amount from bulk_payments_in b where b.id = ap.id) where id in (select id from bulk_payments_in) $E$; ELSE EXECUTE $E$ UPDATE ar set paid = paid + (select amount from bulk_payments_in b where b.id = ar.id) where id in (select id from bulk_payments_in) $E$; END IF; EXECUTE $E$ DROP TABLE bulk_payments_in $E$; perform unlock_all(); return out_count; END;
This posts the payments for large batch workflows. Note that in_transactions is a two-dimensional numeric array. Of each sub-array, the first element is the (integer) transaction id, and the second is the amount for that transaction.
DECLARE out_count int; t_voucher_id int; t_trans_id int; t_amount numeric; t_ar_ap_id int; t_cash_id int; t_currs text[]; t_exchangerate numeric; t_cash_sign int; BEGIN SELECT * INTO t_exchangerate FROM currency_get_exchangerate( in_currency, in_payment_date, in_account_class); IF in_batch_id IS NULL THEN -- t_voucher_id := NULL; RAISE EXCEPTION 'Bulk Post Must be from Batch!'; ELSE INSERT INTO voucher (batch_id, batch_class, trans_id) values (in_batch_id, (SELECT batch_class_id FROM batch WHERE id = in_batch_id), in_transactions[1][1]); t_voucher_id := currval('voucher_id_seq'); END IF; SELECT string_to_array(value, ':') into t_currs from defaults where setting_key = 'curr'; IF (in_currency IS NULL OR in_currency = t_currs[1]) THEN t_exchangerate := 1; ELSIF t_exchangerate IS NULL THEN t_exchangerate := in_exchangerate; PERFORM payments_set_exchangerate(in_account_class, in_exchangerate, in_currency, in_payment_date); ELSIF t_exchangerate <> in_exchangerate THEN RAISE EXCEPTION 'Exchange rate different than on file'; END IF; IF t_exchangerate IS NULL THEN RAISE EXCEPTION 'No exchangerate provided and not default currency'; END IF; CREATE TEMPORARY TABLE bulk_payments_in (id int, amount numeric, fxrate numeric, gain_loss_accno int); select id into t_ar_ap_id from chart where accno = in_ar_ap_accno; select id into t_cash_id from chart where accno = in_cash_accno; FOR out_count IN array_lower(in_transactions, 1) .. array_upper(in_transactions, 1) LOOP -- Fill the bulk payments table INSERT INTO bulk_payments_in(id, amount) VALUES (in_transactions[out_count][1], in_transactions[out_count][2]); END LOOP; IF in_account_class = 1 THEN t_cash_sign := 1; ELSE t_cash_sign := -1; END IF; IF (in_currency IS NULL OR in_currency = t_currs[1]) THEN UPDATE bulk_payments_in SET fxrate = 1; ELSE UPDATE bulk_payments_in SET fxrate = (SELECT CASE WHEN in_account_class = 1 THEN sell ELSE buy END FROM exchangerate e JOIN (SELECT transdate, id, curr FROM ar UNION SELECT transdate, id, curr FROM ap) a ON (e.transdate = a.transdate AND e.curr = a.curr) WHERE a.id = bulk_payments_in.id); UPDATE bulk_payments_in SET gain_loss_accno = (SELECT value::int FROM defaults WHERE setting_key = 'fxgain_accno_id') WHERE ((t_exchangerate - bulk_payments_in.fxrate) * t_cash_sign) < 0; UPDATE bulk_payments_in SET gain_loss_accno = (SELECT value::int FROM defaults WHERE setting_key = 'fxloss_accno_id') WHERE ((t_exchangerate - bulk_payments_in.fxrate) * t_cash_sign) > 0; -- explicitly leave zero gain/loss accno_id entries at NULL -- so we have an easy check for which END IF; -- Insert cash side INSERT INTO acc_trans (trans_id, chart_id, amount, approved, voucher_id, transdate, source) SELECT id, t_cash_id, amount * t_cash_sign * t_exchangerate/fxrate, CASE WHEN t_voucher_id IS NULL THEN true ELSE false END, t_voucher_id, in_payment_date, in_source FROM bulk_payments_in where amount <> 0; -- early payment discounts INSERT INTO acc_trans (trans_id, chart_id, amount, approved, voucher_id, transdate, source) SELECT bpi.id, eca.discount_account_id, amount * t_cash_sign * t_exchangerate/fxrate / (1 - discount::numeric/100) * (discount::numeric/100), CASE WHEN t_voucher_id IS NULL THEN true ELSE false END, t_voucher_id, in_payment_date, in_source FROM bulk_payments_in bpi JOIN (select entity_credit_account, id, transdate FROM ar WHERE in_account_class = 2 UNION SELECT entity_credit_account, id, transdate FROM ap WHERE in_account_class = 1) gl ON gl.id = bpi.id JOIN entity_credit_account eca ON gl.entity_credit_account = eca.id WHERE bpi.amount <> 0 AND extract('days' from age(gl.transdate)) < eca.discount_terms; INSERT INTO acc_trans (trans_id, chart_id, amount, approved, voucher_id, transdate, source) SELECT bpi.id, t_ar_ap_id, amount * t_cash_sign * -1 * t_exchangerate/fxrate / (1 - discount::numeric/100) * (discount::numeric/100), CASE WHEN t_voucher_id IS NULL THEN true ELSE false END, t_voucher_id, in_payment_date, in_source FROM bulk_payments_in bpi JOIN (select entity_credit_account, id, transdate FROM ar WHERE in_account_class = 2 UNION SELECT entity_credit_account, id, transdate FROM ap WHERE in_account_class = 1) gl ON gl.id = bpi.id JOIN entity_credit_account eca ON gl.entity_credit_account = eca.id WHERE bpi.amount <> 0 AND extract('days' from age(gl.transdate)) < eca.discount_terms; -- Insert ar/ap side INSERT INTO acc_trans (trans_id, chart_id, amount, approved, voucher_id, transdate, source) SELECT id, t_ar_ap_id, amount * -1 * t_cash_sign, CASE WHEN t_voucher_id IS NULL THEN true ELSE false END, t_voucher_id, in_payment_date, in_source FROM bulk_payments_in where amount <> 0; -- Insert fx gain/loss effects, if applicable INSERT INTO acc_trans (trans_id, chart_id, amount, approved, voucher_id, transdate, source) SELECT id, gain_loss_accno, amount * t_cash_sign * (1 - t_exchangerate/fxrate), CASE WHEN t_voucher_id IS NULL THEN true ELSE false END, t_voucher_id, in_payment_date, in_source FROM bulk_payments_in WHERE amount <> 0 AND gain_loss_accno IS NOT NULL; DROP TABLE bulk_payments_in; perform unlock_all(); return out_count; END;
BEGIN INSERT INTO payments_queue (transactions, batch_id, source, total, ar_ap_accno, cash_accno, payment_date, account_class) VALUES (in_transactions, in_batch_id, in_source, in_total, in_ar_ap_accno, in_cash_accno, in_payment_date, in_account_class); RETURN array_upper(in_transactions, 1) - array_lower(in_transactions, 1); END;
This function finds a payment based on the id and retrieves the record, it is usefull for printing payments :)
DECLARE out_payment payment_header_item; BEGIN FOR out_payment IN SELECT p.id as payment_id, p.reference as payment_reference, p.payment_date, c.legal_name as legal_name, am.amount as amount, em.first_name, em.last_name, p.currency, p.notes FROM payment p JOIN entity_employee ent_em ON (ent_em.entity_id = p.employee_id) JOIN person em ON (ent_em.entity_id = em.entity_id) JOIN entity_credit_account eca ON (eca.id = p.entity_credit_id) JOIN company c ON (c.entity_id = eca.entity_id) JOIN payment_links pl ON (p.id = pl.payment_id) LEFT JOIN ( SELECT sum(a.amount) as amount FROM acc_trans a JOIN account acc ON (a.chart_id = acc.id) JOIN account_link al ON (acc.id =al.account_id) JOIN payment_links pl ON (pl.entry_id=a.entry_id) WHERE al.description in ('AP_paid', 'AP_discount', 'AR_paid', 'AR_discount') and ((in_account_class = 1 AND al.description like 'AP%') or (in_account_class = 2 AND al.description like 'AR%')) ) am ON (true) WHERE p.id = in_payment_id LOOP RETURN NEXT out_payment; END LOOP; END;
This function finds a payment based on the id and retrieves all the line records, it is usefull for printing payments and build reports :)
DECLARE out_payment_line payment_line_item; BEGIN FOR out_payment_line IN SELECT pl.payment_id, ac.entry_id, pl.type as link_type, ac.trans_id, a.invnumber as invoice_number, ac.chart_id, ch.accno as chart_accno, ch.description as chart_description, ch.link as chart_link, ac.amount, ac.transdate as trans_date, ac.source, ac.cleared_on, ac.fx_transaction, ac.project_id, ac.memo, ac.invoice_id, ac.approved, ac.cleared_on, ac.reconciled_on FROM acc_trans ac JOIN payment_links pl ON (pl.entry_id = ac.entry_id ) JOIN chart ch ON (ch.id = ac.chart_id) LEFT JOIN (SELECT id,invnumber FROM ar WHERE in_account_class = 2 UNION SELECT id,invnumber FROM ap WHERE in_account_class = 1 ) a ON (ac.trans_id = a.id) WHERE pl.payment_id = in_payment_id LOOP RETURN NEXT out_payment_line; END LOOP; END;
This function takes a single argument (1 for vendor, 2 for customer as always) and returns all entities with accounts of the appropriate type.
DECLARE out_entity entity%ROWTYPE; BEGIN FOR out_entity IN SELECT ec.id, e.name, e.entity_class, e.created FROM entity e JOIN entity_credit_account ec ON (ec.entity_id = e.id) WHERE e.entity_class = in_account_class LOOP RETURN NEXT out_entity; END LOOP; END;
This function takes the following arguments (all prefaced with in_ in the db): account_class: 1 for vendor, 2 for customer business_type: integer of business.id. currency: char(3) of currency (for example 'USD') date_from, date_to: These dates are inclusive. batch_id: For payment batches, where fees are concerned. ar_ap_accno: The AR/AP account number. This then returns a set of contact information with a 2 dimensional array cnsisting of outstanding invoices. Note that the payment selection logic is that this returns all invoices which are either approved or in the batch_id specified. It also locks the invoices using the LedgerSMB discretionary locking framework, and if not possible, returns the username of the individual who has the lock.
DECLARE payment_item payment_contact_invoice; BEGIN FOR payment_item IN SELECT c.id AS contact_id, e.control_code as econtrol_code, c.description as eca_description, e.name AS contact_name, c.meta_number AS account_number, sum( case when u.username IS NULL or u.username = SESSION_USER THEN coalesce(p.due::numeric, 0) - CASE WHEN c.discount_terms > extract('days' FROM age(a.transdate)) THEN 0 ELSE (coalesce(p.due::numeric, 0)) * coalesce(c.discount::numeric, 0) / 100 END ELSE 0::numeric END) AS total_due, compound_array(ARRAY[[ a.id::text, a.invnumber, a.transdate::text, a.amount::text, (a.amount - p.due)::text, (CASE WHEN c.discount_terms < extract('days' FROM age(a.transdate)) THEN 0 ELSE (coalesce(p.due, 0) * coalesce(c.discount, 0) / 100) END)::text, (coalesce(p.due, 0) - (CASE WHEN c.discount_terms < extract('days' FROM age(a.transdate)) THEN 0 ELSE (coalesce(p.due, 0)) * coalesce(c.discount, 0) / 100 END))::text, case when u.username IS NOT NULL and u.username <> SESSION_USER THEN 0::text ELSE 1::text END, COALESCE(u.username, 0::text) ]]), sum(case when a.batch_id = in_batch_id then 1 else 0 END), bool_and(lock_record(a.id, (select max(session_id) FROM "session" where users_id = ( select id from users WHERE username = SESSION_USER)))) FROM entity e JOIN entity_credit_account c ON (e.id = c.entity_id) JOIN (SELECT ap.id, invnumber, transdate, amount, entity_id, curr, 1 as invoice_class, entity_credit_account, on_hold, v.batch_id, approved, paid FROM ap LEFT JOIN (select * from voucher where batch_class = 1) v ON (ap.id = v.trans_id) WHERE in_account_class = 1 AND (v.batch_class = 1 or v.batch_id IS NULL) UNION SELECT ar.id, invnumber, transdate, amount, entity_id, curr, 2 as invoice_class, entity_credit_account, on_hold, v.batch_id, approved, paid FROM ar LEFT JOIN (select * from voucher where batch_class = 2) v ON (ar.id = v.trans_id) WHERE in_account_class = 2 AND (v.batch_class = 2 or v.batch_id IS NULL) ORDER BY transdate ) a ON (a.entity_credit_account = c.id) JOIN transactions t ON (a.id = t.id) JOIN (SELECT acc_trans.trans_id, sum(CASE WHEN in_account_class = 1 THEN amount WHEN in_account_class = 2 THEN amount * -1 END) AS due FROM acc_trans JOIN account coa ON (coa.id = acc_trans.chart_id) JOIN account_link al ON (al.account_id = coa.id) LEFT JOIN voucher v ON (acc_trans.voucher_id = v.id) WHERE ((al.description = 'AP' AND in_account_class = 1) OR (al.description = 'AR' AND in_account_class = 2)) AND (approved IS TRUE or v.batch_class IN (3, 6)) GROUP BY acc_trans.trans_id) p ON (a.id = p.trans_id) LEFT JOIN "session" s ON (s."session_id" = t.locked_by) LEFT JOIN users u ON (u.id = s.users_id) WHERE (a.batch_id = in_batch_id OR (a.invoice_class = in_account_class AND a.approved AND due <> 0 AND NOT a.on_hold AND a.curr = in_currency AND EXISTS (select trans_id FROM acc_trans WHERE trans_id = a.id AND chart_id = (SELECT id from account WHERE accno = in_ar_ap_accno) ))) AND (in_meta_number IS NULL OR in_meta_number = c.meta_number) GROUP BY c.id, e.name, c.meta_number, c.threshold, e.control_code, c.description HAVING (sum(p.due) >= c.threshold OR sum(case when a.batch_id = in_batch_id then 1 else 0 END) > 0) ORDER BY c.meta_number ASC LOOP RETURN NEXT payment_item; END LOOP; END;
DECLARE out_overpayment payment_overpayments_available_amount; BEGIN FOR out_overpayment IN SELECT chart_id, accno, chart_description, abs(sum(available)) FROM overpayments WHERE payment_class = in_account_class AND entity_credit_id = in_entity_credit_id AND available <> 0 GROUP BY chart_id, accno, chart_description LOOP RETURN NEXT out_overpayment; END LOOP; END;
Returns payment information on the entity credit account as required to for discount calculations and payment processing.
SELECT ec.id, cp.legal_name || coalesce(':' || ec.description,'') as name, e.entity_class, ec.discount_account_id, ec.meta_number FROM entity_credit_account ec JOIN entity e ON (ec.entity_id = e.id) JOIN company cp ON (cp.entity_id = e.id) WHERE ec.id = $1;
Returns a minimal set of information about customer or vendor accounts as needed for discount calculations and the like.
DECLARE out_entity payment_vc_info; BEGIN FOR out_entity IN SELECT ec.id, cp.legal_name || coalesce(':' || ec.description,'') as name, e.entity_class, ec.discount_account_id, ec.meta_number FROM entity_credit_account ec JOIN entity e ON (ec.entity_id = e.id) JOIN company cp ON (cp.entity_id = e.id) WHERE ec.entity_class = in_account_class AND (cp.legal_name ilike coalesce('%'||in_vc_name||'%','%%') OR cp.tax_id = in_vc_idn) LOOP RETURN NEXT out_entity; END LOOP; END;
This function takes a single argument (1 for vendor, 2 for customer as always) and returns all entities with open accounts of the appropriate type.
DECLARE out_entity entity%ROWTYPE; BEGIN FOR out_entity IN SELECT ec.id, cp.legal_name as name, e.entity_class, e.created FROM entity e JOIN entity_credit_account ec ON (ec.entity_id = e.id) JOIN company cp ON (cp.entity_id = e.id) WHERE ec.entity_class = in_account_class AND CASE WHEN in_account_class = 1 THEN ec.id IN (SELECT entity_credit_account FROM acc_trans JOIN chart ON (acc_trans.chart_id = chart.id) JOIN ap ON (acc_trans.trans_id = ap.id) WHERE link = 'AP' GROUP BY chart_id, trans_id, entity_credit_account HAVING SUM(acc_trans.amount) <> 0) WHEN in_account_class = 2 THEN ec.id IN (SELECT entity_credit_account FROM acc_trans JOIN chart ON (acc_trans.chart_id = chart.id) JOIN ar ON (acc_trans.trans_id = ar.id) WHERE link = 'AR' GROUP BY chart_id, trans_id, entity_credit_account HAVING SUM(acc_trans.amount) <> 0) END LOOP RETURN NEXT out_entity; END LOOP; END;
This function is based on payment_get_open_invoices and returns only one invoice if the in_invnumber is set. if no in_invnumber is passed this function behaves the same as payment_get_open_invoices
DECLARE payment_inv payment_invoice; BEGIN FOR payment_inv IN SELECT * from payment_get_open_invoices(in_account_class, in_entity_credit_id, in_curr, in_datefrom, in_dateto, in_amountfrom, in_amountto, in_department_id) WHERE (invnumber like in_invnumber OR in_invnumber IS NULL) LOOP RETURN NEXT payment_inv; END LOOP; END;
This function is the base for get_open_invoice and returns all open invoices for the entity_credit_id it has a lot of options to enable filtering and use the same logic for entity_class_id and currency.
DECLARE payment_inv payment_invoice; BEGIN FOR payment_inv IN SELECT a.id AS invoice_id, a.invnumber AS invnumber,a.invoice AS invoice, a.transdate AS invoice_date, a.amount AS amount, a.amount/ (CASE WHEN a.curr = (SELECT * from defaults_get_defaultcurrency()) THEN 1 ELSE (CASE WHEN in_account_class = 2 THEN ex.buy ELSE ex.sell END) END) as amount_fx, (CASE WHEN c.discount_terms < extract('days' FROM age(a.transdate)) THEN 0 ELSE (coalesce(ac.due, a.amount)) * coalesce(c.discount, 0) / 100 END) AS discount, (CASE WHEN c.discount_terms < extract('days' FROM age(a.transdate)) THEN 0 ELSE (coalesce(ac.due, a.amount)) * coalesce(c.discount, 0) / 100 END)/ (CASE WHEN a.curr = (SELECT * from defaults_get_defaultcurrency()) THEN 1 ELSE (CASE WHEN in_account_class = 2 THEN ex.buy ELSE ex.sell END) END) as discount_fx, ac.due - (CASE WHEN c.discount_terms < extract('days' FROM age(a.transdate)) THEN 0 ELSE (coalesce(ac.due, a.amount)) * coalesce(c.discount, 0) / 100 END) AS due, (ac.due - (CASE WHEN c.discount_terms < extract('days' FROM age(a.transdate)) THEN 0 ELSE (coalesce(ac.due, a.amount)) * coalesce(c.discount, 0) / 100 END))/ (CASE WHEN a.curr = (SELECT * from defaults_get_defaultcurrency()) THEN 1 ELSE (CASE WHEN in_account_class = 2 THEN ex.buy ELSE ex.sell END) END) AS due_fx, (CASE WHEN a.curr = (SELECT * from defaults_get_defaultcurrency()) THEN 1 ELSE (CASE WHEN in_account_class = 2 THEN ex.buy ELSE ex.sell END) END) AS exchangerate --TODO HV prepare drop entity_id from ap,ar --FROM (SELECT id, invnumber, transdate, amount, entity_id, FROM (SELECT id, invnumber, invoice, transdate, amount, 1 as invoice_class, curr, entity_credit_account, department_id, approved FROM ap UNION --SELECT id, invnumber, transdate, amount, entity_id, SELECT id, invnumber, invoice, transdate, amount, 2 AS invoice_class, curr, entity_credit_account, department_id, approved FROM ar ) a JOIN (SELECT trans_id, chart_id, sum(CASE WHEN in_account_class = 1 THEN amount WHEN in_account_class = 2 THEN amount * -1 END) as due FROM acc_trans GROUP BY trans_id, chart_id) ac ON (ac.trans_id = a.id) JOIN chart ON (chart.id = ac.chart_id) LEFT JOIN exchangerate ex ON ( ex.transdate = a.transdate AND ex.curr = a.curr ) JOIN entity_credit_account c ON (c.id = a.entity_credit_account) -- OR (a.entity_credit_account IS NULL and a.entity_id = c.entity_id)) WHERE ((chart.link = 'AP' AND in_account_class = 1) OR (chart.link = 'AR' AND in_account_class = 2)) AND a.invoice_class = in_account_class AND c.entity_class = in_account_class AND c.id = in_entity_credit_id --### short term: ignore fractional cent differences AND a.curr = in_curr AND (a.transdate >= in_datefrom OR in_datefrom IS NULL) AND (a.transdate <= in_dateto OR in_dateto IS NULL) AND (a.amount >= in_amountfrom OR in_amountfrom IS NULL) AND (a.amount <= in_amountto OR in_amountto IS NULL) AND (a.department_id = in_department_id OR in_department_id IS NULL) AND due <> 0 AND a.approved = true GROUP BY a.invnumber, a.transdate, a.amount, amount_fx, discount, discount_fx, ac.due, a.id, c.discount_terms, ex.buy, ex.sell, a.curr, a.invoice LOOP RETURN NEXT payment_inv; END LOOP; END;
DECLARE out_entity payment_vc_info; BEGIN FOR out_entity IN SELECT DISTINCT entity_credit_id, legal_name, e.entity_class, discount, o.meta_number FROM overpayments o JOIN entity e ON (e.id=o.entity_id) WHERE available <> 0 AND in_account_class = payment_class LOOP RETURN NEXT out_entity; END LOOP; END;
Returns a list of available overpayments
DECLARE out_overpayment overpayments%ROWTYPE; BEGIN FOR out_overpayment IN SELECT DISTINCT * FROM overpayments WHERE payment_class = in_account_class AND entity_credit_id = in_entity_credit_id AND available <> 0 AND (in_chart_id IS NULL OR chart_id = in_chart_id ) ORDER BY payment_date LOOP RETURN NEXT out_overpayment; END LOOP; END;
This function returns vendor or customer info
DECLARE out_row payment_location_result; BEGIN FOR out_row IN SELECT l.id, l.line_one, l.line_two, l.line_three, l.city, l.state, l.mail_code, c.name, lc.class FROM location l JOIN entity_to_location ctl ON (ctl.location_id = l.id) JOIN entity cp ON (ctl.entity_id = cp.id) JOIN location_class lc ON (ctl.location_class = lc.id) JOIN country c ON (c.id = l.country_id) JOIN entity_credit_account ec ON (ec.entity_id = cp.entity_id) WHERE ec.id = in_entity_credit_id AND lc.id = in_location_class_id ORDER BY lc.id, l.id, c.name LOOP RETURN NEXT out_row; END LOOP; END;
Posts a payment. in_op_* arrays are cross-indexed with eachother. Other arrays are cross-indexed with eachother. This API will probably change in 1.4 as we start looking at using more custom complex types and arrays of those (requires Pg 8.4 or higher).
DECLARE var_payment_id int; DECLARE var_gl_id int; DECLARE var_entry record; DECLARE var_entry_id int[]; DECLARE out_count int; DECLARE coa_id record; DECLARE var_employee int; DECLARE var_account_id int; DECLARE default_currency char(3); DECLARE current_exchangerate numeric; DECLARE old_exchangerate numeric; DECLARE fx_gain_loss_amount numeric; BEGIN IF array_upper(in_amount, 1) <> array_upper(in_cash_account_id, 1) THEN RAISE EXCEPTION 'Wrong number of accounts'; END IF; SELECT * INTO default_currency FROM defaults_get_defaultcurrency(); SELECT * INTO current_exchangerate FROM currency_get_exchangerate(in_curr, in_datepaid, in_account_class); SELECT INTO var_employee p.id FROM users u JOIN person p ON (u.entity_id=p.entity_id) WHERE username = SESSION_USER LIMIT 1; -- -- WE HAVE TO INSERT THE PAYMENT, USING THE GL INFORMATION -- THE ID IS GENERATED BY payment_id_seq -- INSERT INTO payment (reference, payment_class, payment_date, employee_id, currency, notes, department_id, entity_credit_id) VALUES ((CASE WHEN in_account_class = 1 THEN setting_increment('rcptnumber') -- I FOUND THIS ON sql/modules/Settings.sql ELSE -- and it is very usefull setting_increment('paynumber') END), in_account_class, in_datepaid, var_employee, in_curr, in_notes, in_department_id, in_entity_credit_id); SELECT currval('payment_id_seq') INTO var_payment_id; -- WE'LL NEED THIS VALUE TO USE payment_link table -- WE'LL NEED THIS VALUE TO JOIN WITH PAYMENT -- NOW COMES THE HEAVY PART, STORING ALL THE POSSIBLE TRANSACTIONS... -- -- FIRST WE SHOULD INSERT THE CASH ACCOUNTS -- -- WE SHOULD HAVE THE DATA STORED AS (ACCNO, AMOUNT), SO IF (array_upper(in_cash_account_id, 1) > 0) THEN FOR out_count IN array_lower(in_cash_account_id, 1) .. array_upper(in_cash_account_id, 1) LOOP INSERT INTO acc_trans (chart_id, amount, trans_id, transdate, approved, source, memo) VALUES (in_cash_account_id[out_count], CASE WHEN in_account_class = 1 THEN in_amount[out_count]*current_exchangerate ELSE (in_amount[out_count]*current_exchangerate)* - 1 END, in_transaction_id[out_count], in_datepaid, coalesce(in_approved, true), in_source[out_count], in_memo[out_count]); INSERT INTO payment_links VALUES (var_payment_id, currval('acc_trans_entry_id_seq'), 1); IF (in_ovp_payment_id IS NOT NULL AND in_ovp_payment_id[out_count] IS NOT NULL) THEN INSERT INTO payment_links VALUES (in_ovp_payment_id[out_count], currval('acc_trans_entry_id_seq'), 0); END IF; END LOOP; -- NOW LETS HANDLE THE AR/AP ACCOUNTS -- WE RECEIVED THE TRANSACTIONS_ID AND WE CAN OBTAIN THE ACCOUNT FROM THERE FOR out_count IN array_lower(in_transaction_id, 1) .. array_upper(in_transaction_id, 1) LOOP SELECT INTO var_account_id chart_id FROM acc_trans as ac JOIN chart as c ON (c.id = ac.chart_id) WHERE trans_id = in_transaction_id[out_count] AND ( c.link = 'AP' OR c.link = 'AR' ); -- We need to know the exchangerate of this transaction -- ### BUG: we don't have a guarantee that the transaction is -- the same currency as in_curr, so, we can't use -- current_exchangerate as the basis for fx gain/loss -- calculations IF (in_curr = default_currency) THEN old_exchangerate := 1; ELSIF (in_account_class = 2) THEN SELECT buy INTO old_exchangerate FROM exchangerate e JOIN ar a ON (a.transdate = e.transdate) AND (a.curr = e.curr) WHERE a.id = in_transaction_id[out_count]; ELSE SELECT sell INTO old_exchangerate FROM exchangerate e JOIN ap a ON (a.transdate = e.transdate) AND (a.curr = e.curr) WHERE a.id = in_transaction_id[out_count]; END IF; -- Now we post the AP/AR transaction INSERT INTO acc_trans (chart_id, amount, trans_id, transdate, approved, source, memo) VALUES (var_account_id, CASE WHEN in_account_class = 1 THEN (in_amount[out_count]*old_exchangerate) * -1 ELSE in_amount[out_count]*old_exchangerate END, in_transaction_id[out_count], in_datepaid, coalesce(in_approved, true), in_source[out_count], in_memo[out_count]); -- Lets set the gain/loss, if fx_gain_loss_amount equals zero then we dont need to post -- any transaction fx_gain_loss_amount := in_amount[out_count]*current_exchangerate - in_amount[out_count]*old_exchangerate; IF (in_account_class = 1) THEN -- in case of vendor invoices, the invoice amounts have been negated, do the same with the diff fx_gain_loss_amount := fx_gain_loss_amount * -1; END IF; IF (fx_gain_loss_amount < 0) THEN INSERT INTO acc_trans (chart_id, amount, trans_id, transdate, approved, source) VALUES ((select value::int from defaults WHERE setting_key = 'fxgain_accno_id'), fx_gain_loss_amount, in_transaction_id[out_count], in_datepaid, coalesce(in_approved, true), in_source[out_count]); ELSIF (fx_gain_loss_amount > 0) THEN INSERT INTO acc_trans (chart_id, amount, trans_id, transdate, approved, source) VALUES ((select value::int from defaults WHERE setting_key = 'fxloss_accno_id'), fx_gain_loss_amount, in_transaction_id[out_count], in_datepaid, coalesce(in_approved, true), in_source[out_count]); END IF; -- Now we set the links INSERT INTO payment_links VALUES (var_payment_id, currval('acc_trans_entry_id_seq'), 1); END LOOP; END IF; -- END IF -- -- WE NEED TO HANDLE THE OVERPAYMENTS NOW -- -- -- FIRST WE HAVE TO MAKE THE GL TO HOLD THE OVERPAYMENT TRANSACTIONS -- THE ID IS GENERATED BY gl_id_seq -- IF (array_upper(in_op_cash_account_id, 1) > 0) THEN INSERT INTO gl (reference, description, transdate, person_id, notes, approved, department_id) VALUES (setting_increment('glnumber'), in_gl_description, in_datepaid, var_employee, in_notes, in_approved, in_department_id); SELECT currval('id') INTO var_gl_id; -- -- WE NEED TO SET THE GL_ID FIELD ON PAYMENT'S TABLE -- UPDATE payment SET gl_id = var_gl_id WHERE id = var_payment_id; -- NOW COMES THE HEAVY PART, STORING ALL THE POSSIBLE TRANSACTIONS... -- -- FIRST WE SHOULD INSERT THE OVERPAYMENT CASH ACCOUNTS -- FOR out_count IN array_lower(in_op_cash_account_id, 1) .. array_upper(in_op_cash_account_id, 1) LOOP INSERT INTO acc_trans (chart_id, amount, trans_id, transdate, approved, source, memo) VALUES (in_op_cash_account_id[out_count], CASE WHEN in_account_class = 1 THEN in_op_amount[out_count] ELSE in_op_amount[out_count] * - 1 END, var_gl_id, in_datepaid, coalesce(in_approved, true), in_op_source[out_count], in_op_memo[out_count]); INSERT INTO payment_links VALUES (var_payment_id, currval('acc_trans_entry_id_seq'), 2); END LOOP; -- NOW LETS HANDLE THE OVERPAYMENT ACCOUNTS FOR out_count IN array_lower(in_op_account_id, 1) .. array_upper(in_op_account_id, 1) LOOP INSERT INTO acc_trans (chart_id, amount, trans_id, transdate, approved, source, memo) VALUES (in_op_account_id[out_count], CASE WHEN in_account_class = 1 THEN in_op_amount[out_count] * -1 ELSE in_op_amount[out_count] END, var_gl_id, in_datepaid, coalesce(in_approved, true), in_op_source[out_count], in_op_memo[out_count]); INSERT INTO payment_links VALUES (var_payment_id, currval('acc_trans_entry_id_seq'), 2); END LOOP; END IF; return var_payment_id; END;
Returns all information on a payment type by the id. This should be renamed to account for its behavior in future versions.
DECLARE out_row payment_type%ROWTYPE; BEGIN FOR out_row IN SELECT * FROM payment_type where id=in_payment_type_id LOOP RETURN NEXT out_row; END LOOP; END;
DECLARE out_row payment_type%ROWTYPE; BEGIN FOR out_row IN SELECT * FROM payment_type LOOP RETURN NEXT out_row; END LOOP; END;
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;
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;
Returns dates for year to date, and last year.
SELECT * FROM periods ORDER BY id
Deletes a contact record specified for the person. Returns true if a record was found and deleted, false if not.
BEGIN DELETE FROM person_to_contact WHERE person_id = in_person_id and contact_class_id = in_contact_class_id and contact= in_contact; RETURN FOUND; END;
Deletes a location mapping to a person. Returns true if found, false if no data deleted.
BEGIN DELETE FROM person_to_location WHERE person_id = in_person_id AND location_id = in_location_id AND location_class = in_location_class; RETURN FOUND; END;
Returns the entity_id of the current, logged in user.
SELECT entity_id from users where username = SESSION_USER;
Lists bank accounts for a person
DECLARE out_row entity_bank_account%ROWTYPE; BEGIN FOR out_row IN SELECT * from entity_bank_account where entity_id = in_entity_id LOOP RETURN NEXT out_row; END LOOP; END;
Returns a list of contacts attached to the function.
DECLARE out_row RECORD; BEGIN FOR out_row IN SELECT cc.class, cc.id, c.description, c.contact FROM person_to_contact c JOIN contact_class cc ON (c.contact_class_id = cc.id) JOIN person p ON (c.person_id = p.id) WHERE p.entity_id = in_entity_id LOOP RETURN NEXT out_row; END LOOP; END;
Returns a list of languages ordered by code
SELECT * FROM language ORDER BY code ASC
Returns a list of notes attached to a person.
DECLARE out_row record; BEGIN FOR out_row IN SELECT * FROM entity_note WHERE ref_key = in_entity_id ORDER BY created LOOP RETURN NEXT out_row; END LOOP; END;
Returns a list of salutations ordered by id.
SELECT * FROM salutation ORDER BY id ASC
Saves the person with the information specified. Returns the entity_id of the record saved.
DECLARE e_id int; e entity; loc location; l_id int; p_id int; BEGIN select * into e from entity where id = in_entity_id and entity_class = 3; e_id := in_entity_id; IF FOUND THEN UPDATE entity SET name = in_first_name || ' ' || in_last_name, country_id = in_country_id WHERE id = in_entity_id; ELSE INSERT INTO entity (name, entity_class, country_id) values (in_first_name || ' ' || in_last_name, 3, in_country_id); e_id := currval('entity_id_seq'); END IF; UPDATE person SET salutation_id = in_salutation_id, first_name = in_first_name, last_name = in_last_name, middle_name = in_middle_name WHERE entity_id = in_entity_id; IF FOUND THEN RETURN in_entity_id; ELSE -- Do an insert INSERT INTO person (salutation_id, first_name, last_name, entity_id) VALUES (in_salutation_id, in_first_name, in_last_name, e_id); RETURN e_id; END IF; END;
Saves saves contact info. Returns 1 if a row was inserted, 0 if it was updated.
DECLARE out_id int; v_orig person_to_contact; BEGIN SELECT cc.* into v_orig FROM person_to_contact cc, person p WHERE p.entity_id = in_entity_id and cc.contact_class_id = in_old_contact_class AND cc.contact = in_old_contact AND cc.person_id = p.id; IF NOT FOUND THEN -- create INSERT INTO person_to_contact(person_id, contact_class_id, contact, description) VALUES ( (SELECT id FROM person WHERE entity_id = in_entity_id), in_contact_class, in_contact_new, in_description ); return 1; ELSE -- edit. UPDATE person_to_contact SET contact = in_contact_new, description = in_description WHERE contact = in_old_contact AND person_id = v_orig.person_id AND contact_class_id = in_old_contact_class; return 0; END IF; END;
Saves a location mapped to the person with the specified information. Returns the location id saved.
DECLARE l_row location; l_id INT; t_person_id int; BEGIN SELECT id INTO t_person_id FROM person WHERE entity_id = in_entity_id; UPDATE person_to_location SET location_class = in_location_class WHERE person_id = t_person_id AND location_class = in_old_location_class AND location_id = in_location_id; IF NOT FOUND THEN -- Create a new one. l_id := location_save( in_location_id, in_line_one, in_line_two, in_line_three, in_city, in_state, in_mail_code, in_country_code); INSERT INTO person_to_location (person_id, location_id, location_class) VALUES (t_person_id, l_id, in_location_class); ELSE l_id := location_save( in_location_id, in_line_one, in_line_two, in_line_three, in_city, in_state, in_mail_code, in_country_code); -- Update the old one. END IF; return l_id; END;
plainto_tsquery_byid
plainto_tsquery
tsa_plainto_tsquery_name
Returns an alphabetically ordered pricegroup list.
SELECT * FROM pricegroup ORDER BY pricegroup;
SELECT * FROM pricegroup;
delete from partscustomer where entry_id = $1 and credit_id = $2; delete from partsvendor where entry_id = $1 and credit_id = $2; select true;
DECLARE retval eca__pricematrix; t_insert bool; t_entity_class int; BEGIN t_insert := false; SELECT entity_class INTO t_entity_class FROM entity_credit_account WHERE id = in_credit_id; IF t_entity_class = 1 THEN -- VENDOR UPDATE partsvendor SET lastcost = in_price, leadtime = in_lead_time, partnumber = in_partnumber, curr = in_curr WHERE credit_id = in_credit_id AND entry_id = in_entry_id; IF NOT FOUND THEN INSERT INTO partsvendor (parts_id, credit_id, lastcost, leadtime, partnumber, curr) VALUES (in_parts_id, in_credit_id, in_price, in_leadtime::int2, in_partnumber, in_curr); END IF; SELECT pv.parts_id, p.partnumber, p.description, pv.credit_id, NULL, NULL, pv.lastcost, pv.leadtime::int, pv.partnumber, NULL, NULL, pv.curr, pv.entry_id INTO retval FROM partsvendor pv JOIN parts p ON p.id = pv.parts_id WHERE parts_id = in_parts_id and credit_id = in_credit_id; RETURN retval; ELSIF t_entity_class = 2 THEN -- CUSTOMER UPDATE partscustomer SET pricebreak = in_pricebreak, sellprice = in_price, validfrom = in_validfrom, validto = in_validto, curr = in_curr WHERE entry_id = in_entry_id and credit_id = in_credit_id; IF NOT FOUND THEN INSERT INTO partscustomer (parts_id, credit_id, sellprice, validfrom, validto, curr) VALUES (in_parts_id, in_credit_id, in_price, in_validfrom, in_validto, in_curr); t_insert := true; END IF; SELECT pc.parts_id, p.partnumber, p.description, pc.credit_id, pc.pricebreak, pc.sellprice, NULL, NULL, NULL, pc.validfrom, pc.validto, pc.curr, pc.entry_id INTO retval FROM partscustomer pc JOIN parts p on pc.parts_id = p.id WHERE entry_id = CASE WHEN t_insert THEN currval('partscustomer_entry_id_seq') ELSE in_entry_id END; RETURN retval; ELSE RAISE EXCEPTION 'No valid entity credit account found'; END IF; END;
This function returns all projects that were open as on the date provided as the argument.
DECLARE out_project project%ROWTYPE; BEGIN FOR out_project IN SELECT * from project WHERE startdate <= in_date AND enddate >= in_date AND completed = 0 LOOP return next out_project; END LOOP; END;
tsa_prsd_end
tsa_prsd_getlexeme
tsa_prsd_headline
tsa_prsd_lextype
tsa_prsd_start
tsquerytree
ts_rank_wtt
ts_rank_wttf
ts_rank_tt
ts_rank_ttf
ts_rankcd_wtt
ts_rankcd_wttf
ts_rankcd_tt
ts_rankcd_ttf
returns set of accounts set up for reconciliation. Currently we pull the account number and description from the account table.
SELECT coa.accno || ' ' || coa.description as name, coa.accno, coa.id as id FROM account coa JOIN cr_coa_to_account cta ON cta.chart_id = coa.id ORDER BY coa.accno;
This function is used for automatically matching entries from an external source like a bank-produced csv file. This function is very sensitive to ordering of inputs. NULL or empty in_scn values MUST be submitted after meaningful scns. It is also highly recommended that within each category, one submits in order of amount. We should therefore wrap it in another function which can operate on a set, perhaps in 1.4....
DECLARE in_account int; la RECORD; t_errorcode INT; our_value NUMERIC; lid INT; in_count int; t_scn TEXT; t_uid int; t_prefix text; t_amount numeric; BEGIN SELECT CASE WHEN a.category in ('A', 'E') THEN in_amount * -1 ELSE in_amount END into t_amount FROM cr_report r JOIN account a ON r.chart_id = a.id WHERE r.id = in_report_id; SELECT value into t_prefix FROM defaults WHERE setting_key = 'check_prefix'; t_uid := person__get_my_entity_id(); IF in_scn = '' THEN t_scn := NULL; ELSE t_scn := t_prefix || in_scn; END IF; IF t_scn IS NOT NULL THEN -- could this be changed to update, if not found insert? SELECT count(*) INTO in_count FROM cr_report_line WHERE scn ilike t_scn AND report_id = in_report_id AND their_balance = 0; IF in_count = 0 THEN INSERT INTO cr_report_line (report_id, scn, their_balance, our_balance, clear_time, "user", trans_type) VALUES (in_report_id, t_scn, t_amount, 0, in_date, t_uid, in_type); ELSIF in_count = 1 THEN UPDATE cr_report_line SET their_balance = t_amount, clear_time = in_date, cleared = true WHERE t_scn = scn AND report_id = in_report_id AND their_balance = 0; ELSE SELECT count(*) INTO in_count FROM cr_report_line WHERE t_scn ilike scn AND report_id = in_report_id AND our_value = t_amount and their_balance = 0; IF in_count = 0 THEN -- no match among many of values SELECT id INTO lid FROM cr_report_line WHERE t_scn ilike scn AND report_id = in_report_id ORDER BY our_balance ASC limit 1; UPDATE cr_report_line SET their_balance = t_amount, clear_time = in_date, trans_type = in_type, cleared = true WHERE id = lid; ELSIF in_count = 1 THEN -- EXECT MATCH UPDATE cr_report_line SET their_balance = t_amount, trans_type = in_type, clear_time = in_date, cleared = true WHERE t_scn = scn AND report_id = in_report_id AND our_value = t_amount AND their_balance = 0; ELSE -- More than one match SELECT id INTO lid FROM cr_report_line WHERE t_scn ilike scn AND report_id = in_report_id AND our_value = t_amount ORDER BY id ASC limit 1; UPDATE cr_report_line SET their_balance = t_amount, trans_type = in_type, cleared = true, clear_time = in_date WHERE id = lid; END IF; END IF; ELSE -- scn IS NULL, check on amount instead SELECT count(*) INTO in_count FROM cr_report_line WHERE report_id = in_report_id AND our_balance = t_amount AND their_balance = 0 and post_date = in_date and scn NOT LIKE t_prefix || '%'; IF in_count = 0 THEN -- no match INSERT INTO cr_report_line (report_id, scn, their_balance, our_balance, clear_time, "user", trans_type) VALUES (in_report_id, t_scn, t_amount, 0, in_date, t_uid, in_type); ELSIF in_count = 1 THEN -- perfect match UPDATE cr_report_line SET their_balance = t_amount, trans_type = in_type, clear_time = in_date, cleared = true WHERE report_id = in_report_id AND our_balance = t_amount AND their_balance = 0 and in_scn NOT LIKE t_prefix || '%'; ELSE -- more than one match SELECT min(id) INTO lid FROM cr_report_line WHERE report_id = in_report_id AND our_balance = t_amount AND their_balance = 0 and post_date = in_date AND scn NOT LIKE t_prefix || '%' LIMIT 1; UPDATE cr_report_line SET their_balance = t_amount, trans_type = in_type, clear_time = in_date, cleared = true WHERE id = lid; END IF; END IF; return 1; END;
This function allows a user to delete his or her own unsubmitted, unapproved reconciliation reports only. This is designed to allow a user to back out of the reconciliation process without cluttering up the search results for others.
BEGIN DELETE FROM cr_report_line WHERE report_id = in_report_id AND report_id IN (SELECT id FROM cr_report WHERE entered_username = SESSION_USER AND submitted IS NOT TRUE and approved IS NOT TRUE); DELETE FROM cr_report WHERE id = in_report_id AND entered_username = SESSION_USER AND submitted IS NOT TRUE AND approved IS NOT TRUE; RETURN FOUND; END;
Deletes the report if the report exists and is unapproved. Note this does not actually delete anything from the database. Deleted reports are kept around in case they need to be investigated. It only marks them as deleted so that they can never be approved.
DECLARE BEGIN PERFORM id FROM cr_report WHERE id = in_report_id; IF NOT FOUND THEN RAISE NOTICE 'reconciliation__delete_report(): Cannot find specified report.'; return FOUND; END IF; -- We found the entry. Update it. PERFORM id FROM cr_report WHERE id = in_report_id AND approved = TRUE; IF FOUND THEN --changing the verbose message to a notice and adding a --program-helpful exception --CT RAISE NOTICE 'reconcilation__delete_report(): report % is approved; cannot delete.', in_report_id; RAISE EXCEPTION 'Cannot delete approved'; END IF; PERFORM id FROM cr_report WHERE id = in_report_id AND submitted = TRUE AND entered_by = person__get_my_entity_id(); -- IF FOUND THEN -- Creators cannot delete their own reports if they've been submitted. -AS -- Why not? If it hasn't been approved..... -- Also concerned about single-user setups here so commenting out -- this block --CT -- RAISE EXCEPTION 'reconciliation__delete_report(): creators cannot delete their own report after submission. %', in_report_id; -- END IF; UPDATE cr_report SET deleted = TRUE, deleted_by = person__get_my_entity_id() WHERE id = in_report_id; return TRUE; END;
This function deletes any specified unapproved transaction.
BEGIN DELETE FROM cr_report_line WHERE report_id = in_report_id AND report_id IN (SELECT id FROM cr_report WHERE approved IS NOT TRUE); DELETE FROM cr_report WHERE id = in_report_id AND approved IS NOT TRUE; RETURN FOUND; END;
Gets the cleared balance of the account specified by chart_id. This is specified in normal format (i.e. positive numbers for debits for asset and espense accounts, and positive numbers for credits in other accounts Note that currently contra accounts will show negative balances.
select CASE WHEN c.category in('A', 'E') THEN sum(ac.amount) * -1 ELSE sum(ac.amount) END FROM account c JOIN acc_trans ac ON (ac.chart_id = c.id) JOIN (select id from ar where approved union select id from ap where approved union select id from gl where approved) g on (g.id = ac.trans_id) WHERE c.id = $1 AND ac.cleared is true and ac.approved is true GROUP BY c.id, c.category;
Gets the current balance of all approved transactions against a specific account. For asset and expense accounts this is the debit balance, for others this is the credit balance.
DECLARE outval NUMERIC; BEGIN SELECT CASE WHEN (select category FROM account WHERE id = in_account_id) IN ('A', 'E') THEN sum(a.amount) * -1 ELSE sum(a.amount) END FROM acc_trans a JOIN ( SELECT id FROM ar WHERE approved is true UNION SELECT id FROM ap WHERE approved is true UNION SELECT id FROM gl WHERE approved is true ) gl ON a.trans_id = gl.id WHERE a.approved IS TRUE AND a.chart_id = in_account_id AND a.transdate <= in_date; RETURN outval; END;
Retrieves all header info from the reconciliation report.
DECLARE row cr_report; BEGIN SELECT * INTO row FROM cr_report where id = in_report_id AND scn = -1; IF NOT FOUND THEN -- I think this is a fairly major error condition RAISE EXCEPTION 'Bad report id.'; ELSE return next row; END IF; END;
Inserts creates a new report and returns the id.
INSERT INTO cr_report(chart_id, their_total, end_date) values ($1, $2, $3); SELECT currval('cr_report_id_seq')::int;
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;
Ensures that the list of pending transactions in the report is up to date.
DECLARE gl_row RECORD; BEGIN INSERT INTO cr_report_line (report_id, scn, their_balance, our_balance, "user", voucher_id, ledger_id, post_date) SELECT in_report_id, COALESCE(ac.source, gl.ref), 0, sum(amount / CASE WHEN gl.table = 'gl' THEN 1 WHEN gl.table = 'ap' THEN ex.sell WHEN gl.table = 'ar' THEN ex.buy END) AS amount, (select entity_id from users where username = CURRENT_USER), ac.voucher_id, min(ac.entry_id), ac.transdate FROM acc_trans ac JOIN transactions t on (ac.trans_id = t.id) JOIN (select id, entity_credit_account::text as ref, curr, transdate, 'ar' as table FROM ar where approved UNION select id, entity_credit_account::text, curr, transdate, 'ap' as table FROM ap WHERE approved UNION select id, reference, '', transdate, 'gl' as table FROM gl WHERE approved) gl ON (gl.table = t.table_name AND gl.id = t.id) LEFT JOIN cr_report_line rl ON (rl.report_id = in_report_id AND ((rl.ledger_id = ac.entry_id AND ac.voucher_id IS NULL) OR (rl.voucher_id = ac.voucher_id))) LEFT JOIN exchangerate ex ON gl.transdate = ex.transdate WHERE ac.cleared IS FALSE AND ac.approved IS TRUE AND ac.chart_id = in_chart_id AND ac.transdate <= in_end_date AND ((in_recon_fx is not true and ac.fx_transaction is not true) OR (in_recon_fx is true AND (gl.table <> 'gl' OR ac.fx_transaction IS TRUE))) GROUP BY gl.ref, ac.source, ac.transdate, ac.memo, ac.voucher_id, gl.table HAVING count(rl.id) = 0; UPDATE cr_report set updated = now(), their_total = coalesce(in_their_total, their_total) where id = in_report_id; RETURN in_report_id; END;
Ensures that the list of pending transactions in the report is up to date.
DECLARE gl_row RECORD; t_recon_fx BOOL; BEGIN SELECT recon_fx INTO t_recon_fx FROM cr_report WHERE id = in_report_id; INSERT INTO cr_report_line (report_id, scn, their_balance, our_balance, "user", voucher_id, ledger_id, post_date) SELECT in_report_id, CASE WHEN ac.source IS NULL OR ac.source = '' THEN gl.ref ELSE ac.source END, 0, sum(amount / CASE WHEN t_recon_fx IS NOT TRUE OR gl.table = 'gl' THEN 1 WHEN t_recon_fx and gl.table = 'ap' THEN ex.sell WHEN t_recon_fx and gl.table = 'ar' THEN ex.buy END) AS amount, (select entity_id from users where username = CURRENT_USER), ac.voucher_id, min(ac.entry_id), ac.transdate FROM acc_trans ac JOIN transactions t on (ac.trans_id = t.id) JOIN (select id, entity_credit_account::text as ref, curr, transdate, 'ar' as table FROM ar where approved UNION select id, entity_credit_account::text, curr, transdate, 'ap' as table FROM ap WHERE approved UNION select id, reference, '', transdate, 'gl' as table FROM gl WHERE approved) gl ON (gl.table = t.table_name AND gl.id = t.id) LEFT JOIN cr_report_line rl ON (rl.report_id = in_report_id AND ((rl.ledger_id = ac.entry_id AND ac.voucher_id IS NULL) OR (rl.voucher_id = ac.voucher_id))) LEFT JOIN exchangerate ex ON gl.transdate = ex.transdate WHERE ac.cleared IS FALSE AND ac.approved IS TRUE AND ac.chart_id = in_chart_id AND ac.transdate <= in_end_date AND ((t_recon_fx is not true and ac.fx_transaction is not true) OR (t_recon_fx is true AND (gl.table <> 'gl' OR ac.fx_transaction IS TRUE))) GROUP BY gl.ref, ac.source, ac.transdate, ac.memo, ac.voucher_id, gl.table, case when gl.table = 'gl' then gl.id else 1 end HAVING count(rl.id) = 0; UPDATE cr_report set updated = now(), their_total = coalesce(in_their_total, their_total) where id = in_report_id; RETURN in_report_id; END;
Marks the report approved and marks all cleared transactions in it cleared.
-- Does some basic checks before allowing the approval to go through; -- moves the approval to "cr_report_line", I guess, or some other "final" table. -- -- Pending may just be a single flag in the database to mark that it is -- not finalized. Will need to discuss with Chris. DECLARE current_row RECORD; completed cr_report_line; total_errors INT; in_user TEXT; ac_entries int[]; BEGIN in_user := current_user; -- so far, so good. Different user, and no errors remain. Therefore, -- we can move it to completed reports. -- -- User may not be necessary - I would think it better to use the -- in_user, to note who approved the report, than the user who -- filed it. This may require clunkier syntax.. -- ac_entries := '{}'; update cr_report set approved = 't', approved_by = person__get_my_entity_id(), approved_username = SESSION_USER where id = in_report_id; FOR current_row IN SELECT compound_array(entries) AS entries FROM ( select as_array(ac.entry_id) as entries FROM acc_trans ac JOIN transactions t on (ac.trans_id = t.id) JOIN (select id, entity_credit_account::text as ref, 'ar' as table FROM ar UNION select id, entity_credit_account::text, 'ap' as table FROM ap UNION select id, reference, 'gl' as table FROM gl) gl ON (gl.table = t.table_name AND gl.id = t.id) LEFT JOIN cr_report_line rl ON (rl.report_id = in_report_id AND ((rl.ledger_id = ac.entry_id AND ac.voucher_id IS NULL) OR (rl.voucher_id = ac.voucher_id)) and rl.cleared is true) WHERE ac.cleared IS FALSE AND ac.chart_id = (select chart_id from cr_report where id = in_report_id) GROUP BY gl.ref, ac.source, ac.transdate, ac.memo, ac.voucher_id, gl.table HAVING count(rl.report_id) > 0) a LOOP ac_entries := ac_entries || current_row.entries; END LOOP; UPDATE acc_trans SET cleared = TRUE where entry_id = any(ac_entries); return 1; END;
Returns the details of the report.
DECLARE row cr_report_line; BEGIN FOR row IN select * from cr_report_line where report_id = in_report_id order by scn, post_date LOOP RETURN NEXT row; END LOOP; END;
Pulls the payee information for the reconciliation report.
DECLARE row recon_payee; BEGIN FOR row IN select * from recon_payee where report_id = in_report_id order by scn, post_date LOOP RETURN NEXT row; END LOOP; END;
DECLARE row cr_report; BEGIN select * into row from cr_report where id = in_report_id; RETURN row; END;
Sets which lines of the report are cleared.
BEGIN UPDATE cr_report_line SET cleared = false WHERE report_id = in_report_id; UPDATE cr_report_line SET cleared = true WHERE report_id = in_report_id AND id = ANY(in_line_ids); RETURN found; END;
Searches for reconciliation reports. NULLs match all values. in_date_to and in_date_from give a range of reports. All other inputs are exact matches.
DECLARE report cr_report; BEGIN FOR report IN SELECT r.* FROM cr_report r JOIN account c ON (r.chart_id = c.id) WHERE (in_date_from IS NULL OR in_date_from <= end_date) and (in_date_to IS NULL OR in_date_to >= end_date) AND (in_balance_from IS NULL or in_balance_from <= their_total ) AND (in_balance_to IS NULL OR in_balance_to >= their_total) AND (in_chart_id IS NULL OR in_chart_id = chart_id) AND (in_submitted IS NULL or in_submitted = submitted) AND (in_approved IS NULL OR in_approved = approved) AND (r.deleted IS FALSE) ORDER BY c.accno, end_date, their_total LOOP RETURN NEXT report; END LOOP; END;
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;
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;
WITH ac (chart_id, amount) AS ( SELECT chart_id, CASE WHEN acc_trans.approved and gl.approved THEN amount ELSE 0 END FROM acc_trans JOIN (select id, approved from ar union all select id, approved from ap union all select id, approved from gl) gl ON gl.id = acc_trans.trans_id ), l(account_id, link) AS ( SELECT account_id, array_to_string(array_agg(description), ':') FROM account_link GROUP BY account_id ) SELECT a.id, a.is_heading, a.accno, a.description, a.gifi_accno, CASE WHEN sum(ac.amount) < 0 THEN sum(amount) * -1 ELSE null::numeric END, CASE WHEN sum(ac.amount) > 0 THEN sum(amount) ELSE null::numeric END, count(ac.*), l.link FROM (SELECT id,false as is_heading, accno, description, gifi_accno FROM account UNION SELECT id, true, accno, description, null::text FROM account_heading) a LEFT JOIN ac ON ac.chart_id = a.id AND not a.is_heading LEFT JOIN l ON l.account_id = a.id AND NOT a.is_heading GROUP BY a.id, a.is_heading, a.accno, a.description, a.gifi_accno, l.link ORDER BY a.accno;
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;
DECLARE retval gl_report_item; t_balance numeric; t_chart_id int; BEGIN IF in_from_date IS NULL THEN t_balance := 0; ELSIF in_accno IS NOT NULL THEN SELECT id INTO t_chart_id FROM account WHERE accno = in_accno; t_balance := account__obtain_balance(in_from_date , (select id from account where accno = in_accno)); ELSE t_balance := null; END IF; FOR retval IN WITH RECURSIVE bu_tree (id, path) AS ( SELECT id, id::text AS path FROM business_unit WHERE parent_id is null UNION SELECT bu.id, bu_tree.path || ',' || bu.id FROM business_unit bu JOIN bu_tree ON bu_tree.id = bu.parent_id ) SELECT g.id, g.type, g.invoice, g.reference, g.description, ac.transdate, ac.source, ac.amount, c.accno, c.gifi_accno, g.till, ac.cleared, ac.memo, c.description AS accname, ac.chart_id, ac.entry_id, sum(ac.amount) over (rows unbounded preceding) + t_balance as running_balance, compound_array(ARRAY[ARRAY[bac.class_id, bac.bu_id]]) FROM (select id, 'gl' as type, false as invoice, reference, description, approved, null::text as till FROM gl UNION SELECT ar.id, 'ar', invoice, invnumber, e.name, approved, till FROM ar JOIN entity_credit_account eca ON ar.entity_credit_account = eca.id JOIN entity e ON e.id = eca.entity_id UNION SELECT ap.id, 'ap', invoice, invnumber, e.name, approved, null as till FROM ap JOIN entity_credit_account eca ON ap.entity_credit_account = eca.id JOIN entity e ON e.id = eca.entity_id) g JOIN acc_trans ac ON ac.trans_id = g.id JOIN account c ON ac.chart_id = c.id LEFT JOIN business_unit_ac bac ON ac.entry_id = bac.entry_id LEFT JOIN bu_tree ON bac.bu_id = bu_tree.id WHERE (g.reference ilike in_reference || '%' or in_reference is null) AND (c.accno = in_accno OR in_accno IS NULL) AND (ac.source ilike '%' || in_source || '%' OR in_source is null) AND (ac.memo ilike '%' || in_memo || '%' OR in_memo is null) AND (in_description IS NULL OR g.description @@ plainto_tsquery(get_default_lang()::regconfig, in_description)) AND (transdate BETWEEN in_from_date AND in_to_date OR (transdate >= in_from_date AND in_to_date IS NULL) OR (transdate <= in_to_date AND in_from_date IS NULL) OR (in_to_date IS NULL AND in_from_date IS NULL)) AND (in_approved is false OR (g.approved AND ac.approved)) AND (in_from_amount IS NULL OR ac.amount >= in_from_amount) AND (in_to_amount IS NULL OR ac.amount <= in_to_amount) AND (in_category = c.category OR in_category IS NULL) GROUP BY g.id, g.type, g.invoice, g.reference, g.description, ac.transdate, ac.source, ac.amount, c.accno, c.gifi_accno, g.till, ac.cleared, ac.memo, c.description, ac.chart_id, ac.entry_id, ac.trans_id HAVING in_business_units is null or in_business_units <@ compound_array(string_to_array(bu_tree.path, ',')::int[]) ORDER BY ac.transdate, ac.trans_id, c.accno LOOP RETURN NEXT retval; END LOOP; END;
DECLARE item report_aging_item; BEGIN FOR item IN WITH RECURSIVE bu_tree (id, path) AS ( SELECT id, id::text AS path FROM business_unit WHERE id = any(in_business_units) UNION SELECT bu.id, bu_tree.path || ',' || bu.id FROM business_unit bu JOIN bu_tree ON bu_tree.id = bu.parent_id ) SELECT c.entity_id, c.meta_number, e.name, e.name as contact_name, a.invnumber, a.transdate, a.till, a.ordnumber, a.ponumber, a.notes, CASE WHEN a.age/30 = 0 THEN (a.sign * sum(ac.amount)) ELSE 0 END as c0, CASE WHEN a.age/30 = 1 THEN (a.sign * sum(ac.amount)) ELSE 0 END as c30, CASE WHEN a.age/30 = 2 THEN (a.sign * sum(ac.amount)) ELSE 0 END as c60, CASE WHEN a.age/30 > 2 THEN (a.sign * sum(ac.amount)) ELSE 0 END as c90, a.duedate, a.id, a.curr, COALESCE((SELECT sell FROM exchangerate ex WHERE a.curr = ex.curr AND ex.transdate = a.transdate), 1) AS exchangerate, (SELECT compound_array(ARRAY[[p.partnumber, i.description, i.qty::text]]) FROM parts p JOIN invoice i ON (i.parts_id = p.id) WHERE i.trans_id = a.id) AS line_items, (coalesce(in_to_date, now())::date - a.transdate) as age FROM (select id, invnumber, till, ordnumber, amount, duedate, curr, ponumber, notes, entity_credit_account, -1 AS sign, transdate, CASE WHEN in_use_duedate THEN coalesce(in_to_date, now())::date - duedate ELSE coalesce(in_to_date, now())::date - transdate END as age FROM ar WHERE in_entity_class = 2 UNION SELECT id, invnumber, null, ordnumber, amount, duedate, curr, ponumber, notes, entity_credit_account, 1 as sign, transdate, CASE WHEN in_use_duedate THEN coalesce(in_to_date, now())::date - duedate ELSE coalesce(in_to_date, now())::date - transdate END as age FROM ap WHERE in_entity_class = 1) a JOIN acc_trans ac ON ac.trans_id = a.id JOIN account acc ON ac.chart_id = acc.id JOIN account_link acl ON acl.account_id = acc.id AND ((in_entity_class = 1 AND acl.description = 'AP') OR (in_entity_class = 2 AND acl.description = 'AR')) JOIN entity_credit_account c ON a.entity_credit_account = c.id JOIN entity e ON (e.id = c.entity_id) LEFT JOIN business_unit_ac buac ON ac.entry_id = buac.entry_id LEFT JOIN bu_tree ON buac.bu_id = bu_tree.id LEFT JOIN entity_to_location e2l ON e.id = e2l.entity_id AND e2l.location_class = 3 LEFT JOIN location l ON l.id = e2l.location_id LEFT JOIN country ON (country.id = l.country_id) WHERE (e.id = in_entity_id OR in_entity_id IS NULL) AND (in_accno IS NULL or acc.accno = in_accno) GROUP BY c.entity_id, c.meta_number, e.name, l.line_one, l.line_two, l.line_three, l.city, l.state, l.mail_code, country.name, a.invnumber, a.transdate, a.till, a.ordnumber, a.ponumber, a.notes, a.amount, a.sign, a.duedate, a.id, a.curr, a.age HAVING in_business_units is null or in_business_units <@ compound_array(string_to_array(bu_tree.path, ',')::int[]) ORDER BY entity_id, curr, transdate, invnumber LOOP return next item; END LOOP; END;
SELECT entity_id, account_number, name, contact_name, null::text, null::date, null::text, null::text, null::text, null::text, sum(c0), sum(c30), sum(c60), sum(c90), null::date, null::int, curr, null::numeric, null::text[], null::int FROM report__invoice_aging_detail($1, $2, $3, $4, $5, $6) GROUP BY entity_id, account_number, name, contact_name, curr ORDER BY account_number
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;
tsa_reset_tsearch
tsquery_rewrite_query
tsquery_rewrite
aggregate_dummy
tsa_rewrite_accum
tsa_rewrite_finish
Saves tax form information. Returns true or raises exception.
BEGIN INSERT INTO country_tax_form(country_id, form_name) values (in_country_code, in_taxform_name); RETURN true; END;
Returns a session row. If no session exists, it returns null
DECLARE out_row session%ROWTYPE; BEGIN DELETE FROM session WHERE last_used < now() - coalesce((SELECT value FROM defaults WHERE setting_key = 'session_timeout')::interval, '90 minutes'::interval); UPDATE session SET last_used = now() WHERE session_id = in_session_id AND token = in_token AND users_id = (select id from users where username = SESSION_USER); IF FOUND THEN SELECT * INTO out_row FROM session WHERE session_id = in_session_id; ELSE DELETE FROM SESSION WHERE users_id IN (select id from users where username = SESSION_USER); -- the above query also releases all discretionary locks by the -- session PERFORM * FROM defaults WHERE setting_key = 'auto_logout' and value = '1'; IF FOUND THEN RAISE NOTICE 'auto logout'; RETURN NULL; ELSE INSERT INTO session (users_id, token) SELECT id, md5(random()::text) FROM users WHERE username = SESSION_USER; SELECT * INTO out_row FROM SESSION WHERE users_id = (select id from users where username = SESSION_USER); RETURN out_row; END IF; END IF; RETURN out_row; END;
tsa_set_curcfg
tsa_set_curcfg_byname
tsa_set_curdict
tsa_set_curdict_byname
tsa_set_curprs
tsa_set_curprs_byname
set_limit
Returns an array of currencies from the defaults table.
SELECT string_to_array(value, ':') from defaults where setting_key = 'curr';
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;
Returns the value of the setting in the defaults table.
SELECT * FROM defaults WHERE setting_key = $1;
Returns a set of settings for default accounts.
DECLARE account defaults%ROWTYPE; BEGIN FOR account IN SELECT * FROM defaults WHERE setting_key like '%accno_id' ORDER BY setting_key LOOP RETURN NEXT account; END LOOP; END;
This function takes a value for a sequence in the defaults table and increments it. Leading zeroes and spaces are preserved as placeholders. Currently <?lsmb parsing is not supported in this routine though it may be added at a later date.
DECLARE base_value VARCHAR; raw_value VARCHAR; increment INTEGER; inc_length INTEGER; new_value VARCHAR; BEGIN SELECT value INTO raw_value FROM defaults WHERE setting_key = in_key FOR UPDATE; SELECT substring(raw_value from '(' || E'\\' || 'd*)(' || E'\\' || 'D*|<' || E'\\' || '?lsmb [^<>] ' || E'\\' || '?>)*$') INTO base_value; IF base_value like '0%' THEN increment := base_value::integer + 1; SELECT char_length(increment::text) INTO inc_length; SELECT overlay(base_value placing increment::varchar from (select char_length(base_value) - inc_length + 1) for inc_length) INTO new_value; ELSE new_value := base_value::integer + 1; END IF; SELECT regexp_replace(raw_value, base_value, new_value) INTO new_value; UPDATE defaults SET value = new_value WHERE setting_key = in_key; return new_value; END;
sets a value in the defaults thable and returns true if successful.
BEGIN UPDATE defaults SET value = in_value WHERE setting_key = in_key; IF NOT FOUND THEN INSERT INTO defaults (setting_key, value) VALUES (in_setting_key, in_value); END IF; RETURN TRUE; END;
tsvector_setweight
get_current_ts_config
show_limit
show_trgm
similarity
similarity_op
tsa_snb_en_init
tsa_snb_lexize
tsa_snb_ru_init
tsa_snb_ru_init_koi8
tsa_snb_ru_init_utf8
tsa_spell_init
tsa_spell_lexize
ts_stat1
ts_stat2
tsvector_strip
tsa_syn_init
tsa_syn_lexize
Retrieves specified tax form information from the database.
SELECT * FROM country_tax_form where id = $1;
Returns a set of all tax forms, ordered by country_id and id
SELECT * FROM country_tax_form ORDER BY country_id, id;
Returns a list of tax forms with an added field, country_name, to specify the name of the country.
SELECT t.id, t.form_name, t.country_id, c.name, t.default_reportable FROM country_tax_form t JOIN country c ON c.id = t.country_id ORDER BY c.name, t.form_name;
Saves tax form information to the database.
BEGIN UPDATE country_tax_form SET country_id = in_country_id, form_name =in_form_name, default_reportable = coalesce(in_default_reportable,false) WHERE id = in_id; IF FOUND THEN RETURN in_id; END IF; insert into country_tax_form(country_id,form_name, default_reportable) values (in_country_id, in_form_name, coalesce(in_default_reportable, false)); RETURN currval('country_tax_form_id_seq'); END;
This provides a list of invoices and transactions that a report hits. This is intended to allow an organization to adjust what is reported on the 1099 before printing them.
DECLARE out_row tax_form_report_detail_item; BEGIN FOR out_row IN SELECT entity_credit_account.id, company.legal_name, company.entity_id, entity_credit_account.entity_class, entity.control_code, entity_credit_account.meta_number, sum(CASE WHEN gl.amount = 0 then 0 when relation = 'acc_trans' THEN ac.reportable_amount * pmt.amount / gl.amount ELSE 0 END * CASE WHEN gl.class = 'ap' THEN -1 else 1 end), sum(CASE WHEN gl.amount = 0 then 0 WHEN relation = 'invoice' THEN ac.reportable_amount * pmt.amount / gl.amount ELSE 0 END * CASE WHEN gl.class = 'ar' THEN -1 else 1 end), SUM(CASE WHEN gl.amount = 0 THEN 0 ELSE ac.reportable_amount * pmt.amount / gl.amount END * CASE WHEN gl.class = 'ap' THEN -1 else 1 end * CASE WHEN relation = 'invoice' THEN -1 ELSE 1 END), gl.invnumber, gl.duedate::text, gl.id FROM (select id, entity_credit_account, invnumber, duedate, amount, transdate, 'ar' as class FROM ar UNION select id, entity_credit_account, invnumber, duedate, amount, transdate, 'ap' as class FROM ap ) gl JOIN (select trans_id, 'acc_trans' as relation, sum(amount) as amount, sum(case when atf.reportable then amount else 0 end) as reportable_amount FROM acc_trans LEFT JOIN ac_tax_form atf ON (acc_trans.entry_id = atf.entry_id) GROUP BY trans_id UNION select trans_id, 'invoice' as relation, sum(sellprice * qty) as amount, sum(case when itf.reportable then sellprice * qty else 0 end) as reportable_amount FROM invoice LEFT JOIN invoice_tax_form itf ON (invoice.id = itf.invoice_id) GROUP BY trans_id ) ac ON (ac.trans_id = gl.id) JOIN entity_credit_account ON (gl.entity_credit_account = entity_credit_account.id) JOIN entity ON (entity.id = entity_credit_account.entity_id) JOIN company ON (entity.id = company.entity_id) JOIN country_tax_form ON (entity_credit_account.taxform_id = country_tax_form.id) JOIN (SELECT ac.trans_id, sum(ac.amount) as amount, as_array(entry_id) as entry_ids, as_array(chart_id) as chart_ids, count(*) as num FROM acc_trans ac where chart_id in (select account_id from account_link where description like '%paid') AND transdate BETWEEN in_begin AND in_end group by ac.trans_id ) pmt ON (pmt.trans_id = gl.id) WHERE country_tax_form.id = in_tax_form_id AND meta_number = in_meta_number GROUP BY legal_name, meta_number, company.entity_id, entity_credit_account.entity_class, entity.control_code, gl.invnumber, gl.duedate, gl.id, entity_credit_account.id LOOP RETURN NEXT out_row; END LOOP; END;
This provides the total reportable value per vendor. As per 1099 forms, these are cash-basis documents and show amounts paid.
DECLARE out_row tax_form_report_item; BEGIN FOR out_row IN SELECT entity_credit_account.id, company.legal_name, company.entity_id, entity_credit_account.entity_class, entity.control_code, entity_credit_account.meta_number, sum(CASE WHEN gl.amount = 0 THEN 0 WHEN relation = 'acc_trans' THEN ac.reportable_amount * pmt.amount / gl.amount ELSE 0 END * CASE WHEN gl.class = 'ap' THEN -1 else 1 end), sum(CASE WHEN gl.amount = 0 THEN 0 WHEN relation = 'invoice' THEN ac.reportable_amount * pmt.amount / gl.amount ELSE 0 END * CASE WHEN gl.class = 'ar' THEN -1 else 1 end), sum(CASE WHEN gl.amount = 0 THEN 0 ELSE ac.reportable_amount * pmt.amount / gl.amount END * CASE WHEN gl.class = 'ap' THEN -1 else 1 end * CASE WHEN ac.relation = 'invoice' then -1 else 1 end) FROM (select id, transdate, entity_credit_account, invoice, amount, 'ar' as class FROM ar UNION select id, transdate, entity_credit_account, invoice, amount, 'ap' as class from ap ) gl JOIN (select trans_id, 'acc_trans' as relation, sum(amount) as amount, sum(case when atf.reportable then amount else 0 end) as reportable_amount FROM acc_trans LEFT JOIN ac_tax_form atf ON (acc_trans.entry_id = atf.entry_id) GROUP BY trans_id UNION select trans_id, 'invoice' as relation, sum(sellprice * qty) as amount, sum(case when itf.reportable then sellprice * qty else 0 end) as reportable_amount FROM invoice LEFT JOIN invoice_tax_form itf ON (invoice.id = itf.invoice_id) GROUP BY trans_id ) ac ON (ac.trans_id = gl.id AND ((gl.invoice is true and ac.relation='invoice') OR (gl.invoice is false and ac.relation='acc_trans'))) JOIN (SELECT ac.trans_id, sum(ac.amount) as amount, as_array(entry_id) as entry_ids, as_array(chart_id) as chart_ids, count(*) as num FROM acc_trans ac where chart_id in (select account_id from account_link where description like '%paid') AND transdate BETWEEN in_begin AND in_end group by ac.trans_id ) pmt ON (pmt.trans_id = gl.id) JOIN entity_credit_account ON (gl.entity_credit_account = entity_credit_account.id) JOIN entity ON (entity.id = entity_credit_account.entity_id) JOIN company ON (entity.id = company.entity_id) JOIN country_tax_form ON (entity_credit_account.taxform_id = country_tax_form.id) WHERE country_tax_form.id = in_tax_form_id GROUP BY legal_name, meta_number, company.entity_id, entity_credit_account.entity_class, entity.control_code, entity_credit_account.id LOOP RETURN NEXT out_row; END LOOP; END;
DECLARE r_eclass entity_class; roll_pfx text; BEGIN IF TG_OP = 'DELETE' THEN RETURN OLD; ELSE SELECT value INTO roll_pfx FROM defaults WHERE setting_key = 'roll_prefix'; SELECT * INTO r_eclass from entity_class WHERE id = NEW.entity_class; IF pg_has_role(SESSION_USER, coalesce(roll_pfx, 'lsmb_' || current_database() || '__') || 'contact_class_' || lower(regexp_replace( r_eclass.class, ' ', '_')), 'USAGE') THEN RETURN NEW; ELSE RAISE EXCEPTION 'Access Denied for class'; END IF; END IF; END;
tsa_thesaurus_init
tsa_thesaurus_lexize
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;
Turns a setof ARRAY[key,value] into an ARRAY[key||'='||value, key||'='||value,...]
aggregate_dummy
to_tsquery_byid
to_tsquery
tsa_to_tsquery_name
to_tsvector_byid
to_tsvector
tsa_to_tsvector_name
tsa_token_type_current
ts_token_type_byid
ts_token_type_byname
This trigger is used to track the id sequence entries across the transactions table, and with the ar, ap, and gl tables. This is necessary because these have not been properly refactored yet.
BEGIN IF tg_op = 'INSERT' THEN INSERT INTO transactions (id, table_name) VALUES (new.id, TG_RELNAME); ELSEIF tg_op = 'UPDATE' THEN IF new.id = old.id THEN return new; ELSE UPDATE transactions SET id = new.id WHERE id = old.id; END IF; ELSE DELETE FROM transactions WHERE id = old.id; END IF; RETURN new; END;
BEGIN IF NEW.onhand >= NEW.rop THEN NOTIFY parts_short; END IF; RETURN NEW; END;
BEGIN IF NEW.success IS NULL THEN NOTIFY job_entered; END IF; RETURN NEW; END;
select (select c.cfgname::text from pg_catalog.pg_ts_config as c where c.oid = show_curcfg()), t.alias as tok_type, t.descr as description, p.token, ARRAY ( SELECT m.mapdict::pg_catalog.regdictionary::pg_catalog.text FROM pg_catalog.pg_ts_config_map AS m WHERE m.mapcfg = show_curcfg() AND m.maptokentype = p.tokid ORDER BY m.mapseqno ) AS dict_name, strip(to_tsvector(p.token)) as tsvector from parse( _get_parser_from_curcfg(), $1 ) as p, token_type() as t where t.tokid = p.tokid
tsa_tsearch2
tsq_mcontained
tsq_mcontains
tsquery_and
tsquery_not
tsquery_or
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;
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;
Alloes a user to change his or her own password. The password is set to expire setting_get('password_duration') days after the password change.
DECLARE t_expires timestamp; t_password_duration text; BEGIN SELECT value INTO t_password_duration FROM defaults WHERE setting_key = 'password_duration'; IF t_password_duration IS NULL or t_password_duration='' THEN t_expires := 'infinity'; ELSE t_expires := now() + (t_password_duration::numeric::text || ' days')::interval; END IF; UPDATE users SET notify_password = DEFAULT where username = SESSION_USER; EXECUTE 'ALTER USER ' || quote_ident(SESSION_USER) || ' with ENCRYPTED password ' || quote_literal(in_new_password) || ' VALID UNTIL '|| quote_literal(t_expires); return 1; END;
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;
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';
select * from user_listable;
Returns the preferences row for the user.
declare v_row user_preference; BEGIN select * into v_row from user_preference where id = in_user_id; IF NOT FOUND THEN RAISE EXCEPTION 'Could not find user preferences for id %', in_user_id; ELSE return next v_row; END IF; END;
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;
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;
Retrieves a list of vouchers and amounts attached to the batch.
declare voucher_item record; BEGIN FOR voucher_item IN SELECT v.id, a.invoice, a.invnumber, e.name, v.batch_id, v.trans_id, a.amount, a.transdate, 'Payable' FROM voucher v JOIN ap a ON (v.trans_id = a.id) JOIN entity_credit_account eca ON (eca.id = a.entity_credit_account) JOIN entity e ON (eca.entity_id = e.id) WHERE v.batch_id = in_batch_id AND v.batch_class = (select id from batch_class WHERE class = 'ap') UNION SELECT v.id, a.invoice, a.invnumber, e.name, v.batch_id, v.trans_id, a.amount, a.transdate, 'Receivable' FROM voucher v JOIN ar a ON (v.trans_id = a.id) JOIN entity_credit_account eca ON (eca.id = a.entity_credit_account) JOIN entity e ON (eca.entity_id = e.id) WHERE v.batch_id = in_batch_id AND v.batch_class = (select id from batch_class WHERE class = 'ar') UNION ALL -- TODO: Add the class labels to the class table. SELECT v.id, false, a.source, cr.meta_number || '--' || co.legal_name , v.batch_id, v.trans_id, sum(CASE WHEN bc.class LIKE 'payment%' THEN a.amount * -1 ELSE a.amount END), a.transdate, CASE WHEN bc.class = 'payment' THEN 'Payment' WHEN bc.class = 'payment_reversal' THEN 'Payment Reversal' END FROM voucher v JOIN acc_trans a ON (v.id = a.voucher_id) JOIN batch_class bc ON (bc.id = v.batch_class) JOIN chart c ON (a.chart_id = c.id) JOIN ap ON (ap.id = a.trans_id) JOIN entity_credit_account cr ON (ap.entity_credit_account = cr.id) JOIN company co ON (cr.entity_id = co.entity_id) WHERE v.batch_id = in_batch_id AND a.voucher_id = v.id AND (bc.class like 'payment%' AND c.link = 'AP') GROUP BY v.id, a.source, cr.meta_number, co.legal_name , v.batch_id, v.trans_id, a.transdate, bc.class UNION ALL SELECT v.id, false, a.source, a.memo, v.batch_id, v.trans_id, CASE WHEN bc.class LIKE 'receipt%' THEN sum(a.amount) * -1 ELSE sum(a.amount) END, a.transdate, CASE WHEN bc.class = 'receipt' THEN 'Receipt' WHEN bc.class = 'receipt_reversal' THEN 'Receipt Reversal' END FROM voucher v JOIN acc_trans a ON (v.id = a.voucher_id) JOIN batch_class bc ON (bc.id = v.batch_class) JOIN chart c ON (a.chart_id = c.id) JOIN ar ON (ar.id = a.trans_id) JOIN entity_credit_account cr ON (ar.entity_credit_account = cr.id) JOIN company co ON (cr.entity_id = co.entity_id) WHERE v.batch_id = in_batch_id AND a.voucher_id = v.id AND (bc.class like 'receipt%' AND c.link = 'AR') GROUP BY v.id, a.source, cr.meta_number, co.legal_name , a.memo, v.batch_id, v.trans_id, a.transdate, bc.class UNION ALL SELECT v.id, false, g.reference, g.description, v.batch_id, v.trans_id, sum(a.amount), g.transdate, 'GL' FROM voucher v JOIN gl g ON (g.id = v.trans_id) JOIN acc_trans a ON (v.trans_id = a.trans_id) WHERE a.amount > 0 AND v.batch_id = in_batch_id AND v.batch_class IN (select id from batch_class where class = 'gl') GROUP BY v.id, g.reference, g.description, v.batch_id, v.trans_id, g.transdate ORDER BY 7, 1 LOOP RETURN NEXT voucher_item; END LOOP; END;
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;
SELECT * FROM warehouse order by description;
Mapping journal_line to country_tax_form for reporting purposes.
F-Key | Name | Type | Description |
---|---|---|---|
lsmb14.acc_trans.entry_id | entry_id | integer | PRIMARY KEY |
reportable | boolean |
This table stores line items for financial transactions. Please note that payments in 1.3 are not full-fledged transactions.
F-Key | Name | Type | Description |
---|---|---|---|
lsmb14.transactions.id | trans_id | integer | NOT NULL |
lsmb14.account.id | chart_id | integer | NOT NULL |
amount | numeric | NOT NULL | |
transdate | date | DEFAULT ('now'::text)::date | |
source | text |
Document Source identifier for individual line items, usually used for payments. |
|
cleared | boolean | DEFAULT false | |
fx_transaction | boolean | DEFAULT false | |
memo | text | ||
invoice_id | integer | ||
approved | boolean | DEFAULT true | |
cleared_on | date | ||
reconciled_on | date | ||
lsmb14.voucher.id | voucher_id | integer | |
entry_id | serial | PRIMARY KEY |
Tables referencing this one via Foreign Key Constraints:
ac_transdate_year_idx date_part('YEAR'::text, transdate) acc_trans_voucher_id_idx voucher_idThis table stores the main account info.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | UNIQUE NOT NULL | |
accno | text | PRIMARY KEY | |
description | text | ||
is_temp | boolean |
NOT NULL
DEFAULT false
Only affects equity accounts. If set, close at end of year. |
|
category | character(1) | NOT NULL | |
gifi_accno | text | ||
lsmb14.account_heading.id | heading | integer | NOT NULL |
contra | boolean | NOT NULL DEFAULT false | |
tax | boolean | NOT NULL DEFAULT false | |
obsolete | boolean | NOT NULL DEFAULT false |
Tables referencing this one via Foreign Key Constraints:
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.
F-Key | Name | Type | Description |
---|---|---|---|
end_date | date | PRIMARY KEY | |
lsmb14.account.id | account_id | integer | PRIMARY KEY |
amount | numeric | NOT NULL | |
id | serial | UNIQUE NOT NULL | |
debits | numeric | ||
credits | numeric |
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.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | UNIQUE NOT NULL | |
accno | text | PRIMARY KEY | |
lsmb14.account_heading.id | parent_id | integer | |
description | text |
Tables referencing this one via Foreign Key Constraints:
F-Key | Name | Type | Description |
---|---|---|---|
lsmb14.account.id | account_id | integer | PRIMARY KEY |
lsmb14.account_link_description.description | description | text | PRIMARY KEY |
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.
F-Key | Name | Type | Description |
---|---|---|---|
description | text | PRIMARY KEY | |
summary | boolean | NOT NULL | |
custom | boolean | NOT NULL |
Tables referencing this one via Foreign Key Constraints:
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
F-Key | Name | Type | Description |
---|---|---|---|
lsmb14.transactions.id | id | integer | PRIMARY KEY DEFAULT nextval('id'::regclass) |
invnumber | text |
Text identifier for the invoice. Must be unique. |
|
transdate | date | DEFAULT ('now'::text)::date | |
lsmb14.entity.id | entity_id | integer | |
taxincluded | boolean | DEFAULT false | |
amount | numeric |
This stores the total amount (including taxes) for the transaction. |
|
netamount | numeric |
Total amount excluding taxes for the transaction. |
|
paid | numeric | ||
datepaid | date | ||
duedate | date | ||
invoice | boolean |
DEFAULT false
True if the transaction tracks goods/services purchase using the invoice table. False otherwise. |
|
ordnumber | text |
Order Number |
|
curr | character(3) |
3 letters to identify the currency. |
|
notes | text |
These notes are displayed on the invoice when printed or emailed |
|
lsmb14.entity_employee.entity_id | person_id | integer |
Person who created the transaction |
till | character varying(20) | ||
quonumber | text |
Quotation Number |
|
intnotes | text |
These notes are not displayed when the invoice is printed or emailed and may be updated without reposting hte invocie. |
|
shipvia | text | ||
language_code | character varying(6) | ||
ponumber | text |
Purchase Order Number |
|
shippingpoint | text | ||
on_hold | boolean | DEFAULT false | |
approved | boolean |
DEFAULT true
Only show in financial reports if true. |
|
reverse | boolean |
DEFAULT false
If true numbers are displayed after multiplying by -1 |
|
terms | smallint | ||
description | text | ||
force_closed | boolean |
Not exposed to the UI, but can be set to prevent an invoice from showing up for payment or in outstanding reports. |
|
lsmb14.entity_credit_account.id | entity_credit_account | integer |
NOT NULL
reference for the vendor account used. |
Name | Constraint |
---|---|
ap_check | CHECK ((((amount IS NULL) AND (curr IS NULL)) OR ((amount IS NOT NULL) AND (curr IS NOT NULL)))) |
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
F-Key | Name | Type | Description |
---|---|---|---|
lsmb14.transactions.id | id | integer | PRIMARY KEY DEFAULT nextval('id'::regclass) |
invnumber | text |
UNIQUE
Text identifier for the invoice. Must be unique. |
|
transdate | date | DEFAULT ('now'::text)::date | |
lsmb14.entity.id | entity_id | integer | |
taxincluded | boolean | ||
amount | numeric |
This stores the total amount (including taxes) for the transaction. |
|
netamount | numeric |
Total amount excluding taxes for the transaction. |
|
paid | numeric | ||
datepaid | date | ||
duedate | date | ||
invoice | boolean |
DEFAULT false
True if the transaction tracks goods/services purchase using the invoice table. False otherwise. |
|
shippingpoint | text | ||
terms | smallint | ||
notes | text |
These notes are displayed on the invoice when printed or emailed |
|
curr | character(3) |
3 letters to identify the currency. |
|
ordnumber | text |
Order Number |
|
lsmb14.entity_employee.entity_id | person_id | integer |
Person who created the transaction |
till | character varying(20) | ||
quonumber | text |
Quotation Number |
|
intnotes | text |
These notes are not displayed when the invoice is printed or emailed and may be updated without reposting hte invocie. |
|
shipvia | text | ||
language_code | character varying(6) | ||
ponumber | text |
Purchase Order Number |
|
on_hold | boolean | DEFAULT false | |
reverse | boolean |
DEFAULT false
If true numbers are displayed after multiplying by -1 |
|
approved | boolean |
DEFAULT true
Only show in financial reports if true. |
|
lsmb14.entity_credit_account.id | entity_credit_account | integer |
NOT NULL
reference for the customer account used. |
force_closed | boolean |
Not exposed to the UI, but can be set to prevent an invoice from showing up for payment or in outstanding reports. |
|
description | text | ||
is_return | boolean | DEFAULT false |
Name | Constraint |
---|---|
ar_check | CHECK ((((amount IS NULL) AND (curr IS NULL)) OR ((amount IS NOT NULL) AND (curr IS NOT NULL)))) |
Holds mapping for parts that are members of assemblies.
F-Key | Name | Type | Description |
---|---|---|---|
lsmb14.parts.id | id | integer |
PRIMARY KEY
This is the id of the assembly the part is being mapped to. |
lsmb14.parts.id | parts_id | integer |
PRIMARY KEY
ID of part that is a member of the assembly. |
qty | numeric | ||
bom | boolean | ||
adj | boolean |
The account fields here set the defaults for the individual asset items. They are non-authoritative.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | UNIQUE NOT NULL | |
label | text | PRIMARY KEY | |
lsmb14.account.id | asset_account_id | integer | |
lsmb14.account.id | dep_account_id | integer | |
lsmb14.asset_dep_method.id | method | integer |
Tables referencing this one via Foreign Key Constraints:
Stores asset depreciation methods, and their relevant stored procedures. The fixed asset system is such depreciation methods can be plugged in via this table.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | UNIQUE NOT NULL | |
method | text |
PRIMARY KEY
These are keyed to specific stored procedures. Currently only "straight_line" is supported |
|
sproc | text |
UNIQUE
NOT NULL
The sproc mentioned here is a stored procedure which must have the following arguments: (in_asset_ids int[], in_report_date date, in_report_id int). Here in_asset_ids are the assets to be depreciated, in_report_date is the date of the report, and in_report_id is the id of the report. The sproc MUST insert the relevant lines into asset_report_line. |
|
unit_label | text | NOT NULL | |
short_name | text | UNIQUE NOT NULL | |
lsmb14.asset_unit_class.id | unit_class | integer | NOT NULL |
Tables referencing this one via Foreign Key Constraints:
F-Key | Name | Type | Description |
---|---|---|---|
label | text | PRIMARY KEY | |
id | serial | UNIQUE NOT NULL | |
multiple | integer | ||
short_label | character(1) |
Name | Constraint |
---|---|
asset_disposal_method_multiple_check | CHECK ((multiple = ANY (ARRAY[1, 0, (-1)]))) |
Tables referencing this one via Foreign Key Constraints:
Stores details of asset items. The account fields here are authoritative, while the ones in the asset_class table are defaults.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
description | text | ||
tag | text |
UNIQUE#1
NOT NULL
This can be plugged into other routines to generate it automatically via ALTER TABLE .... SET DEFAULT..... |
|
purchase_value | numeric | ||
salvage_value | numeric | ||
usable_life | numeric | ||
purchase_date | date | NOT NULL | |
start_depreciation | date | NOT NULL | |
lsmb14.warehouse.id | location_id | integer | |
lsmb14.business_unit.id | department_id | integer | |
lsmb14.eca_invoice.journal_id | invoice_id | integer | |
lsmb14.account.id | asset_account_id | integer | |
lsmb14.account.id | dep_account_id | integer | |
lsmb14.account.id | exp_account_id | integer | |
lsmb14.asset_item.id | obsolete_by | integer | UNIQUE#1 |
lsmb14.asset_class.id | asset_class_id | integer |
Tables referencing this one via Foreign Key Constraints:
F-Key | Name | Type | Description |
---|---|---|---|
id | integer | NOT NULL DEFAULT nextval('note_id_seq'::regclass) | |
note_class | integer | NOT NULL DEFAULT 4 | |
note | text | NOT NULL | |
vector | tsvector | NOT NULL DEFAULT ''::tsvector | |
created | timestamp without time zone | NOT NULL DEFAULT now() | |
created_by | text | DEFAULT "session_user"() | |
lsmb14.asset_item.id | ref_key | integer | NOT NULL |
subject | text |
Table lsmb14.asset_note Inherits note,
Name | Constraint |
---|---|
asset_note_note_class_check | CHECK ((note_class = 4)) |
Asset reports are discrete sets of depreciation or disposal transctions, and each one may be turned into no more than one GL transaction.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
report_date | date | ||
lsmb14.journal_entry.id | gl_id | bigint | UNIQUE |
lsmb14.asset_class.id | asset_class | bigint | |
lsmb14.asset_report_class.id | report_class | integer | |
lsmb14.entity.id | entered_by | bigint | NOT NULL DEFAULT person__get_my_entity_id() |
lsmb14.entity.id | approved_by | bigint | |
entered_at | timestamp without time zone | DEFAULT now() | |
approved_at | timestamp without time zone | ||
depreciated_qty | numeric | ||
dont_approve | boolean | DEFAULT false | |
submitted | boolean | NOT NULL DEFAULT false |
Tables referencing this one via Foreign Key Constraints:
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.
F-Key | Name | Type | Description |
---|---|---|---|
id | integer | UNIQUE NOT NULL | |
class | text | PRIMARY KEY |
Tables referencing this one via Foreign Key Constraints:
F-Key | Name | Type | Description |
---|---|---|---|
lsmb14.asset_item.id | asset_id | bigint | PRIMARY KEY |
lsmb14.asset_report.id | report_id | bigint | PRIMARY KEY |
amount | numeric | ||
lsmb14.business_unit.id | department_id | integer |
In case assets are moved between departments, we have to store this here. |
lsmb14.warehouse.id | warehouse_id | integer |
Maps disposal method to line items in the asset disposal report.
F-Key | Name | Type | Description |
---|---|---|---|
lsmb14.asset_report.id | report_id | integer | PRIMARY KEY |
lsmb14.asset_item.id | asset_id | integer | PRIMARY KEY |
lsmb14.asset_disposal_method.id | disposal_method_id | integer | PRIMARY KEY |
percent_disposed | numeric |
F-Key | Name | Type | Description |
---|---|---|---|
id | integer | UNIQUE NOT NULL | |
class | text | PRIMARY KEY |
Tables referencing this one via Foreign Key Constraints:
This stores information on who entered or updated rows in the ar, ap, or gl tables.
F-Key | Name | Type | Description |
---|---|---|---|
trans_id | integer | ||
tablename | text | ||
reference | text | ||
formname | text | ||
action | text | ||
transdate | timestamp without time zone | DEFAULT now() | |
lsmb14.person.entity_id | person_id | integer | NOT NULL |
entry_id | bigserial | PRIMARY KEY |
Stores batch header info. Batches are groups of vouchers that are posted together.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
lsmb14.batch_class.id | batch_class_id | integer |
NOT NULL
Note that this field is largely used for sorting the vouchers. A given batch is NOT restricted to this type. |
control_code | text | NOT NULL | |
description | text | ||
default_date | date | NOT NULL | |
approved_on | date | ||
lsmb14.entity_employee.entity_id | approved_by | integer | |
lsmb14.entity_employee.entity_id | created_by | integer | |
lsmb14.session.session_id | locked_by | integer | |
created_on | date | DEFAULT now() |
Name | Constraint |
---|---|
batch_control_code_check | CHECK ((length(control_code) > 0)) |
Tables referencing this one via Foreign Key Constraints:
These values are hard-coded. Please coordinate before adding standard values. Values from 900 to 999 are reserved for local use.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | UNIQUE NOT NULL | |
class | character varying | PRIMARY KEY |
Tables referencing this one via Foreign Key Constraints:
F-Key | Name | Type | Description |
---|---|---|---|
lsmb14.business_unit_class.id | bu_class_id | integer | PRIMARY KEY |
lsmb14.lsmb_module.id | module_id | integer | PRIMARY KEY |
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | UNIQUE NOT NULL | |
start_date | date | NOT NULL | |
end_date | date | NOT NULL | |
reference | text | PRIMARY KEY | |
description | text | NOT NULL | |
lsmb14.entity.id | entered_by | integer | NOT NULL DEFAULT person__get_my_entity_id() |
lsmb14.entity.id | approved_by | integer | |
lsmb14.entity.id | obsolete_by | integer | |
entered_at | timestamp without time zone | NOT NULL DEFAULT now() | |
approved_at | timestamp without time zone | ||
obsolete_at | timestamp without time zone |
Name | Constraint |
---|---|
budget_info_check | CHECK ((start_date < end_date)) |
Tables referencing this one via Foreign Key Constraints:
F-Key | Name | Type | Description |
---|---|---|---|
lsmb14.budget_info.id | budget_id | integer | PRIMARY KEY |
lsmb14.account.id | account_id | integer | PRIMARY KEY |
description | text | ||
amount | numeric | NOT NULL |
F-Key | Name | Type | Description |
---|---|---|---|
id | integer | PRIMARY KEY DEFAULT nextval('note_id_seq'::regclass) | |
note_class | integer | NOT NULL DEFAULT 6 | |
note | text | NOT NULL | |
vector | tsvector | NOT NULL DEFAULT ''::tsvector | |
created | timestamp without time zone | NOT NULL DEFAULT now() | |
created_by | text | DEFAULT "session_user"() | |
lsmb14.budget_info.id | ref_key | integer | NOT NULL |
subject | text |
Table lsmb14.budget_note Inherits note,
Name | Constraint |
---|---|
budget_note_note_class_check | CHECK ((note_class = 6)) |
F-Key | Name | Type | Description |
---|---|---|---|
lsmb14.budget_info.id | budget_id | integer | UNIQUE PRIMARY KEY |
lsmb14.business_unit.id | bu_id | integer | NOT NULL |
lsmb14.business_unit_class.id | bu_class | integer | PRIMARY KEY |
Groups of Customers assigned joint discounts.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
description | text | ||
discount | numeric |
Tracks Projects, Departments, Funds, Etc.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | UNIQUE#1 PRIMARY KEY | |
lsmb14.business_unit_class.id | class_id | integer | UNIQUE#2 UNIQUE#1 NOT NULL |
control_code | text | UNIQUE#2 | |
description | text | ||
start_date | date | ||
end_date | date | ||
lsmb14.business_unit.id | parent_id | integer | |
lsmb14.entity_credit_account.id | credit_id | integer |
Tables referencing this one via Foreign Key Constraints:
F-Key | Name | Type | Description |
---|---|---|---|
lsmb14.acc_trans.entry_id | entry_id | integer | PRIMARY KEY |
lsmb14.business_unit_class.id lsmb14.business_unit.class_id#1 | class_id | integer | PRIMARY KEY |
lsmb14.business_unit.id#1 | bu_id | integer | PRIMARY KEY |
Consolidates projects and departments, and allows this to be extended for funds accounting and other purposes.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | UNIQUE NOT NULL | |
label | text | PRIMARY KEY | |
active | boolean | NOT NULL DEFAULT false | |
ordering | integer |
Tables referencing this one via Foreign Key Constraints:
F-Key | Name | Type | Description |
---|---|---|---|
lsmb14.invoice.id | entry_id | integer | PRIMARY KEY |
lsmb14.business_unit_class.id lsmb14.business_unit.class_id#1 | class_id | integer | PRIMARY KEY |
lsmb14.business_unit.id#1 | bu_id | integer | PRIMARY KEY |
F-Key | Name | Type | Description |
---|---|---|---|
lsmb14.journal_line.id | entry_id | integer | PRIMARY KEY |
lsmb14.business_unit_class.id | bu_class | integer | PRIMARY KEY |
lsmb14.business_unit.id | bu_id | integer | NOT NULL |
F-Key | Name | Type | Description |
---|---|---|---|
lsmb14.orderitems.id | entry_id | integer | PRIMARY KEY |
lsmb14.business_unit_class.id lsmb14.business_unit.class_id#1 | class_id | integer | PRIMARY KEY |
lsmb14.business_unit.id#1 | bu_id | integer | PRIMARY KEY |
Translation information for projects, departments, etc.
F-Key | Name | Type | Description |
---|---|---|---|
lsmb14.business_unit.id | trans_id | integer | PRIMARY KEY |
language_code | character varying(6) | PRIMARY KEY | |
description | text |
Table lsmb14.business_unit_translation Inherits translation,
This view is used by cash basis reports to determine the fraction of a transaction to be counted.
F-Key | Name | Type | Description |
---|---|---|---|
id | integer | ||
portion | numeric | ||
rel | text |
( SELECT gl.id , 1::numeric AS portion ,'gl'::text AS rel FROM lsmb14.gl UNION ALLSELECT gl.id , CASE WHEN ( (gl.amount - sum (ac.amount) ) = (0)::numeric ) THEN (0)::numeric ELSE (gl.amount / (gl.amount - sum (ac.amount) ) ) END AS portion ,'ar'::text AS rel FROM ( (lsmb14.ar gl JOIN lsmb14.acc_trans ac ON ( (ac.trans_id = gl.id) ) ) JOIN lsmb14.account_link al ON ( ( (ac.chart_id = al.account_id) AND (al.description = 'AR'::text) ) ) ) GROUP BY gl.id , gl.amount ) UNION ALLSELECT gl.id , CASE WHEN ( (gl.amount - sum (ac.amount) ) = (0)::numeric ) THEN (0)::numeric ELSE (gl.amount / (gl.amount - sum (ac.amount) ) ) END AS portion ,'ap'::text AS rel FROM ( (lsmb14.ap gl JOIN lsmb14.acc_trans ac ON ( (ac.trans_id = gl.id) ) ) JOIN lsmb14.account_link al ON ( ( (ac.chart_id = al.account_id) AND (al.description = 'AP'::text) ) ) ) GROUP BY gl.id , gl.amount;
Compatibility chart for 1.2 and earlier.
F-Key | Name | Type | Description |
---|---|---|---|
id | integer | ||
accno | text | ||
description | text | ||
charttype | text | ||
category | bpchar | ||
link | text | ||
account_heading | integer | ||
gifi_accno | text | ||
contra | boolean | ||
tax | boolean |
SELECT account_heading.id , account_heading.accno , account_heading.description ,'H'::text AS charttype , NULL::bpchar AS category , NULL::text AS link , NULL::integer AS account_heading , NULL::text AS gifi_accno , false AS contra , false AS tax FROM lsmb14.account_heading UNIONSELECT c.id , c.accno , c.description ,'A'::text AS charttype , c.category , lsmb14.concat_colon (l.description) AS link , c.heading AS account_heading , c.gifi_accno , c.contra , c.tax FROM (lsmb14.account c LEFT JOIN lsmb14.account_link l ON ( (c.id = l.account_id) ) ) GROUP BY c.id , c.accno , c.description , c.category , c.heading , c.gifi_accno , c.contra , c.tax;
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | UNIQUE NOT NULL | |
lsmb14.entity.id | entity_id | integer | PRIMARY KEY |
legal_name | text | PRIMARY KEY | |
tax_id | text |
In the US this would be a EIN. |
|
sales_tax_id | text | ||
license_number | text | ||
sic_code | character varying | ||
created | date | NOT NULL DEFAULT ('now'::text)::date |
Name | Constraint |
---|---|
company_legal_name_check | CHECK ((legal_name ~ '[[:alnum:]_]'::text)) |
Tables referencing this one via Foreign Key Constraints:
Stores type of contact information attached to companies and persons. Please coordinate with others before adding new types.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | UNIQUE NOT NULL | |
class | text | PRIMARY KEY |
Name | Constraint |
---|---|
contact_class_class_check | CHECK ((class ~ '[[:alnum:]_]'::text)) |
Tables referencing this one via Foreign Key Constraints:
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
name | text | NOT NULL | |
short_name | text | NOT NULL | |
itu | text |
The ITU Telecommunication Standardization Sector code for calling internationally. For example, the US is 1, Great Britain is 44 |
Name | Constraint |
---|---|
country_name_check | CHECK ((name ~ '[[:alnum:]_]'::text)) |
country_short_name_check | CHECK ((short_name ~ '[[:alnum:]_]'::text)) |
Tables referencing this one via Foreign Key Constraints:
This table was designed for holding information relating to reportable sales or purchases, such as IRS 1099 forms and international equivalents.
F-Key | Name | Type | Description |
---|---|---|---|
lsmb14.country.id | country_id | integer | PRIMARY KEY |
form_name | text | PRIMARY KEY | |
id | serial | UNIQUE NOT NULL | |
default_reportable | boolean | NOT NULL DEFAULT false |
Tables referencing this one via Foreign Key Constraints:
Provides name mapping for the cash reconciliation screen.
F-Key | Name | Type | Description |
---|---|---|---|
lsmb14.account.id | chart_id | integer | NOT NULL |
account | text | NOT NULL |
This table holds header data for cash reports.
F-Key | Name | Type | Description |
---|---|---|---|
id | bigserial | PRIMARY KEY | |
lsmb14.account.id | chart_id | integer | NOT NULL |
their_total | numeric | NOT NULL | |
approved | boolean | NOT NULL DEFAULT false | |
submitted | boolean | NOT NULL DEFAULT false | |
end_date | date | NOT NULL DEFAULT now() | |
updated | timestamp without time zone | NOT NULL DEFAULT now() | |
lsmb14.entity.id | entered_by | integer | NOT NULL DEFAULT person__get_my_entity_id() |
entered_username | text | NOT NULL DEFAULT "session_user"() | |
deleted | boolean | NOT NULL DEFAULT false | |
lsmb14.entity.id | deleted_by | integer | |
lsmb14.entity.id | approved_by | integer | |
approved_username | text | ||
recon_fx | boolean | DEFAULT false |
Name | Constraint |
---|---|
cr_report_check | CHECK (((deleted IS NOT TRUE) OR (approved IS NOT TRUE))) |
Tables referencing this one via Foreign Key Constraints:
This stores line item data on transaction lines and whether they are cleared.
F-Key | Name | Type | Description |
---|---|---|---|
id | bigserial | PRIMARY KEY | |
lsmb14.cr_report.id | report_id | integer | NOT NULL |
scn | text |
This is the check number. Maps to journal_entry.reference |
|
their_balance | numeric | ||
our_balance | numeric | ||
errorcode | integer | ||
lsmb14.entity.id | user | integer | NOT NULL |
clear_time | date | ||
insert_time | timestamp with time zone | NOT NULL DEFAULT now() | |
trans_type | text | ||
post_date | date | ||
lsmb14.acc_trans.entry_id | ledger_id | integer | |
voucher_id | integer | ||
overlook | boolean | NOT NULL DEFAULT false | |
cleared | boolean | NOT NULL DEFAULT false |
Deprecated, use only with old code.
F-Key | Name | Type | Description |
---|---|---|---|
field_id | serial | PRIMARY KEY | |
lsmb14.custom_table_catalog.table_id | table_id | integer | |
field_name | text |
Deprecated, use only with old code.
F-Key | Name | Type | Description |
---|---|---|---|
table_id | serial | PRIMARY KEY | |
extends | text | ||
table_name | text |
Tables referencing this one via Foreign Key Constraints:
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.
F-Key | Name | Type | Description |
---|---|---|---|
setting_key | text | PRIMARY KEY | |
value | text |
Replaces the rest of the ar and ap tables. Also tracks payments and receipts.
F-Key | Name | Type | Description |
---|---|---|---|
order_id | integer |
Link to order it was created from |
|
lsmb14.journal_entry.id | journal_id | integer | PRIMARY KEY |
on_hold | boolean |
DEFAULT false
On hold invoices can not be paid, and overpayments that are on hold cannot be used to pay invoices. |
|
reverse | boolean |
DEFAULT false
When this is set to true, the invoice is shown with opposite normal numbers, i.e. negatives appear as positives, and positives appear as negatives. |
|
lsmb14.entity_credit_account.id | credit_id | integer | NOT NULL |
due | date | NOT NULL | |
lsmb14.language.code | language_code | character(6) | |
force_closed | boolean |
NOT NULL
DEFAULT false
When this is set to true, the invoice does not show up on outstanding reports and cannot be paid. Overpayments where this is set to true do not appear on outstanding reports and cannot be paid. |
|
order_number | text |
This is the order number of the other party. So for a sales invoice, this would be a purchase order, and for a vendor invoice, this would be a sales order. |
Tables referencing this one via Foreign Key Constraints:
Notes for entity_credit_account entries.
F-Key | Name | Type | Description |
---|---|---|---|
id | integer | PRIMARY KEY DEFAULT nextval('note_id_seq'::regclass) | |
note_class | integer | NOT NULL | |
note | text | NOT NULL | |
vector | tsvector | NOT NULL DEFAULT ''::tsvector | |
created | timestamp without time zone | NOT NULL DEFAULT now() | |
created_by | text | DEFAULT "session_user"() | |
lsmb14.entity_credit_account.id | ref_key | integer |
NOT NULL
references entity_credit_account.id |
subject | text |
Table lsmb14.eca_note Inherits note,
Name | Constraint |
---|---|
eca_note_note_class_check | CHECK ((note_class = 3)) |
Mapping customers and vendors to taxes.
F-Key | Name | Type | Description |
---|---|---|---|
lsmb14.entity_credit_account.id | eca_id | integer | PRIMARY KEY |
lsmb14.account.id | chart_id | integer | PRIMARY KEY |
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.
F-Key | Name | Type | Description |
---|---|---|---|
lsmb14.entity_credit_account.id | credit_id | integer | PRIMARY KEY |
lsmb14.contact_class.id | contact_class_id | integer | PRIMARY KEY |
contact | text | PRIMARY KEY | |
description | text |
Name | Constraint |
---|---|
eca_to_contact_contact_check | CHECK ((contact ~ '[[:alnum:]_]'::text)) |
This table is used for locations bound to contracts. For generic contact addresses, use entity_to_location instead
F-Key | Name | Type | Description |
---|---|---|---|
lsmb14.location.id | location_id | integer | PRIMARY KEY |
lsmb14.location_class.id | location_class | integer | PRIMARY KEY |
lsmb14.entity_credit_account.id | credit_id | integer | PRIMARY KEY |
F-Key | Name | Type | Description |
---|---|---|---|
label | text | PRIMARY KEY | |
id | serial | UNIQUE NOT NULL |
Tables referencing this one via Foreign Key Constraints:
F-Key | Name | Type | Description |
---|---|---|---|
entity_id | integer | ||
startdate | date | ||
enddate | date | ||
role | character varying(20) | ||
ssn | text | ||
sales | boolean | ||
manager_id | integer | ||
employeenumber | character varying(32) | ||
dob | date | ||
manager | text | ||
note | text | ||
name | text |
SELECT e.entity_id , e.startdate , e.enddate , e.role , e.ssn , e.sales , e.manager_id , e.employeenumber , e.dob , em.name AS manager , emn.note , en.name FROM ( ( ( (lsmb14.entity_employee e LEFT JOIN lsmb14.entity en ON ( (e.entity_id = en.id) ) ) LEFT JOIN lsmb14.entity_employee m ON ( (e.manager_id = m.entity_id) ) ) LEFT JOIN lsmb14.entity em ON ( (em.id = m.entity_id) ) ) LEFT JOIN lsmb14.entity_note emn ON ( (emn.ref_key = em.id) ) );
F-Key | Name | Type | Description |
---|---|---|---|
lsmb14.entity_employee.entity_id | employee_id | integer | PRIMARY KEY |
lsmb14.employee_class.id | ec_id | integer |
F-Key | Name | Type | Description |
---|---|---|---|
salutation | text | ||
first_name | text | ||
last_name | text | ||
entity_id | integer | ||
startdate | date | ||
enddate | date | ||
role | character varying(20) | ||
ssn | text | ||
sales | boolean | ||
manager_id | integer | ||
employeenumber | character varying(32) | ||
dob | date |
SELECT s.salutation , p.first_name , p.last_name , ee.entity_id , ee.startdate , ee.enddate , ee.role , ee.ssn , ee.sales , ee.manager_id , ee.employeenumber , ee.dob FROM ( (lsmb14.person p JOIN lsmb14.entity_employee ee USING (entity_id) ) LEFT JOIN lsmb14.salutation s ON ( (p.salutation_id = s.id) ) );
The primary entity table to map to all contacts
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | UNIQUE NOT NULL | |
name | text |
This is the common name of an entity. If it was a person it may be Joshua Drake, a company Acme Corp. You may also choose to use a domain such as commandprompt.com |
|
lsmb14.entity_class.id lsmb14.entity_class.id | entity_class | integer | PRIMARY KEY |
created | date | NOT NULL DEFAULT ('now'::text)::date | |
control_code | text | UNIQUE PRIMARY KEY DEFAULT setting_increment('entity_control'::character varying) | |
lsmb14.country.id | country_id | integer | NOT NULL |
Name | Constraint |
---|---|
entity_name_check | CHECK ((name ~ '[[:alnum:]_]'::text)) |
Tables referencing this one via Foreign Key Constraints:
This stores bank account information for both companies and persons.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | UNIQUE NOT NULL | |
lsmb14.entity.id | entity_id | integer | PRIMARY KEY |
bic | character varying |
PRIMARY KEY
Banking Institution Code, such as routing number of SWIFT code. |
|
iban | character varying |
PRIMARY KEY
International Bank Account Number. used to store the actual account number for the banking institution. |
Tables referencing this one via Foreign Key Constraints:
Defines the class type such as vendor, customer, contact, employee
F-Key | Name | Type | Description |
---|---|---|---|
id | serial |
PRIMARY KEY
The first 7 values are reserved and permanent. Individuals who create new classes, however, should coordinate with others for ranges to use. |
|
class | text | NOT NULL | |
lsmb14.country.id | country_id | integer | |
active | boolean | NOT NULL DEFAULT true |
Name | Constraint |
---|---|
entity_class_class_check | CHECK ((class ~ '[[:alnum:]_]'::text)) |
Tables referencing this one via Foreign Key Constraints:
entity_class_idx lower(class)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.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | UNIQUE NOT NULL | |
lsmb14.entity.id | entity_id | integer | PRIMARY KEY |
lsmb14.entity_class.id | entity_class | integer | PRIMARY KEY |
pay_to_name | text | ||
discount | numeric | ||
description | text | ||
discount_terms | integer | ||
lsmb14.account.id | discount_account_id | integer | |
taxincluded | boolean | DEFAULT false | |
creditlimit | numeric | ||
terms | smallint | ||
meta_number | character varying(32) |
PRIMARY KEY
This stores the human readable control code for the customer/vendor record. This is typically called the customer/vendor "account" in the application. |
|
business_id | integer | ||
lsmb14.language.code | language_code | character varying(6) | DEFAULT 'en'::character varying |
lsmb14.pricegroup.id | pricegroup_id | integer | |
curr | character(3) | ||
startdate | date | DEFAULT ('now'::text)::date | |
enddate | date | ||
threshold | numeric | ||
lsmb14.entity_employee.entity_id | employee_id | integer | |
lsmb14.person.id | primary_contact | integer | |
lsmb14.account.id | ar_ap_account_id | integer | |
lsmb14.account.id | cash_account_id | integer | |
lsmb14.entity_bank_account.id | bank_account | integer | |
lsmb14.country_tax_form.id | taxform_id | integer |
Name | Constraint |
---|---|
entity_credit_account_check | CHECK (((ar_ap_account_id IS NOT NULL) OR (entity_id = 0))) |
entity_credit_account_entity_class_check | CHECK ((entity_class = ANY (ARRAY[1, 2]))) |
Tables referencing this one via Foreign Key Constraints:
This contains employee-specific extensions to person/entity.
F-Key | Name | Type | Description |
---|---|---|---|
lsmb14.entity.id | entity_id | integer | PRIMARY KEY |
startdate | date | NOT NULL DEFAULT ('now'::text)::date | |
enddate | date | ||
role | character varying(20) | ||
ssn | text | ||
sales | boolean | DEFAULT false | |
lsmb14.entity.id | manager_id | integer | |
employeenumber | character varying(32) | ||
dob | date |
Tables referencing this one via Foreign Key Constraints:
F-Key | Name | Type | Description |
---|---|---|---|
id | integer | PRIMARY KEY DEFAULT nextval('note_id_seq'::regclass) | |
note_class | integer | NOT NULL | |
note | text | NOT NULL | |
vector | tsvector | NOT NULL DEFAULT ''::tsvector | |
created | timestamp without time zone | NOT NULL DEFAULT now() | |
created_by | text | DEFAULT "session_user"() | |
lsmb14.entity.id | ref_key | integer | NOT NULL |
subject | text | ||
lsmb14.entity.id | entity_id | integer |
Table lsmb14.entity_note Inherits note,
Name | Constraint |
---|---|
entity_note_note_class_check | CHECK ((note_class = 1)) |
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.
F-Key | Name | Type | Description |
---|---|---|---|
lsmb14.entity.id | entity_id | integer | PRIMARY KEY |
other_name | text | PRIMARY KEY |
Name | Constraint |
---|---|
entity_other_name_other_name_check | CHECK ((other_name ~ '[[:alnum:]_]'::text)) |
This table stores contact information for entities
F-Key | Name | Type | Description |
---|---|---|---|
lsmb14.entity.id | entity_id | integer | PRIMARY KEY |
lsmb14.contact_class.id | contact_class_id | integer | PRIMARY KEY |
contact | text | PRIMARY KEY | |
description | text |
Name | Constraint |
---|---|
entity_to_contact_contact_check | CHECK ((contact ~ '[[:alnum:]_]'::text)) |
This table is used for locations generic to companies. For contract-bound addresses, use eca_to_location instead
F-Key | Name | Type | Description |
---|---|---|---|
lsmb14.location.id | location_id | integer | PRIMARY KEY |
lsmb14.location_class.id | location_class | integer | PRIMARY KEY |
lsmb14.entity.id | entity_id | integer | PRIMARY KEY |
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).
F-Key | Name | Type | Description |
---|---|---|---|
curr | character(3) | PRIMARY KEY | |
transdate | date | PRIMARY KEY | |
buy | numeric | ||
sell | numeric |
Abstract table, holds no records. Inheriting table store actual file attachment data. Can be queried however to retrieve lists of all files.
F-Key | Name | Type | Description |
---|---|---|---|
content | bytea | NOT NULL | |
lsmb14.mime_type.id | mime_type_id | integer | NOT NULL |
file_name | text | PRIMARY KEY | |
description | text | ||
lsmb14.entity.id | uploaded_by | integer | NOT NULL |
uploaded_at | timestamp without time zone | NOT NULL DEFAULT now() | |
id | serial | UNIQUE NOT NULL | |
ref_key | integer |
PRIMARY KEY
This column inheriting tables is used to reference the database row for the attachment. Inheriting tables MUST set the foreign key here appropriately. This can also be used to create classifications of other documents, such as by source of automatic import (where the file is not yet attached) or even standard, long-lived documents. |
|
lsmb14.file_class.id | file_class | integer | PRIMARY KEY |
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.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | UNIQUE NOT NULL | |
class | text | PRIMARY KEY |
Tables referencing this one via Foreign Key Constraints:
File attachments primarily attached to customer and vendor agreements.
F-Key | Name | Type | Description |
---|---|---|---|
content | bytea | NOT NULL | |
mime_type_id | integer | NOT NULL | |
file_name | text | PRIMARY KEY | |
description | text | ||
uploaded_by | integer | NOT NULL | |
uploaded_at | timestamp without time zone | NOT NULL DEFAULT now() | |
id | integer | UNIQUE NOT NULL DEFAULT nextval('file_base_id_seq'::regclass) | |
lsmb14.entity_credit_account.id | ref_key | integer | PRIMARY KEY |
file_class | integer | PRIMARY KEY |
Table lsmb14.file_eca Inherits file_base,
Name | Constraint |
---|---|
file_eca_file_class_check | CHECK ((file_class = 5)) |
File attachments primarily attached to entities.
F-Key | Name | Type | Description |
---|---|---|---|
content | bytea | NOT NULL | |
mime_type_id | integer | NOT NULL | |
file_name | text | PRIMARY KEY | |
description | text | ||
uploaded_by | integer | NOT NULL | |
uploaded_at | timestamp without time zone | NOT NULL DEFAULT now() | |
id | integer | UNIQUE NOT NULL DEFAULT nextval('file_base_id_seq'::regclass) | |
lsmb14.entity.id | ref_key | integer | PRIMARY KEY |
file_class | integer | PRIMARY KEY |
Table lsmb14.file_entity Inherits file_base,
Name | Constraint |
---|---|
file_entity_file_class_check | CHECK ((file_class = 4)) |
F-Key | Name | Type | Description |
---|---|---|---|
file_id | integer | ||
ref_key | integer | ||
reference | text | ||
type | text | ||
dest_class | integer | ||
source_class | integer | ||
dest_ref | integer |
SELECT file_tx_links.file_id , file_tx_links.ref_key , file_tx_links.reference , file_tx_links.type , file_tx_links.dest_class , file_tx_links.source_class , file_tx_links.dest_ref FROM lsmb14.file_tx_links UNIONSELECT file_order_links.file_id , file_order_links.ref_key , file_order_links.reference , file_order_links.oe_class AS type , file_order_links.dest_class , file_order_links.source_class , file_order_links.dest_ref FROM lsmb14.file_order_links;
File attachments primarily attached to orders and quotations.
F-Key | Name | Type | Description |
---|---|---|---|
content | bytea | NOT NULL | |
mime_type_id | integer | NOT NULL | |
file_name | text | PRIMARY KEY | |
description | text | ||
uploaded_by | integer | NOT NULL | |
uploaded_at | timestamp without time zone | NOT NULL DEFAULT now() | |
id | integer | UNIQUE NOT NULL DEFAULT nextval('file_base_id_seq'::regclass) | |
lsmb14.oe.id | ref_key | integer | PRIMARY KEY |
file_class | integer | PRIMARY KEY |
Table lsmb14.file_order Inherits file_base,
Name | Constraint |
---|---|
file_order_file_class_check | CHECK ((file_class = 2)) |
Tables referencing this one via Foreign Key Constraints:
F-Key | Name | Type | Description |
---|---|---|---|
file_id | integer | ||
ref_key | integer | ||
reference | text | ||
oe_class | text | ||
dest_class | integer | ||
source_class | integer | ||
dest_ref | integer |
SELECT sl.file_id , sl.ref_key , oe.ordnumber AS reference , oc.oe_class , sl.dest_class , sl.source_class , sl.ref_key AS dest_ref FROM ( (lsmb14.file_secondary_attachment sl JOIN lsmb14.oe ON ( (sl.ref_key = oe.id) ) ) JOIN lsmb14.oe_class oc ON ( (oe.oe_class_id = oc.id) ) ) WHERE (sl.source_class = 2);
Secondary links from one order to another, for example to support order consolidation.
F-Key | Name | Type | Description |
---|---|---|---|
lsmb14.file_order.id | file_id | integer | PRIMARY KEY |
source_class | integer | PRIMARY KEY | |
lsmb14.oe.id | ref_key | integer | PRIMARY KEY |
dest_class | integer | PRIMARY KEY | |
attached_by | integer | NOT NULL | |
attached_at | timestamp without time zone | NOT NULL DEFAULT now() |
Table lsmb14.file_order_to_order Inherits file_secondary_attachment,
Name | Constraint |
---|---|
file_order_to_order_dest_class_check | CHECK ((dest_class = 2)) |
file_order_to_order_source_class_check | CHECK ((source_class = 2)) |
Secondary links from orders to transactions, for example to track files when invoices are generated from orders.
F-Key | Name | Type | Description |
---|---|---|---|
lsmb14.file_order.id | file_id | integer | PRIMARY KEY |
source_class | integer | PRIMARY KEY | |
lsmb14.journal_entry.id | ref_key | integer | PRIMARY KEY |
dest_class | integer | PRIMARY KEY | |
attached_by | integer | NOT NULL | |
attached_at | timestamp without time zone | NOT NULL DEFAULT now() |
Table lsmb14.file_order_to_tx Inherits file_secondary_attachment,
Name | Constraint |
---|---|
file_order_to_tx_dest_class_check | CHECK ((dest_class = 1)) |
file_order_to_tx_source_class_check | CHECK ((source_class = 2)) |
File attachments primarily attached to goods and services.
F-Key | Name | Type | Description |
---|---|---|---|
content | bytea | NOT NULL | |
mime_type_id | integer | NOT NULL | |
file_name | text | PRIMARY KEY | |
description | text | ||
uploaded_by | integer | NOT NULL | |
uploaded_at | timestamp without time zone | NOT NULL DEFAULT now() | |
id | integer | UNIQUE NOT NULL DEFAULT nextval('file_base_id_seq'::regclass) | |
lsmb14.parts.id | ref_key | integer | PRIMARY KEY |
file_class | integer | PRIMARY KEY |
Table lsmb14.file_part Inherits file_base,
Name | Constraint |
---|---|
file_part_file_class_check | CHECK ((file_class = 3)) |
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.
F-Key | Name | Type | Description |
---|---|---|---|
file_id | integer | PRIMARY KEY | |
lsmb14.file_class.id | source_class | integer | PRIMARY KEY |
ref_key | integer | PRIMARY KEY | |
lsmb14.file_class.id | dest_class | integer | PRIMARY KEY |
lsmb14.entity.id | attached_by | integer | NOT NULL |
attached_at | timestamp without time zone | NOT NULL DEFAULT now() |
File attachments primarily attached to AR/AP/GL.
F-Key | Name | Type | Description |
---|---|---|---|
content | bytea | NOT NULL | |
mime_type_id | integer | NOT NULL | |
file_name | text | PRIMARY KEY | |
description | text | ||
uploaded_by | integer | NOT NULL | |
uploaded_at | timestamp without time zone | NOT NULL DEFAULT now() | |
id | integer | UNIQUE NOT NULL DEFAULT nextval('file_base_id_seq'::regclass) | |
lsmb14.journal_entry.id | ref_key | integer | PRIMARY KEY |
file_class | integer | PRIMARY KEY |
Table lsmb14.file_transaction Inherits file_base,
Name | Constraint |
---|---|
file_transaction_file_class_check | CHECK ((file_class = 1)) |
Tables referencing this one via Foreign Key Constraints:
F-Key | Name | Type | Description |
---|---|---|---|
file_id | integer | ||
ref_key | integer | ||
reference | text | ||
type | text | ||
dest_class | integer | ||
source_class | integer | ||
dest_ref | integer |
SELECT sl.file_id , sl.ref_key , gl.reference , gl.type , sl.dest_class , sl.source_class , sl.ref_key AS dest_ref FROM (lsmb14.file_secondary_attachment sl JOIN ( ( SELECT gl.id , gl.reference ,'gl'::text AS type FROM lsmb14.gl UNIONSELECT ar.id , ar.invnumber , CASE WHEN ar.invoice THEN 'is'::text ELSE 'ar'::text END AS type FROM lsmb14.ar ) UNIONSELECT ap.id , ap.invnumber , CASE WHEN ap.invoice THEN 'ir'::text ELSE 'ap'::text END AS type FROM lsmb14.ap ) gl ON ( ( (sl.ref_key = gl.id) AND (sl.source_class = 1) ) ) );
Secondary links from journal entries to orders.
F-Key | Name | Type | Description |
---|---|---|---|
lsmb14.file_transaction.id | file_id | integer | PRIMARY KEY |
source_class | integer | PRIMARY KEY | |
lsmb14.oe.id | ref_key | integer | PRIMARY KEY |
dest_class | integer | PRIMARY KEY | |
attached_by | integer | NOT NULL | |
attached_at | timestamp without time zone | NOT NULL DEFAULT now() |
Table lsmb14.file_tx_to_order Inherits file_secondary_attachment,
Name | Constraint |
---|---|
file_tx_to_order_dest_class_check | CHECK ((dest_class = 2)) |
file_tx_to_order_source_class_check | CHECK ((source_class = 1)) |
F-Key | Name | Type | Description |
---|---|---|---|
lsmb14.file_class.id | file_class | integer | PRIMARY KEY |
view_name | text | UNIQUE NOT NULL |
GIFI labels for accounts, used in Canada and some EU countries for tax reporting
F-Key | Name | Type | Description |
---|---|---|---|
accno | text | PRIMARY KEY | |
description | text |
This table holds summary information for entries in the general journal. Does not hold summary information in 1.3 for AR or AP entries.
F-Key | Name | Type | Description |
---|---|---|---|
lsmb14.transactions.id | id | integer | PRIMARY KEY DEFAULT nextval('id'::regclass) |
reference | text | ||
description | text | ||
transdate | date | DEFAULT ('now'::text)::date | |
lsmb14.person.id | person_id | integer |
the person_id of the employee who created the entry. |
notes | text | ||
approved | boolean | DEFAULT true |
Tables referencing this one via Foreign Key Constraints:
This table contains inventory mappings to warehouses, not general inventory management data.
F-Key | Name | Type | Description |
---|---|---|---|
lsmb14.entity_employee.entity_id | entity_id | integer | |
warehouse_id | integer | ||
parts_id | integer | ||
trans_id | integer | ||
orderitems_id | integer | ||
qty | numeric | ||
shippingdate | date | ||
entry_id | serial | PRIMARY KEY |
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
transdate | date | NOT NULL | |
source | text | ||
ar_trans_id | integer | ||
ap_trans_id | integer |
Tables referencing this one via Foreign Key Constraints:
F-Key | Name | Type | Description |
---|---|---|---|
lsmb14.inventory_report.id | report_id | integer | PRIMARY KEY |
lsmb14.parts.id | parts_id | integer | PRIMARY KEY |
counted | numeric | ||
expected | numeric |
Line items of invoices with goods/services attached.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
lsmb14.transactions.id | trans_id | integer | |
lsmb14.parts.id | parts_id | integer | |
description | text | ||
qty | numeric |
Positive is normal for sales invoices, negative for vendor invoices. |
|
allocated | integer |
Number of allocated items, negative relative to qty. When qty + allocated = 0, then the item is fully used for purposes of COGS calculations. |
|
sellprice | numeric | ||
precision | integer | ||
fxsellprice | numeric | ||
discount | numeric | ||
assemblyitem | boolean | DEFAULT false | |
unit | character varying | ||
deliverydate | date | ||
serialnumber | text | ||
notes | text |
Tables referencing this one via Foreign Key Constraints:
invoice_id_key id invoice_trans_id_key trans_idF-Key | Name | Type | Description |
---|---|---|---|
id | integer | PRIMARY KEY DEFAULT nextval('note_id_seq'::regclass) | |
note_class | integer | NOT NULL | |
note | text | NOT NULL | |
vector | tsvector | NOT NULL DEFAULT ''::tsvector | |
created | timestamp without time zone | NOT NULL DEFAULT now() | |
created_by | text | DEFAULT "session_user"() | |
lsmb14.invoice.id | ref_key | integer | NOT NULL |
subject | text |
Table lsmb14.invoice_note Inherits note,
invoice_note_id_idx id invoice_note_vectors_idx vectorMaping invoice to country_tax_form.
F-Key | Name | Type | Description |
---|---|---|---|
lsmb14.invoice.id | invoice_id | integer | PRIMARY KEY |
reportable | boolean |
Time and materials cards. Materials cards not implemented.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
lsmb14.business_unit.id | business_unit_id | integer | |
parts_id | integer | ||
description | text | ||
qty | numeric | ||
allocated | numeric | ||
sellprice | numeric | ||
fxsellprice | numeric | ||
serialnumber | text | ||
checkedin | timestamp with time zone | ||
checkedout | timestamp with time zone | ||
lsmb14.person.id | person_id | integer | NOT NULL |
notes | text | ||
total | numeric | NOT NULL | |
non_billable | numeric | NOT NULL |
F-Key | Name | Type | Description |
---|---|---|---|
lsmb14.business_unit.id | bu_id | integer | PRIMARY KEY |
parts_id | integer |
Job costing/manufacturing here not implemented. |
|
production | numeric | ||
completed | numeric |
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.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
reference | text |
NOT NULL
Invoice number or journal entry number. |
|
description | text | ||
lsmb14.session.session_id | locked_by | integer | |
lsmb14.journal_type.id | journal | integer | |
post_date | date | NOT NULL DEFAULT now() | |
effective_start | date |
NOT NULL
For transactions whose effects are spread out over a period of time, this is the effective start date for the transaction. To be used by add-ons for automating adjustments. |
|
effective_end | date |
NOT NULL
For transactions whose effects are spread out over a period of time, this is the effective end date for the transaction. To be used by add-ons for automating adjustments. |
|
currency | character(3) | NOT NULL | |
approved | boolean | DEFAULT false | |
is_template | boolean |
DEFAULT false
Set true for template transactions. Templates can never be approved but can be copied into new transactions and are useful for recurrances. |
|
lsmb14.entity.id | entered_by | integer | NOT NULL |
lsmb14.entity.id | approved_by | integer |
Name | Constraint |
---|---|
journal_entry_check | CHECK (((is_template IS FALSE) OR (approved IS FALSE))) |
Tables referencing this one via Foreign Key Constraints:
Replaces acc_trans as the main account transaction line table.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
lsmb14.account.id | account_id | integer | NOT NULL |
lsmb14.journal_entry.id | journal_id | integer | NOT NULL |
amount | numeric | NOT NULL | |
cleared | boolean |
NOT NULL
DEFAULT false
Still needed both for legacy data and in case reconciliation data must eventually be purged. |
|
lsmb14.cr_report.id | reconciliation_report | integer | |
lsmb14.account_link_description.description | line_type | text |
Name | Constraint |
---|---|
journal_line_amount_check | CHECK ((amount <> 'NaN'::numeric)) |
Tables referencing this one via Foreign Key Constraints:
This stores notes attached to journal entries, including payments and invoices.
F-Key | Name | Type | Description |
---|---|---|---|
id | integer | PRIMARY KEY DEFAULT nextval('note_id_seq'::regclass) | |
note_class | integer | NOT NULL | |
note | text | NOT NULL | |
vector | tsvector | NOT NULL DEFAULT ''::tsvector | |
created | timestamp without time zone | NOT NULL DEFAULT now() | |
created_by | text | DEFAULT "session_user"() | |
lsmb14.journal_entry.id | ref_key | integer | NOT NULL |
subject | text | ||
internal_only | boolean |
NOT NULL
DEFAULT false
When set to true, does not show up in notes list for invoice templates |
Table lsmb14.journal_note Inherits note,
Name | Constraint |
---|---|
journal_note_note_class_check | CHECK ((note_class = 5)) |
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
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | UNIQUE NOT NULL | |
name | text | PRIMARY KEY |
Tables referencing this one via Foreign Key Constraints:
Languages for manual translations and so forth.
F-Key | Name | Type | Description |
---|---|---|---|
code | character varying(6) | PRIMARY KEY | |
description | text |
Tables referencing this one via Foreign Key Constraints:
This table stores addresses, such as shipto and bill to addresses.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
line_one | text | NOT NULL | |
line_two | text | ||
line_three | text | ||
city | text | NOT NULL | |
state | text | ||
lsmb14.country.id | country_id | integer | NOT NULL |
mail_code | text | NOT NULL | |
created | date | NOT NULL DEFAULT now() | |
inactive_date | timestamp without time zone | ||
active | boolean | NOT NULL DEFAULT true |
Name | Constraint |
---|---|
location_city_check | CHECK ((city ~ '[[:alnum:]_]'::text)) |
location_line_one_check | CHECK ((line_one ~ '[[:alnum:]_]'::text)) |
location_mail_code_check | CHECK ((mail_code ~ '[[:alnum:]_]'::text)) |
location_state_check | CHECK ((state ~ '[[:alnum:]_]'::text)) |
Tables referencing this one via Foreign Key Constraints:
Individuals seeking to add new location classes should coordinate with others.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | UNIQUE NOT NULL | |
class | text | PRIMARY KEY | |
authoritative | boolean | PRIMARY KEY |
Name | Constraint |
---|---|
location_class_class_check | CHECK ((class ~ '[[:alnum:]_]'::text)) |
Tables referencing this one via Foreign Key Constraints:
F-Key | Name | Type | Description |
---|---|---|---|
role_name | text | PRIMARY KEY |
Tables referencing this one via Foreign Key Constraints:
F-Key | Name | Type | Description |
---|---|---|---|
lsmb14.lsmb_group.role_name | group_name | text | PRIMARY KEY |
granted_role | text | PRIMARY KEY |
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.
F-Key | Name | Type | Description |
---|---|---|---|
id | integer | UNIQUE NOT NULL | |
label | text | PRIMARY KEY |
Tables referencing this one via Foreign Key Constraints:
Tracks role assignments in the front end. Not sure why we need this. Will rethink for 1.4.
F-Key | Name | Type | Description |
---|---|---|---|
lsmb14.users.id | user_id | integer | NOT NULL |
role | text | NOT NULL |
A single parts entry can have multiple make/model entries. These store manufacturer/model number info.
F-Key | Name | Type | Description |
---|---|---|---|
parts_id | integer | PRIMARY KEY | |
barcode | text | ||
make | text | PRIMARY KEY | |
model | text | PRIMARY KEY |
Provides access control list entries for menu nodes.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | NOT NULL | |
role_name | character varying | PRIMARY KEY | |
acl_type | character varying |
Nodes are hidden unless a role is found of which the user is a member, and where the acl_type for that role type and node is set to 'allow' and no acl is found for any role of which the user is a member, where the acl_type is set to 'deny'. |
|
lsmb14.menu_node.id | node_id | integer | PRIMARY KEY |
Name | Constraint |
---|---|
menu_acl_acl_type_check | CHECK ((((acl_type)::text = 'allow'::text) OR ((acl_type)::text = 'deny'::text))) |
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.
F-Key | Name | Type | Description |
---|---|---|---|
lsmb14.menu_node.id | node_id | integer | PRIMARY KEY |
attribute | character varying | PRIMARY KEY | |
value | character varying | NOT NULL | |
id | serial | NOT NULL |
A nice human-readable view for investigating the menu tree. Does not show menu attributes or acls.
F-Key | Name | Type | Description |
---|---|---|---|
level | integer | ||
path | text | ||
label | text | ||
id | integer | ||
position | integer |
WITH RECURSIVE tree (path , id , parent , level , positions ) AS ( SELECT (menu_node.id)::text AS path , menu_node.id , menu_node.parent , 0 AS level , (menu_node."position")::text AS "position" FROM lsmb14.menu_node WHERE (menu_node.parent IS NULL) UNIONSELECT ( (t.path || ','::text ) || (n.id)::text ) , n.id , n.parent , (t.level + 1) , ( (t.positions || ','::text ) || n."position" ) FROM (lsmb14.menu_node n JOIN tree t ON ( (t.id = n.parent) ) ) ) SELECT t.level , t.path , (repeat (' '::text , (2 * t.level) ) || (n.label)::text ) AS label , n.id , n."position" FROM (tree t JOIN lsmb14.menu_node n USING (id) ) ORDER BY (string_to_array (t.positions ,','::text ) )::integer[];
This table stores the tree structure of the menu.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
label | character varying | NOT NULL | |
lsmb14.menu_node.id | parent | integer | UNIQUE#1 |
position | integer | UNIQUE#1 NOT NULL |
Tables referencing this one via Foreign Key Constraints:
This is a lookup table for storing MIME types.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | UNIQUE NOT NULL | |
mime_type | text | PRIMARY KEY | |
invoice_include | boolean | DEFAULT false |
Tables referencing this one via Foreign Key Constraints:
Tracks ship_to information for orders and invoices.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
lsmb14.journal_entry.id | trans_id | integer | |
lsmb14.oe.id | oe_id | integer | |
lsmb14.location.id | location_id | integer |
This is an abstract table which should have zero rows. It is inherited by other tables for specific notes.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
lsmb14.note_class.id | note_class | integer | NOT NULL |
note | text |
NOT NULL
Body of note. |
|
vector | tsvector |
NOT NULL
DEFAULT ''::tsvector
tsvector for full text indexing, requires both setting up tsearch dictionaries and adding triggers to use at present. |
|
created | timestamp without time zone | NOT NULL DEFAULT now() | |
created_by | text | DEFAULT "session_user"() | |
ref_key | integer |
NOT NULL
Subclassed tables use this column as a foreign key against the table storing the record a note is attached to. |
|
subject | text |
Coordinate with others before adding entries.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
class | text | NOT NULL |
Name | Constraint |
---|---|
note_class_class_check | CHECK ((class ~ '[[:alnum:]_]'::text)) |
Tables referencing this one via Foreign Key Constraints:
Header information for: * Sales orders * Purchase Orders * Quotations * Requests for Quotation
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
ordnumber | text | ||
transdate | date | DEFAULT ('now'::text)::date | |
lsmb14.entity.id | entity_id | integer | |
amount | numeric | ||
netamount | numeric | ||
reqdate | date | ||
taxincluded | boolean | ||
shippingpoint | text | ||
notes | text | ||
curr | character(3) | ||
lsmb14.person.id | person_id | integer | |
closed | boolean | DEFAULT false | |
quotation | boolean | DEFAULT false | |
quonumber | text | ||
intnotes | text | ||
shipvia | text | ||
language_code | character varying(6) | ||
ponumber | text | ||
terms | smallint | ||
lsmb14.entity_credit_account.id | entity_credit_account | integer | NOT NULL |
lsmb14.oe_class.id | oe_class_id | integer | NOT NULL |
Tables referencing this one via Foreign Key Constraints:
oe_id_key id oe_ordnumber_key ordnumber oe_transdate_key transdateHardwired classifications for orders and quotations. Coordinate before adding.
F-Key | Name | Type | Description |
---|---|---|---|
id | smallint | UNIQUE | |
oe_class | text | PRIMARY KEY |
Name | Constraint |
---|---|
oe_class_id_check | CHECK ((id = ANY (ARRAY[1, 2, 3, 4]))) |
Tables referencing this one via Foreign Key Constraints:
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.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
lsmb14.session.session_id | session_id | integer |
Line items for sales/purchase orders and quotations.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
trans_id | integer | ||
parts_id | integer | ||
description | text | ||
qty | numeric | ||
sellprice | numeric | ||
precision | integer | ||
discount | numeric | ||
unit | character varying(5) | ||
reqdate | date | ||
ship | numeric | ||
serialnumber | text | ||
notes | text |
Tables referencing this one via Foreign Key Constraints:
orderitems_id_key id orderitems_trans_id_key trans_idF-Key | Name | Type | Description |
---|---|---|---|
payment_id | integer | ||
payment_reference | text | ||
payment_class | integer | ||
payment_closed | boolean | ||
payment_date | date | ||
chart_id | integer | ||
accno | text | ||
chart_description | text | ||
available | numeric | ||
legal_name | text | ||
entity_credit_id | integer | ||
entity_id | integer | ||
discount | numeric | ||
meta_number | character varying(32) |
SELECT p.id AS payment_id , p.reference AS payment_reference , p.payment_class , p.closed AS payment_closed , p.payment_date , ac.chart_id , c.accno , c.description AS chart_description , abs (sum (ac.amount) ) AS available , cmp.legal_name , eca.id AS entity_credit_id , eca.entity_id , eca.discount , eca.meta_number FROM ( ( ( ( (lsmb14.payment p JOIN lsmb14.payment_links pl ON ( (pl.payment_id = p.id) ) ) JOIN lsmb14.acc_trans ac ON ( (ac.entry_id = pl.entry_id) ) ) JOIN lsmb14.chart c ON ( (c.id = ac.chart_id) ) ) JOIN lsmb14.entity_credit_account eca ON ( (eca.id = p.entity_credit_id) ) ) JOIN lsmb14.company cmp ON ( (cmp.entity_id = eca.entity_id) ) ) WHERE ( ( (p.gl_id IS NOT NULL) AND ( (pl.type = 2) OR (pl.type = 0) ) ) AND (c.link ~~ '%overpayment%'::text) ) GROUP BY p.id , c.accno , p.reference , p.payment_class , p.closed , p.payment_date , ac.chart_id , c.description , cmp.legal_name , eca.id , eca.entity_id , eca.discount , eca.meta_number;
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.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
partnumber | text | ||
description | text | ||
unit | character varying(5) | ||
listprice | numeric | ||
sellprice | numeric | ||
lastcost | numeric | ||
priceupdate | date | DEFAULT ('now'::text)::date | |
weight | numeric | ||
onhand | numeric | ||
notes | text | ||
makemodel | boolean | DEFAULT false | |
assembly | boolean | DEFAULT false | |
alternate | boolean | DEFAULT false | |
rop | numeric |
Re-order point. Used to select parts for short inventory report. |
|
lsmb14.account.id | inventory_accno_id | integer | |
lsmb14.account.id | income_accno_id | integer | |
lsmb14.account.id | expense_accno_id | integer | |
lsmb14.account.id | returns_accno_id | integer | |
bin | text |
Text identifier for where a part is stored. |
|
obsolete | boolean | DEFAULT false | |
bom | boolean |
DEFAULT false
Show on Bill of Materials. |
|
image | text |
Hyperlink to product image. |
|
drawing | text | ||
microfiche | text | ||
partsgroup_id | integer | ||
avgcost | numeric |
Tables referencing this one via Foreign Key Constraints:
parts_description_key lower(description) parts_id_key id parts_partnumber_key lower(partnumber)Translation information for parts.
F-Key | Name | Type | Description |
---|---|---|---|
lsmb14.parts.id | trans_id | integer | PRIMARY KEY |
language_code | character varying(6) | PRIMARY KEY | |
description | text |
Table lsmb14.parts_translation Inherits translation,
Tracks per-customer pricing. Discounts can be offered for periods of time and for pricegroups as well as per customer
F-Key | Name | Type | Description |
---|---|---|---|
parts_id | integer | ||
lsmb14.entity_credit_account.id | credit_id | integer | |
lsmb14.pricegroup.id | pricegroup_id | integer | |
pricebreak | numeric | ||
sellprice | numeric | ||
validfrom | date | ||
validto | date | ||
curr | character(3) | ||
entry_id | serial | PRIMARY KEY |
Groups of parts for Point of Sale screen.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
partsgroup | text | ||
lsmb14.partsgroup.id | parent | integer |
Tables referencing this one via Foreign Key Constraints:
partsgroup_id_key idTranslation information for partsgroups.
F-Key | Name | Type | Description |
---|---|---|---|
lsmb14.partsgroup.id | trans_id | integer | PRIMARY KEY |
language_code | character varying(6) | PRIMARY KEY | |
description | text |
Table lsmb14.partsgroup_translation Inherits translation,
Mapping of parts to taxes.
F-Key | Name | Type | Description |
---|---|---|---|
lsmb14.parts.id | parts_id | integer | PRIMARY KEY |
lsmb14.account.id | chart_id | integer | PRIMARY KEY |
lsmb14.taxcategory.taxcategory_id | taxcategory_id | integer |
Tracks vendor's pricing, as well as vendor's part number, lead time required and currency.
F-Key | Name | Type | Description |
---|---|---|---|
lsmb14.entity_credit_account.id | credit_id | integer | NOT NULL |
parts_id | integer | ||
partnumber | text | ||
leadtime | smallint | ||
lastcost | numeric | ||
curr | character(3) | ||
entry_id | serial | PRIMARY KEY |
This table will store the main data on a payment, prepayment, overpayment, et
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
reference | text |
NOT NULL
This field will store the code for both receipts and payment order |
|
lsmb14.gl.id | gl_id | integer |
A payment should always be linked to a GL movement |
payment_class | integer | NOT NULL | |
payment_date | date | DEFAULT ('now'::text)::date | |
closed | boolean |
DEFAULT false
This will store the current state of a payment/receipt order |
|
lsmb14.entity_credit_account.id | entity_credit_id | integer | |
lsmb14.person.id | employee_id | integer | |
currency | character(3) | ||
notes | text |
Tables referencing this one via Foreign Key Constraints:
payment_id_idx idAn 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.
F-Key | Name | Type | Description |
---|---|---|---|
lsmb14.payment.id | payment_id | integer | |
lsmb14.acc_trans.entry_id | entry_id | integer | |
type | integer |
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.
F-Key | Name | Type | Description |
---|---|---|---|
lsmb14.journal_line.id | line_id | integer | PRIMARY KEY |
lsmb14.eca_invoice.journal_id | pays | integer | NOT NULL |
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | UNIQUE NOT NULL | |
label | text | PRIMARY KEY |
F-Key | Name | Type | Description |
---|---|---|---|
entry_id | serial | UNIQUE NOT NULL | |
lsmb14.entity.id | entity_id | integer | PRIMARY KEY |
lsmb14.payroll_deduction_type.id | type_id | integer | PRIMARY KEY |
rate | numeric | NOT NULL |
F-Key | Name | Type | Description |
---|---|---|---|
id | integer | UNIQUE#1 NOT NULL | |
lsmb14.country.id | country_id | integer | UNIQUE#1 PRIMARY KEY |
label | text | PRIMARY KEY |
Tables referencing this one via Foreign Key Constraints:
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | UNIQUE NOT NULL | |
lsmb14.account.id | account_id | integer | NOT NULL |
lsmb14.payroll_deduction_class.id#1 | pdc_id | integer | NOT NULL |
lsmb14.payroll_deduction_class.country_id#1 | country_id | integer | NOT NULL |
label | text | NOT NULL | |
unit | text | NOT NULL | |
default_amount | numeric | ||
calc_percent | boolean | NOT NULL |
Tables referencing this one via Foreign Key Constraints:
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | UNIQUE NOT NULL | |
label | text | PRIMARY KEY |
Tables referencing this one via Foreign Key Constraints:
F-Key | Name | Type | Description |
---|---|---|---|
lsmb14.payroll_employee_class.id | ec_id | integer | PRIMARY KEY |
lsmb14.payroll_income_type.id | it_id | integer | PRIMARY KEY |
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | UNIQUE NOT NULL | |
label | text |
F-Key | Name | Type | Description |
---|---|---|---|
id | integer | UNIQUE#1 NOT NULL | |
lsmb14.country.id | country_id | integer | UNIQUE#1 PRIMARY KEY |
label | text | PRIMARY KEY |
Tables referencing this one via Foreign Key Constraints:
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | UNIQUE NOT NULL | |
lsmb14.account.id | account_id | integer | NOT NULL |
lsmb14.payroll_income_class.id#1 | pic_id | integer | NOT NULL |
lsmb14.payroll_income_class.country_id#1 | country_id | integer | NOT NULL |
label | text | NOT NULL | |
unit | text | NOT NULL | |
default_amount | numeric |
Tables referencing this one via Foreign Key Constraints:
F-Key | Name | Type | Description |
---|---|---|---|
lsmb14.entity.id | employee_id | integer | NOT NULL |
lsmb14.payroll_pto_class.id | pto_class_id | integer | NOT NULL |
lsmb14.payroll_report.id | report_id | integer | NOT NULL |
amount | numeric | NOT NULL |
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | UNIQUE NOT NULL | |
label | text | PRIMARY KEY |
Tables referencing this one via Foreign Key Constraints:
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
lsmb14.payroll_employee_class.id | ec_id | integer | NOT NULL |
payment_date | date | NOT NULL | |
lsmb14.entity_employee.entity_id | created_by | integer | |
lsmb14.entity_employee.entity_id | approved_by | integer |
Tables referencing this one via Foreign Key Constraints:
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | UNIQUE NOT NULL | |
lsmb14.payroll_report.id | report_id | integer | PRIMARY KEY |
lsmb14.entity.id | employee_id | integer | PRIMARY KEY |
lsmb14.payroll_income_type.id | it_id | integer | PRIMARY KEY |
qty | numeric | NOT NULL | |
rate | numeric | NOT NULL | |
description | text |
F-Key | Name | Type | Description |
---|---|---|---|
entry_id | serial | UNIQUE NOT NULL | |
lsmb14.entity.id | entity_id | integer | PRIMARY KEY |
lsmb14.payroll_income_type.id | type_id | integer | PRIMARY KEY |
rate | numeric | NOT NULL |
F-Key | Name | Type | Description |
---|---|---|---|
id | text | ||
label | text | ||
date_to | date | ||
date_from | date |
SELECT'ytd'::text AS id ,'Year to Date'::text AS label , (now () )::date AS date_to , ( ( (date_part ('year'::text , now () ) )::text || '-01-01'::text ) )::date AS date_from UNIONSELECT'last_year'::text AS id ,'Last Year'::text AS label , ( ( ( (date_part ('YEAR'::text , now () ) - (1)::double precision ) )::text || '-12-31'::text ) )::date AS date_to , ( ( ( (date_part ('YEAR'::text , now () ) - (1)::double precision ) )::text || '-01-01'::text ) )::date AS date_from;
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.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
lsmb14.entity.id | entity_id | integer | UNIQUE NOT NULL |
lsmb14.salutation.id | salutation_id | integer | |
first_name | text | NOT NULL | |
middle_name | text | ||
last_name | text | NOT NULL | |
created | date | NOT NULL DEFAULT ('now'::text)::date |
Name | Constraint |
---|---|
person_first_name_check | CHECK ((first_name ~ '[[:alnum:]_]'::text)) |
person_last_name_check | CHECK ((last_name ~ '[[:alnum:]_]'::text)) |
Tables referencing this one via Foreign Key Constraints:
currently unused in the front-end, but can be used to map persons to companies.
F-Key | Name | Type | Description |
---|---|---|---|
lsmb14.location.id | location_id | integer | PRIMARY KEY |
lsmb14.person.id | person_id | integer | PRIMARY KEY |
lsmb14.company.id | company_id | integer | NOT NULL |
Pricegroups are groups of customers who are assigned prices and discounts together.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
pricegroup | text |
Tables referencing this one via Foreign Key Constraints:
pricegroup_id_key id pricegroup_pricegroup_key pricegroupF-Key | Name | Type | Description |
---|---|---|---|
payee | text | ||
id | bigint | ||
report_id | integer | ||
scn | text | ||
their_balance | numeric | ||
our_balance | numeric | ||
errorcode | integer | ||
user | integer | ||
clear_time | date | ||
insert_time | timestamp with time zone | ||
trans_type | text | ||
post_date | date | ||
ledger_id | integer | ||
voucher_id | integer | ||
overlook | boolean | ||
cleared | boolean |
SELECT n.name AS payee , rr.id , rr.report_id , rr.scn , rr.their_balance , rr.our_balance , rr.errorcode , rr."user" , rr.clear_time , rr.insert_time , rr.trans_type , rr.post_date , rr.ledger_id , ac.voucher_id , rr.overlook , rr.cleared FROM ( ( (lsmb14.cr_report_line rr LEFT JOIN lsmb14.acc_trans ac ON ( (rr.ledger_id = ac.entry_id) ) ) LEFT JOIN lsmb14.gl ON ( (ac.trans_id = gl.id) ) ) LEFT JOIN ( ( SELECT ap.id , e.name FROM ( (lsmb14.ap JOIN lsmb14.entity_credit_account eca ON ( (ap.entity_credit_account = eca.id) ) ) JOIN lsmb14.entity e ON ( (eca.entity_id = e.id) ) ) UNIONSELECT ar.id , e.name FROM ( (lsmb14.ar JOIN lsmb14.entity_credit_account eca ON ( (ar.entity_credit_account = eca.id) ) ) JOIN lsmb14.entity e ON ( (eca.entity_id = e.id) ) ) ) UNIONSELECT gl.id , gl.description FROM lsmb14.gl ) n ON ( (n.id = ac.trans_id) ) );
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.
F-Key | Name | Type | Description |
---|---|---|---|
id | integer | PRIMARY KEY DEFAULT nextval('id'::regclass) | |
reference | text | ||
startdate | date | ||
nextdate | date | ||
enddate | date | ||
repeat | smallint | ||
unit | character varying(6) | ||
howmany | integer | ||
payment | boolean | DEFAULT false |
Email to be sent out when recurring transaction is posted.
F-Key | Name | Type | Description |
---|---|---|---|
id | integer | PRIMARY KEY | |
formname | text | PRIMARY KEY | |
format | text | ||
message | text |
Template, printer etc. to print to when recurring transaction posts.
F-Key | Name | Type | Description |
---|---|---|---|
id | integer | PRIMARY KEY | |
formname | text | PRIMARY KEY | |
format | text | ||
printer | text |
F-Key | Name | Type | Description |
---|---|---|---|
roleid | oid | ||
member | oid | ||
grantor | oid | ||
admin_option | boolean | ||
rolname | name | ||
rolsuper | boolean | ||
rolinherit | boolean | ||
rolcreaterole | boolean | ||
rolcreatedb | boolean | ||
rolcatupdate | boolean | ||
rolcanlogin | boolean | ||
rolreplication | boolean | ||
rolconnlimit | integer | ||
rolpassword | text | ||
rolvaliduntil | timestamp with time zone | ||
rolconfig | text[] | ||
oid | oid |
SELECT m.roleid , m.member , m.grantor , m.admin_option , a.rolname , a.rolsuper , a.rolinherit , a.rolcreaterole , a.rolcreatedb , a.rolcatupdate , a.rolcanlogin , a.rolreplication , a.rolconnlimit , a.rolpassword , a.rolvaliduntil , a.rolconfig , a.oid FROM (pg_auth_members m JOIN pg_roles a ON ( (m.roleid = a.oid) ) );
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | UNIQUE NOT NULL | |
salutation | text | PRIMARY KEY |
Tables referencing this one via Foreign Key Constraints:
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.
F-Key | Name | Type | Description |
---|---|---|---|
session_id | serial | PRIMARY KEY | |
token | character varying(32) | ||
last_used | timestamp without time zone | DEFAULT now() | |
ttl | integer | NOT NULL DEFAULT 3600 | |
lsmb14.users.id | users_id | integer | NOT NULL |
notify_pasword | interval | NOT NULL DEFAULT '7 days'::interval |
Name | Constraint |
---|---|
session_token_check | CHECK ((length((token)::text) = 32)) |
Tables referencing this one via Foreign Key Constraints:
This can be used SIC codes or any equivalent, such as ISIC, NAICS, etc.
F-Key | Name | Type | Description |
---|---|---|---|
code | character varying(6) | PRIMARY KEY | |
sictype | character(1) | ||
description | text |
Whether AR/AP transactions and invoices have been emailed and/or printed
F-Key | Name | Type | Description |
---|---|---|---|
trans_id | integer | PRIMARY KEY | |
formname | text | PRIMARY KEY | |
printed | boolean | DEFAULT false | |
emailed | boolean | DEFAULT false | |
spoolfile | text |
Information on tax rates.
F-Key | Name | Type | Description |
---|---|---|---|
lsmb14.account.id lsmb14.account.id | chart_id | integer | PRIMARY KEY |
rate | numeric | ||
minvalue | numeric | ||
maxvalue | numeric | ||
taxnumber | text | ||
validto | timestamp without time zone | PRIMARY KEY DEFAULT 'infinity'::timestamp without time zone | |
pass | integer |
NOT NULL
This is an integer indicating the pass of the tax. This is to support cumultative sales tax rules (for example, Quebec charging taxes on the federal taxes collected). |
|
lsmb14.taxmodule.taxmodule_id | taxmodule_id | integer | NOT NULL DEFAULT 1 |
This stores extended information for manual tax calculations.
F-Key | Name | Type | Description |
---|---|---|---|
tax_basis | numeric | ||
rate | numeric | ||
lsmb14.journal_line.id | entry_id | integer | PRIMARY KEY |
F-Key | Name | Type | Description |
---|---|---|---|
taxcategory_id | serial | PRIMARY KEY | |
taxcategoryname | text | NOT NULL | |
lsmb14.taxmodule.taxmodule_id | taxmodule_id | integer | NOT NULL |
Tables referencing this one via Foreign Key Constraints:
This is used to store information on tax modules. the module name is used to determine the Perl class for the taxes.
F-Key | Name | Type | Description |
---|---|---|---|
taxmodule_id | serial | PRIMARY KEY | |
taxmodulename | text | NOT NULL |
Tables referencing this one via Foreign Key Constraints:
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.
F-Key | Name | Type | Description |
---|---|---|---|
id | integer | PRIMARY KEY | |
table_name | text | ||
lsmb14.session.session_id | locked_by | integer |
This should only be used in pessimistic locking measures as required by large batch work flows. |
approved | boolean | ||
lsmb14.entity.id | approved_by | integer | |
approved_at | timestamp without time zone |
Tables referencing this one via Foreign Key Constraints:
transactions_locked_by_i locked_byabstract table for manual translation data. Should have zero rows.
F-Key | Name | Type | Description |
---|---|---|---|
trans_id | integer | PRIMARY KEY | |
language_code | character varying(6) | PRIMARY KEY | |
description | text |
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
date_from | date | ||
date_to | date | ||
description | text | NOT NULL | |
lsmb14.trial_balance__yearend_types.type | yearend | text | NOT NULL |
Tables referencing this one via Foreign Key Constraints:
F-Key | Name | Type | Description |
---|---|---|---|
lsmb14.trial_balance.id | report_id | integer | NOT NULL |
lsmb14.account.id | account_id | integer | NOT NULL |
F-Key | Name | Type | Description |
---|---|---|---|
lsmb14.trial_balance.id | report_id | integer | NOT NULL |
lsmb14.account_heading.id | heading_id | integer | NOT NULL |
F-Key | Name | Type | Description |
---|---|---|---|
type | text | PRIMARY KEY |
Tables referencing this one via Foreign Key Constraints:
This view provides join and approval information for transactions.
F-Key | Name | Type | Description |
---|---|---|---|
id | integer | ||
reference | text | ||
entity_credit_account | integer | ||
table | text | ||
approved | boolean |
( SELECT gl.id , gl.reference , NULL::integer AS entity_credit_account ,'gl'::text AS "table" , gl.approved FROM lsmb14.gl UNION ALLSELECT ap.id , ap.invnumber AS reference , ap.entity_credit_account ,'ap'::text AS "table" , ap.approved FROM lsmb14.ap ) UNION ALLSELECT ar.id , ar.invnumber AS reference , ar.entity_credit_account ,'ar'::text AS "table" , ar.approved FROM lsmb14.ar;
F-Key | Name | Type | Description |
---|---|---|---|
id | integer | ||
username | character varying(30) | ||
created | date |
SELECT u.id , u.username , e.created FROM (lsmb14.entity e JOIN lsmb14.users u ON ( (u.entity_id = e.id) ) );
This table sets the basic preferences for formats, languages, printers, and user-selected stylesheets.
F-Key | Name | Type | Description |
---|---|---|---|
lsmb14.users.id | id | integer | PRIMARY KEY |
lsmb14.language.code | language | character varying(6) | |
stylesheet | text | NOT NULL DEFAULT 'ledgersmb.css'::text | |
printer | text | ||
dateformat | text | NOT NULL DEFAULT 'yyyy-mm-dd'::text | |
numberformat | text | NOT NULL DEFAULT '1000.00'::text |
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | UNIQUE NOT NULL | |
username | character varying(30) | PRIMARY KEY | |
notify_password | interval | NOT NULL DEFAULT '7 days'::interval | |
lsmb14.entity.id | entity_id | integer | NOT NULL |
Tables referencing this one via Foreign Key Constraints:
Mapping transactions to batches for batch approval.
F-Key | Name | Type | Description |
---|---|---|---|
lsmb14.transactions.id | trans_id | integer | NOT NULL |
lsmb14.batch.id | batch_id | integer | NOT NULL |
id | serial |
PRIMARY KEY
This is simply a surrogate key for easy reference. |
|
lsmb14.batch_class.id | batch_class | integer |
NOT NULL
This is the authoritative class of the voucher. |
Tables referencing this one via Foreign Key Constraints:
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
description | text |
Tables referencing this one via Foreign Key Constraints:
An extension to the journal_entry table to track transactionsactions which close out the books at yearend.
F-Key | Name | Type | Description |
---|---|---|---|
lsmb14.gl.id | trans_id | integer | PRIMARY KEY |
reversed | boolean | DEFAULT false | |
transdate | date |
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;
This deletes an account with the id specified. If the account has transactions associated with it, it will fail and raise a foreign key constraint.
BEGIN DELETE FROM account_link WHERE account_id = in_id; DELETE FROM account WHERE id = in_id; RETURN FOUND; END;
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);
Returns the account where the accno field matches (excatly) the in_accno provided.
select * from account where accno = $1;
Returns set of accounts where the tax attribute is true.
SELECT * FROM account WHERE tax is true ORDER BY accno;
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;
SELECT * FROM account ORDER BY heading;
Returns the account balance at a given point in time, calculating forward from most recent check point.
DECLARE balance numeric; BEGIN SELECT coalesce(sum(ac.amount) + cp.amount, sum(ac.amount)) INTO balance FROM acc_trans ac JOIN (select id, approved from ar union select id, approved from ap union select id, approved from gl) a ON (a.id = ac.trans_id) LEFT JOIN (select account_id, end_date, amount from account_checkpoint WHERE account_id = in_account_id AND end_date < in_transdate ORDER BY end_date desc limit 1 ) cp ON (cp.account_id = ac.chart_id) WHERE ac.chart_id = in_account_id AND ac.transdate > coalesce(cp.end_date, ac.transdate - '1 day'::interval) and ac.approved and a.approved and ac.transdate <= in_transdate GROUP BY cp.amount, ac.chart_id; RETURN balance; END;
This deletes existing account_link entries, where the account_link.description is not designated as a custom one in the account_link_description table. If no account heading is provided, the account heading which has an accno field closest to but prior (by collation order) is used. Then it saves the account information, and rebuilds the account_link records based on the in_link array.
DECLARE t_heading_id int; t_link record; t_id int; t_tax bool; BEGIN SELECT count(*) > 0 INTO t_tax FROM tax WHERE in_id = chart_id; t_tax := t_tax OR in_tax; -- check to ensure summary accounts are exclusive -- necessary for proper handling by legacy code FOR t_link IN SELECT description FROM account_link_description WHERE summary='t' LOOP IF t_link.description = ANY (in_link) and array_upper(in_link, 1) > 1 THEN RAISE EXCEPTION 'Invalid link settings: Summary'; END IF; END LOOP; -- heading settings IF in_heading IS NULL THEN SELECT id INTO t_heading_id FROM account_heading WHERE accno < in_accno order by accno desc limit 1; ELSE t_heading_id := in_heading; END IF; -- don't remove custom links. DELETE FROM account_link WHERE account_id = in_id and description in ( select description from account_link_description where custom = 'f'); UPDATE account SET accno = in_accno, description = in_description, category = in_category, gifi_accno = in_gifi_accno, heading = t_heading_id, contra = in_contra, obsolete = in_obsolete, tax = t_tax, is_temp = in_is_temp WHERE id = in_id; IF FOUND THEN t_id := in_id; ELSE -- can't obsolete on insert, but this can be changed if users -- request it --CT INSERT INTO account (accno, description, category, gifi_accno, heading, contra, tax, is_temp) VALUES (in_accno, in_description, in_category, in_gifi_accno, t_heading_id, in_contra, in_tax, in_is_temp); t_id := currval('account_id_seq'); END IF; FOR t_link IN select in_link[generate_series] AS val FROM generate_series(array_lower(in_link, 1), array_upper(in_link, 1)) LOOP INSERT INTO account_link (account_id, description) VALUES (t_id, t_link.val); END LOOP; RETURN t_id; END;
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;
Returns an entry from the chart view which matches the id requested, and which is an account, not a heading.
SELECT * from chart where id = $1 and charttype = 'A';
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;
Returns a list of all account headings, currently ordered by account number.
SELECT * FROM account_heading order by accno;
Returns an entry from the chart view which matches the id requested, and which is a heading, not an account.
DECLARE account chart%ROWTYPE; BEGIN SELECT * INTO account FROM chart WHERE id = in_id AND charttype = 'H'; RETURN account; END;
Lists all existing account headings.
SELECT * FROM account_heading order by accno;
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;
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;
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;
This function inserts the arguments into lsmb_group_grants for future reference and issues the db-level grant. It then returns true if there are no exceptions.
BEGIN PERFORM * FROM lsmb_group_grants WHERE group_name = in_group_name AND granted_role = in_role_name; IF NOT FOUND THEN INSERT INTO lsmb_group_grants(group_name, granted_role) VALUES (in_group_name, in_role_name); END IF; EXECUTE 'GRANT ' || quote_ident(in_role_name) || ' TO ' || quote_literal('lsmb_' || t_dbname || '__' || in_group_name); RETURN TRUE; END;
declare stmt TEXT; a_role name; a_user name; BEGIN -- Issue the grant select rolname into a_role from pg_roles where rolname = in_role; IF NOT FOUND THEN RAISE EXCEPTION 'Cannot grant permissions of a non-existant role.'; END IF; select rolname into a_user from pg_roles where rolname = in_username; IF NOT FOUND THEN RAISE EXCEPTION 'Cannot grant permissions to a non-existant user.'; END IF; stmt := 'GRANT '|| quote_ident(in_role) ||' to '|| quote_ident(in_username); EXECUTE stmt; insert into lsmb_roles (user_id, role) SELECT id, in_role from users where username = in_username; return 1; END;
DECLARE stmt text; t_dbname text; BEGIN t_dbname := current_database(); stmt := 'create role '|| quote_ident('lsmb_' || t_dbname || '__' || in_group_name); execute stmt; INSERT INTO lsmb_group (role_name) values (quote_literal('lsmb_' || t_dbname || '__' || in_group_name)); return 1; END;
Deletes the input group from the database. Not designed to be used to remove a login-capable user.
DECLARE stmt text; a_role role_view; t_dbname text; BEGIN t_dbname := current_database(); select * into a_role from role_view where rolname = in_group_name; if not found then return 'f'::bool; else stmt := 'drop role lsmb_' || quote_ident(t_dbname || '__' || in_group_name); execute stmt; return 't'::bool; end if; END;
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;
Drops the session identified, releasing all locks held.
BEGIN DELETE FROM "session" WHERE session_id = in_session_id; RETURN FOUND; END;
DECLARE v_rol record; t_dbname text; BEGIN t_dbname := current_database(); FOR v_rol in SELECT * from pg_roles where rolname ~ ('^lsmb_' || t_dbname || '__') and rolcanlogin is false order by rolname ASC LOOP RETURN NEXT v_rol; END LOOP; END;
Returns a set of roles that a user is a part of.
declare u_role record; a_user users; begin select * into a_user from admin__get_user(in_user_id); FOR u_role IN select r.rolname from pg_roles r, (select m.roleid from pg_auth_members m, pg_roles b where m.member = b.oid and b.rolname = a_user.username ) as ar where r.oid = ar.roleid LOOP RETURN NEXT u_role.rolname::text; END LOOP; RETURN; end;
Returns a set of (only one) user specified by the id.
DECLARE a_user users; BEGIN select * into a_user from users where entity_id = in_entity_id; return next a_user; return; END;
-- 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;
Returns true if user is set up in LedgerSMB. False otherwise.
BEGIN PERFORM * from users where username = in_user; RETURN found; END;
SELECT * FROM lsmb_group_grants WHERE group_name = $1 ORDER BY granted_role;
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;
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;
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;
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;
Creates a user and relevant records in LedgerSMB and PostgreSQL.
DECLARE a_user users; v_user_id int; p_id int; l_id int; stmt text; t_is_role bool; t_is_user bool; BEGIN -- WARNING TO PROGRAMMERS: This function runs as the definer and runs -- utility statements via EXECUTE. -- PLEASE BE VERY CAREFUL ABOUT SQL-INJECTION INSIDE THIS FUNCTION. PERFORM rolname FROM pg_roles WHERE rolname = in_username; t_is_role := found; t_is_user := admin__is_user(in_username); IF t_is_role is true and t_is_user is false and in_import is false THEN RAISE EXCEPTION 'Duplicate user'; END IF; if t_is_role and in_password is not null then execute 'ALTER USER ' || quote_ident( in_username ) || ' WITH ENCRYPTED PASSWORD ' || quote_literal (in_password) || $e$ valid until $e$ || quote_literal(now() + '1 day'::interval); elsif in_import is false AND t_is_user is false AND in_password IS NULL THEN RAISE EXCEPTION 'No password'; elsif t_is_role is false and in_import IS FALSE THEN -- create an actual user execute 'CREATE USER ' || quote_ident( in_username ) || ' WITH ENCRYPTED PASSWORD ' || quote_literal (in_password) || $e$ valid until $e$ || quote_literal(now() + '1 day'::interval); END IF; select * into a_user from users lu where lu.id = in_id; IF FOUND THEN return a_user.id; ELSE -- Insert cycle --- The entity is expected to already BE created. See admin.pm. v_user_id := nextval('users_id_seq'); insert into users (id, username, entity_id) VALUES ( v_user_id, in_username, in_entity_id ); insert into user_preference (id) values (v_user_id); IF NOT exists(SELECT * FROM entity_employee WHERE entity_id = in_entity_id) THEN INSERT into entity_employee (entity_id) values (in_entity_id); END IF; -- Finally, issue the create user statement return v_user_id ; END IF; END;
Returns a list of users matching search criteria. Nulls match all values. only username is not an exact match.
DECLARE t_return_row user_result; BEGIN FOR t_return_row IN SELECT u.id, u.username, p.first_name, p.last_name, e.ssn, e.dob FROM users u JOIN person p ON (u.entity_id = p.entity_id) JOIN entity_employee e ON (e.entity_id = p.entity_id) WHERE u.username LIKE '%' || coalesce(in_username,'') || '%' AND (p.first_name = in_first_name or in_first_name is null) AND (p.last_name = in_last_name or in_last_name is null) AND (in_ssn is NULL or in_ssn = e.ssn) AND (e.dob = in_dob::date or in_dob is NULL) LOOP RETURN NEXT t_return_row; END LOOP; END;
A basic array aggregate to take elements and return a one-dimensional array. Example: SELECT as_array(id) from entity_class;
aggregate_dummy
Retrieves a given asset either by id or tag. Both are complete matches. Note that the behavior is undefined if both id and tag are provided.
DECLARE ret_val asset_item; BEGIN SELECT * into ret_val from asset_item WHERE id = in_id OR in_tag = tag ORDER BY id desc limit 1; return ret_val; END;
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;
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;
Searches for assets. Nulls match all records. Asset class is exact, as is purchase date, purchase value, and salvage value. Tag and description are partial matches.
DECLARE out_val asset_item; BEGIN FOR out_val IN SELECT * FROM asset_item WHERE (in_asset_class is null or asset_class_id = in_asset_class) AND (in_description is null or description LIKE '%' || in_description || '%') and (in_tag is not null or tag like '%'||in_tag||'%') AND (in_purchase_date is null or purchase_date = in_purchase_date) AND (in_purchase_value is null or in_purchase_value = purchase_value) AND (in_usable_life is null or in_usable_life = usable_life) AND (in_salvage_value is null OR in_salvage_value = salvage_value) LOOP RETURN NEXT out_val; END LOOP; END;
returns the row from asset_class identified by in_id.
DECLARE ret_val asset_class; BEGIN SELECT * INTO ret_val FROM asset_class WHERE id = in_id; RETURN ret_val; END;
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;
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;
Returns the depreciation method associated with the asset class.
SELECT * from asset_dep_method WHERE id = (select method from asset_class where id = $1);
Returns a set of asset_dep_methods ordered by the method label.
SELECT * FROM asset_dep_method ORDER BY method;
Returns an alphabetical list of asset classes.
SELECT * FROM asset_class ORDER BY label;
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;
Returns a list of matching asset classes. The account id's are exact matches as is the method, but the label is a partial match. NULL's match all.
DECLARE out_var asset_class_result; BEGIN FOR out_var IN SELECT ac.id, ac.asset_account_id, aa.accno, aa.description, ad.accno, ad.description, m.method, ac.method, ac.label FROM asset_class ac JOIN account aa ON (aa.id = ac.asset_account_id) JOIN account ad ON (ad.id = ac.dep_account_id) JOIN asset_dep_method m ON (ac.method = m.id) WHERE (in_asset_account_id is null or in_asset_account_id = ac.asset_account_id) AND (in_dep_account_id is null OR in_dep_account_id = ac.dep_account_id) AND (in_method is null OR in_method = ac.method) AND (in_label IS NULL OR ac.label LIKE '%' || in_label || '%') ORDER BY label LOOP RETURN NEXT out_var; END LOOP; END;
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;
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;
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;
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;
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;
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;
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;
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;
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');
Returns a list of matching asset items. Nulls match all records. Tag and description allow for partial match. All other matches are exact.
DECLARE retval asset_item; BEGIN FOR retval IN SELECT * FROM asset_item WHERE (id = in_id or in_id is null) and (asset_class_id = in_asset_class or in_asset_class is null) and (description like '%'||in_description||'%' or in_description is null) and (tag like '%' || in_tag || '%' or in_tag is null) and (purchase_value = in_purchase_value or in_purchase_value is null) and (in_purchase_date = purchase_date or in_purchase_date is null) and (start_depreciation = in_start_depreciation or in_start_depreciation is null) and (in_warehouse_id = location_id OR in_warehouse_id is null) and (department_id = in_department_id or in_department_id is null) and (in_invoice_id = invoice_id OR in_invoice_id IS NULL) and (asset_account_id = in_asset_account_id or in_asset_account_id is null) and (dep_account_id = in_dep_account_id or in_dep_account_id is null) LOOP return next retval; end loop; END;
Returns the current net book value report.
SELECT ai.id, ai.tag, ai.description, coalesce(ai.start_depreciation, ai.purchase_date), adm.short_name, ai.usable_life - months_passed(coalesce(ai.start_depreciation, ai.purchase_date), coalesce(max(r.report_date), ai.start_depreciation, ai.purchase_date))/ 12, ai.purchase_value - ai.salvage_value, ai.salvage_value, max(r.report_date), sum(rl.amount), ai.purchase_value - sum(rl.amount) FROM asset_item ai JOIN asset_class ac ON (ai.asset_class_id = ac.id) JOIN asset_dep_method adm ON (adm.id = ac.method) LEFT JOIN asset_report_line rl ON (ai.id = rl.asset_id) LEFT JOIN asset_report r on (rl.report_id = r.id) WHERE r.id IS NULL OR r.approved_at IS NOT NULL GROUP BY ai.id, ai.tag, ai.description, ai.start_depreciation, ai.purchase_date, adm.short_name, ai.usable_life, ai.purchase_value, salvage_value HAVING (NOT 2 = ANY(as_array(r.report_class))) AND (NOT 4 = ANY(as_array(r.report_class))) OR max(r.report_class) IS NULL ORDER BY ai.id, ai.tag, ai.description;
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;
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;
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');
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;
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;
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;
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;
Returns the asset_report line identified by id.
select * from asset_report where id = $1;
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;
Returns a list of asset_disposal_method items ordered by label.
SELECT * FROM asset_disposal_method order by label;
Lists all asset expense reports.
SELECT * FROM account__get_by_link_desc('asset_expense');
Returns a list of gain accounts for asset depreciation and disposal reports.
SELECT * FROM account__get_by_link_desc('asset_gain');
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;
Returns a list of loss accounts for asset depreciation and disposal reports.
SELECT * FROM account__get_by_link_desc('asset_loss');
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;
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;
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;
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;
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;
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;
Inserts the batch into the table.
BEGIN INSERT INTO batch (batch_class_id, default_date, description, control_code, created_by) VALUES ((SELECT id FROM batch_class WHERE class = in_batch_class), in_batch_date, in_description, in_batch_number, (select entity_id FROM users WHERE username = session_user)); return currval('batch_id_seq'); END;
If the batch is found and unapproved, deletes it and returns 1. Otherwise raises an exception.
DECLARE t_transaction_ids int[]; BEGIN -- Adjust AR/AP tables for payment and payment reversal vouchers -- voucher_id is only set in acc_trans on payment/receipt vouchers and -- their reversals. -CT perform * from batch where id = in_batch_id and approved_on IS NULL; IF NOT FOUND THEN RAISE EXCEPTION 'Batch not found'; END IF; update ar set paid = amount + (select sum(amount) from acc_trans join chart ON (acc_trans.chart_id = chart.id) where link = 'AR' AND trans_id = ar.id AND (voucher_id IS NULL OR voucher_id NOT IN (select id from voucher WHERE batch_id = in_batch_id))) where id in (select trans_id from acc_trans where voucher_id IN (select id from voucher where batch_id = in_batch_id)); update ap set paid = amount - (select sum(amount) from acc_trans join chart ON (acc_trans.chart_id = chart.id) where link = 'AP' AND trans_id = ap.id AND (voucher_id IS NULL OR voucher_id NOT IN (select id from voucher WHERE batch_id = in_batch_id))) where id in (select trans_id from acc_trans where voucher_id IN (select id from voucher where batch_id = in_batch_id)); DELETE FROM ac_tax_form WHERE entry_id IN (select entry_id from acc_trans where voucher_id in (select id from voucher where batch_id = in_batch_id) ); DELETE FROM acc_trans WHERE voucher_id IN (select id FROM voucher where batch_id = in_batch_id); -- The rest of this function involves the deletion of actual -- transactions, vouchers, and batches, and jobs which are in progress. -- -CT SELECT as_array(trans_id) INTO t_transaction_ids FROM voucher WHERE batch_id = in_batch_id AND batch_class IN (1, 2, 5); DELETE FROM ac_tax_form WHERE entry_id in (select entry_id from acc_trans where trans_id = any(t_transaction_ids)); DELETE FROM acc_trans WHERE trans_id = ANY(t_transaction_ids); DELETE FROM ap WHERE id = ANY(t_transaction_ids); DELETE FROM gl WHERE id = ANY(t_transaction_ids); DELETE FROM voucher WHERE batch_id = in_batch_id; DELETE FROM batch WHERE id = in_batch_id; DELETE FROM transactions WHERE id = ANY(t_transaction_ids); RETURN 1; END;
returns the batch class id associated with the in_type label provided.
SELECT id FROM batch_class WHERE class = $1;
Returns a sim[ple set of user objects. This should be renamed so that it is more obvious it is a general purpose function.
DECLARE out_record users%ROWTYPE; BEGIN FOR out_record IN SELECT * from users WHERE entity_id IN (select created_by from batch) LOOP RETURN NEXT out_record; END LOOP; END;
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;
Posts the specified batch to the books. Only posted batches should show up on standard financial reports.
BEGIN UPDATE ar SET approved = true WHERE id IN (select trans_id FROM voucher WHERE batch_id = in_batch_id AND batch_class = 2); UPDATE ap SET approved = true WHERE id IN (select trans_id FROM voucher WHERE batch_id = in_batch_id AND batch_class = 1); UPDATE gl SET approved = true WHERE id IN (select trans_id FROM voucher WHERE batch_id = in_batch_id AND batch_class = 5); UPDATE acc_trans SET approved = true WHERE voucher_id IN (select id FROM voucher WHERE batch_id = in_batch_id AND batch_class IN (3, 4, 6, 7)); UPDATE batch SET approved_on = now(), approved_by = (select entity_id FROM users WHERE username = SESSION_USER) WHERE id = in_batch_id; RETURN now()::date; END;
Returns a list of batches and amounts processed on the batch. Nulls match all values. in_date_from and in_date_to specify date ranges. in_description is a partial match. All other criteria are exact matches.
DECLARE out_value batch_list_item; BEGIN FOR out_value IN SELECT b.id, c.class, b.control_code, b.description, u.username, b.created_on, b.default_date, sum( CASE WHEN vc.id = 5 AND al.amount < 0 -- GL THEN al.amount WHEN vc.id = 1 THEN ap.amount WHEN vc.id = 2 THEN ap.amount ELSE 0 END) AS transaction_total, sum( CASE WHEN alc.link = 'AR' AND vc.id IN (6, 7) THEN al.amount WHEN alc.link = 'AP' AND vc.id IN (3, 4) THEN al.amount * -1 ELSE 0 END ) AS payment_total FROM batch b JOIN batch_class c ON (b.batch_class_id = c.id) LEFT JOIN users u ON (u.entity_id = b.created_by) LEFT JOIN voucher v ON (v.batch_id = b.id) LEFT JOIN batch_class vc ON (v.batch_class = vc.id) LEFT JOIN ar ON (vc.id = 2 AND v.trans_id = ar.id) LEFT JOIN ap ON (vc.id = 1 AND v.trans_id = ap.id) LEFT JOIN acc_trans al ON ((vc.id = 5 AND v.trans_id = al.trans_id) OR (vc.id IN (3, 4, 6, 7) AND al.voucher_id = v.id)) LEFT JOIN chart alc ON (al.chart_id = alc.id) WHERE (c.id = in_class_id OR in_class_id IS NULL) AND (b.description LIKE '%' || in_description || '%' OR in_description IS NULL) AND (in_created_by_eid = b.created_by OR in_created_by_eid IS NULL) AND ((in_approved = false OR in_approved IS NULL AND approved_on IS NULL) OR (in_approved = true AND approved_on IS NOT NULL) ) and (in_date_from IS NULL or b.default_date >= in_date_from) and (in_date_to IS NULL or b.default_date <= in_date_to) GROUP BY b.id, c.class, b.description, u.username, b.created_on, b.control_code, b.default_date HAVING (in_amount_gt IS NULL OR sum(coalesce(ar.amount - ar.paid, ap.amount - ap.paid, al.amount)) >= in_amount_gt) AND (in_amount_lt IS NULL OR sum(coalesce(ar.amount - ar.paid, ap.amount - ap.paid, al.amount)) <= in_amount_lt) ORDER BY b.control_code, b.description LOOP RETURN NEXT out_value; END LOOP; END;
This is a full search for the batches, listing them by amount processed. in_amount_gt and in_amount_lt provide a range to search for. in_description is a partial match field. Other fields are exact matches. NULLs match all values.
DECLARE out_value batch_list_item; BEGIN FOR out_value IN SELECT b.id, c.class, b.control_code, b.description, u.username, b.created_on, b.default_date, 0, 0 FROM batch b JOIN batch_class c ON (b.batch_class_id = c.id) JOIN users u ON (u.entity_id = b.created_by) LEFT JOIN voucher v ON (v.batch_id = b.id) where v.id is null and(u.entity_id = in_created_by_eid or in_created_by_eid is null) and (in_description is null or b.description like '%' || in_description || '%') and (in_class_id is null or c.id = in_class_id) GROUP BY b.id, c.class, b.description, u.username, b.created_on, b.control_code, b.default_date ORDER BY b.control_code, b.description LOOP RETURN NEXT out_value; END LOOP; END;
This performs a simple search of open batches created by the entity_id in question. This is used to pull up batches that were currently used so that they can be picked up and more vouchers added. NULLs match all values. in_description is a partial match All other inouts are exact matches.
DECLARE out_value batch_list_item; BEGIN FOR out_value IN SELECT b.id, c.class, b.control_code, b.description, u.username, b.created_on, b.default_date, NULL FROM batch b JOIN batch_class c ON (b.batch_class_id = c.id) LEFT JOIN users u ON (u.entity_id = b.created_by) WHERE (c.id = in_class_id OR in_class_id IS NULL) AND (b.description LIKE '%' || in_description || '%' OR in_description IS NULL) AND (in_created_by_eid = b.created_by OR in_created_by_eid IS NULL) AND ((in_approved = false OR in_approved IS NULL AND approved_on IS NULL) OR (in_approved = true AND approved_on IS NOT NULL) ) GROUP BY b.id, c.class, b.description, u.username, b.created_on, b.control_code, b.default_date LOOP RETURN NEXT out_value; END LOOP; END;
UPDATE budget_info set approved_at = now(), approved_by = person__get_my_entity_id() WHERE id = $1; SELECT budget__get_info($1);
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;
This retrieves the budget lines associated with a budget.
SELECT * FROM budget_line where budget_id = $1;
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;
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;
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)
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;
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;
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;
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);
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;
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;
Returns a list of all business types. Ordered by description by default.
DECLARE out_row business%ROWTYPE; BEGIN FOR out_row IN SELECT * FROM business ORDER BY description LOOP RETURN NEXT out_row; END LOOP; END;
SELECT * FROM business_unit where id = $1;
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;
This function retUrns a list of all units (projects, departments, funds, etc) active on the in_active_on date, where in_credit_id matches the credit id of the customer or vendor requested, and where in_business_uni_class_id is the class id of the class of business units (1 for department, 2 for project, etc). With the exception of in_business_unit_class_id, the null matches all records.
BEGIN RETURN QUERY SELECT * FROM business_unit WHERE (in_active_on BETWEEN coalesce(start_date, in_active_on) AND coalesce(end_date, in_active_on) OR in_active_on IS NULL) AND (in_credit_id = credit_id OR (credit_id IS NULL and in_strict_credit IS NOT TRUE) OR (in_credit_id IS NULL)) AND class_id = in_business_unit_class_id ORDER BY control_code; END;
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;
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;
SELECT * FROM lsmb_module WHERE id IN (select module_id from bu_class_to_module where bu_class_id = $1) ORDER BY id;
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;
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;
SELECT * FROM business_unit WHERE id = $1;
This function returns the cash account acording with in_account_class which must be 1 or 2. If in_account_class is 1 then it returns a list of AP accounts, and if in_account_class is 2, then a list of AR accounts.
DECLARE out_row chart%ROWTYPE; BEGIN IF in_account_class NOT IN (1, 2) THEN RAISE EXCEPTION 'Bad Account Type'; END IF; FOR out_row IN SELECT * FROM chart WHERE link = CASE WHEN in_account_class = 1 THEN 'AP' WHEN in_account_class = 2 THEN 'AR' END ORDER BY accno LOOP RETURN NEXT out_row; END LOOP; END;
Generates a list of chart view entries.
DECLARE out_row chart%ROWTYPE; BEGIN FOR out_row IN SELECT * FROM chart ORDER BY accno LOOP RETURN next out_row; END LOOP; END;
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;
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;
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;
This returns a list of account entries where the description or account number begins with in_search. If in_link_desc is provided, the list is further filtered by which accounts are set to an account_link.description equal to that provided.
DECLARE out_row account%ROWTYPE; BEGIN FOR out_row IN SELECT * FROM account WHERE (accno ~* ('^'||in_search) OR description ~* ('^'||in_search)) AND (in_link_desc IS NULL or id in (select account_id from account_link where description = in_link_desc)) AND not obsolete ORDER BY accno LOOP RETURN next out_row; END LOOP; END;
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;
DECLARE t_alloc numeric := 0; t_cogs numeric := 0; t_inv invoice; t_cp account_checkpoint; t_ar ar; t_avail numeric; BEGIN IF in_qty > 0 THEN return cogs__reverse_ap(in_parts_id, in_qty * -1) * in_lastcost; END IF; SELECT * INTO t_cp FROM account_checkpoint ORDER BY end_date DESC LIMIT 1; FOR t_inv IN SELECT i.* FROM invoice i JOIN ar a ON a.id = i.trans_id WHERE qty + allocated > 0 and parts_id = in_parts_id ORDER BY a.transdate, a.id, i.id LOOP t_avail := t_inv.qty + t_inv.allocated; SELECT * INTO t_ar FROM ar WHERE id = t_inv.trans_id; IF t_alloc < in_qty THEN RAISE EXCEPTION 'TOO MANY ALLOCATED'; ELSIF t_alloc = in_qty THEN return t_alloc; ELSIF (in_qty + t_alloc) * -1 <= t_avail THEN UPDATE invoice SET allocated = allocated + (in_qty + t_alloc) WHERE id = t_inv.id; INSERT INTO acc_trans (chart_id, transdate, amount, invoice_id, approved, trans_id) SELECT expense_accno_id, CASE WHEN t_ar.transdate > t_cp.end_date THEN t_ar.transdate ELSE t_cp.end_date + '1 day'::interval END, (in_qty + t_alloc) * in_lastcost, t_inv.id, true, t_inv.trans_id FROM parts WHERE id = t_inv.parts_id AND inventory_accno_id IS NOT NULL AND expense_accno_id IS NOT NULL UNION SELECT income_accno_id, CASE WHEN t_ar.transdate > t_cp.end_date THEN t_ar.transdate ELSE t_cp.end_date + '1 day'::interval END, -1 * (in_qty + t_alloc) * in_lastcost, t_inv.id, true, t_inv.trans_id FROM parts WHERE id = t_inv.parts_id AND inventory_accno_id IS NOT NULL AND expense_accno_id IS NOT NULL; t_cogs := t_cogs + (in_qty + t_alloc) * in_lastcost; return in_qty * -1; ELSE UPDATE invoice SET allocated = qty * -1 WHERE id = t_inv.id; t_cogs := t_cogs + t_avail * in_lastcost; INSERT INTO acc_trans (chart_id, transdate, amount, invoice_id, approved, trans_id) SELECT expense_accno_id, CASE WHEN t_ar.transdate > t_cp.end_date THEN t_ar.transdate ELSE t_cp.end_date + '1 day'::interval END, -1 * t_avail * in_lastcost, t_inv.id, true, t_inv.trans_id FROM parts WHERE id = t_inv.parts_id AND inventory_accno_id IS NOT NULL AND expense_accno_id IS NOT NULL UNION SELECT income_accno_id, CASE WHEN t_ar.transdate > t_cp.end_date THEN t_ar.transdate ELSE t_cp.end_date + '1 day'::interval END, -t_avail * in_lastcost, t_inv.id, true, t_inv.trans_id FROM parts WHERE id = t_inv.parts_id AND inventory_accno_id IS NOT NULL AND expense_accno_id IS NOT NULL; t_alloc := t_alloc + t_avail; t_cogs := t_cogs + t_avail * in_lastcost; END IF; END LOOP; RETURN t_alloc; END;
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;
This function accepts a parts_id and a quantity, and iterates through AP records in order, calculating COGS on a FIFO basis and returning it to the application to attach to the current transaction. Return values are an array of {allocated, cogs}.
DECLARE t_alloc numeric := 0; t_cogs numeric := 0; t_inv invoice; t_avail numeric; BEGIN FOR t_inv IN SELECT i.* FROM invoice i JOIN (select id, approved, transdate from ap union select id, approved, transdate from gl) a ON a.id = i.trans_id WHERE qty + allocated < 0 AND i.parts_id = in_parts_id ORDER BY a.transdate asc, a.id asc, i.id asc LOOP t_avail := (t_inv.qty + t_inv.allocated) * -1; IF t_alloc > in_qty THEN RAISE EXCEPTION 'TOO MANY ALLOCATED'; ELSIF t_alloc = in_qty THEN return ARRAY[t_alloc, t_cogs]; ELSIF (in_qty + t_alloc) <= t_avail THEN UPDATE invoice SET allocated = allocated + (in_qty - t_alloc) WHERE id = t_inv.id; t_cogs := t_cogs + (in_qty - t_alloc) * t_inv.sellprice; t_alloc := in_qty; return ARRAY[t_alloc, t_cogs]; ELSE UPDATE invoice SET allocated = qty * -1 WHERE id = t_inv.id; t_cogs := t_cogs + (t_avail * t_inv.sellprice); t_alloc := t_alloc + t_avail; END IF; END LOOP; RETURN ARRAY[t_alloc, t_cogs]; END;
DECLARE t_cogs numeric[]; t_inv invoice; t_part parts; t_ar ar; t_transdate date; BEGIN SELECT * INTO t_inv FROM invoice WHERE id = in_invoice_id; SELECT * INTO t_part FROM parts WHERE id = t_inv.parts_id; SELECT * INTO t_ar FROM ar WHERE id = t_inv.trans_id; IF t_inv.qty + t_inv.allocated = 0 THEN return 0; END IF; IF t_inv.qty > 0 THEN t_cogs := cogs__add_for_ar(t_inv.parts_id, t_inv.qty + t_inv.allocated); ELSE t_cogs := cogs__reverse_ar(t_inv.parts_id, t_inv.qty + t_inv.allocated); END IF; UPDATE invoice set allocated = allocated - t_cogs[1] WHERE id = in_invoice_id; SELECT CASE WHEN t_ar.transdate > max(end_date) THEN t_ar.transdate ELSE max(end_date) + '1 day'::interval END INTO t_transdate from account_checkpoint td; INSERT INTO acc_trans (trans_id, chart_id, approved, amount, transdate, invoice_id) VALUES (t_inv.trans_id, CASE WHEN t_inv.qty < 0 AND t_ar.is_return THEN t_part.returns_accno_id ELSE t_part.expense_accno_id END, TRUE, t_cogs[2] * -1, t_transdate, t_inv.id), (t_inv.trans_id, t_part.inventory_accno_id, TRUE, t_cogs[2], t_transdate, t_inv.id); RETURN t_cogs[1]; END;
This function iterates through invoice rows attached to ap transactions and allocates them on a first-in first-out basis. The sort of pseudo-"COGS" value is returned to the application for further handling.
DECLARE t_alloc numeric :=0; t_inv invoice; t_cogs numeric :=0; retval numeric[]; BEGIN RAISE NOTICE 'reversing AP: parts_id %, qty %', in_parts_id, in_qty; FOR t_inv IN SELECT i.* FROM invoice i JOIN ap a ON a.id = i.trans_id WHERE qty + allocated < 0 AND parts_id = in_parts_id ORDER BY a.transdate, a.id, i.id LOOP RAISE NOTICE 'id %, avail %, allocated %, requesting %', t_inv.id, t_inv.qty + t_inv.allocated, t_alloc, in_qty - t_alloc; IF t_alloc > in_qty THEN RAISE EXCEPTION 'TOO MANY ALLOCATED'; ELSIF t_alloc = in_qty THEN return ARRAY[t_alloc, t_cogs]; ELSIF (in_qty - t_alloc) <= -1 * (t_inv.qty + t_inv.allocated) THEN raise notice 'partial reversal'; UPDATE invoice SET allocated = allocated + (in_qty - t_alloc) WHERE id = t_inv.id; return ARRAY[in_qty * -1, t_cogs + (in_qty - t_alloc) * t_inv.sellprice]; ELSE raise notice 'total reversal'; UPDATE invoice SET allocated = qty * -1 WHERE id = t_inv.id; t_alloc := t_alloc - (t_inv.qty + t_inv.allocated); t_cogs := t_cogs - (t_inv.qty + t_inv.allocated) * t_inv.sellprice; END IF; END LOOP; RETURN ARRAY[t_alloc, t_cogs]; RAISE EXCEPTION 'TOO FEW TO ALLOCATE'; END;
This function accepts a part id and quantity to reverse. It then iterates backwards over AP related records, calculating COGS. This does not save COGS but rather returns it to the application to save. Return values are an array of {allocated, cogs}.
DECLARE t_alloc numeric := 0; t_cogs numeric := 0; t_inv invoice; t_avail numeric; BEGIN FOR t_inv IN SELECT i.* FROM invoice i JOIN (select id, approved, transdate from ap union select id, approved, transdate from gl) a ON a.id = i.trans_id WHERE allocated > 0 and a.approved and parts_id = in_parts_id ORDER BY a.transdate DESC, a.id DESC, i.id DESC LOOP t_avail := t_inv.allocated; IF t_alloc < in_qty THEN RAISE EXCEPTION 'TOO MANY ALLOCATED'; ELSIF t_alloc = in_qty THEN RETURN ARRAY[t_alloc, t_cogs]; ELSIF (in_qty - t_alloc) * -1 <= t_inv.allocated THEN raise notice 'partial reversal'; UPDATE invoice SET allocated = allocated + (in_qty - t_alloc) WHERE id = t_inv.id; t_cogs := t_cogs + (in_qty - t_alloc) * t_inv.sellprice; return ARRAY[t_alloc + (in_qty - t_alloc), t_cogs]; ELSE raise notice 'full reversal'; UPDATE invoice SET allocated = 0 WHERE id = t_inv.id; t_alloc := t_alloc + t_inv.allocated * -1; t_cogs := t_cogs + -1 * (t_inv.allocated) * t_inv.sellprice; END IF; END LOOP; RAISE EXCEPTION 'TOO FEW TO REVERSE'; END;
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;
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;
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;
select nextval('company_id_seq');
Saves a company. Returns the id number of the record stored.
DECLARE t_entity_id INT; t_company_id INT; t_control_code TEXT; t_retval COMPANY; BEGIN t_company_id := in_id; IF in_control_code IS NULL THEN t_control_code := setting_increment('company_control'); ELSE t_control_code := in_control_code; END IF; UPDATE entity SET name = in_legal_name, entity_class = in_entity_class, control_code = in_control_code WHERE id = in_entity_id; IF FOUND THEN t_entity_id = in_entity_id; ELSE INSERT INTO entity (name, entity_class, control_code,country_id) VALUES (in_legal_name, in_entity_class, t_control_code,in_country_id); t_entity_id := currval('entity_id_seq'); END IF; UPDATE company SET legal_name = in_legal_name, tax_id = in_tax_id, sic_code = in_sic_code, sales_tax_id = in_sales_tax_id, license_number = in_license_number WHERE id = t_company_id; IF NOT FOUND THEN INSERT INTO company(entity_id, legal_name, tax_id, sic_code, sales_tax_id, license_number) VALUES (t_entity_id, in_legal_name, in_tax_id, in_sic_code, in_sales_tax_id, in_license_number); END IF; SELECT * INTO t_retval FROM company WHERE entity_id = t_entity_id; RETURN t_retval; END;
Returns billing information (billing name and address) for a given credit account.
DECLARE out_var company_billing_info; t_id INT; BEGIN select coalesce(eca.pay_to_name, c.legal_name), eca.meta_number, e.control_code, c.tax_id, a.line_one, a.line_two, a.line_three, a.city, a.state, a.mail_code, cc.name into out_var FROM (select legal_name, tax_id, entity_id FROM company UNION ALL SELECT last_name || ', ' || first_name, null, entity_id FROM person) c JOIN entity e ON (c.entity_id = e.id) JOIN entity_credit_account eca ON (eca.entity_id = e.id) LEFT JOIN eca_to_location cl ON (eca.id = cl.credit_id) LEFT JOIN location a ON (a.id = cl.location_id) LEFT JOIN country cc ON (cc.id = a.country_id) WHERE eca.id = in_id AND (location_class = 1 or location_class is null); RETURN out_var; END;
Returns an n dimensional array. Example: SELECT as_array(ARRAY[id::text, class]) from contact_class
aggregate_dummy
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
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;
DECLARE out_row contact_search_result; loop_count int; t_contact_info text[]; BEGIN t_contact_info = in_contact_info; FOR out_row IN SELECT e.id, e.control_code, ec.id, ec.meta_number, ec.description, ec.entity_class, c.legal_name, c.sic_code, b.description , ec.curr::text FROM (select * from entity where control_code like in_control_code || '%' union select * from entity where in_control_code is null) e JOIN (SELECT legal_name, sic_code, entity_id FROM company WHERE legal_name @@ plainto_tsquery(in_name_part) UNION ALL SELECT legal_name, sic_code, entity_id FROM company WHERE in_name_part IS NULL UNION ALL SELECT coalesce(first_name, '') || ' ' || coalesce(middle_name, '') || ' ' || coalesce(last_name, ''), null, entity_id FROM person WHERE coalesce(first_name, '') || ' ' || coalesce(middle_name, '') || ' ' || coalesce(last_name, '') @@ plainto_tsquery(in_name_part) UNION ALL SELECT coalesce(first_name, '') || ' ' || coalesce(middle_name, '') || ' ' || coalesce(last_name, ''), null, entity_id FROM person WHERE in_name_part IS NULL) c ON (e.id = c.entity_id) LEFT JOIN entity_credit_account ec ON (ec.entity_id = e.id) LEFT JOIN business b ON (ec.business_id = b.id) WHERE coalesce(ec.entity_class,e.entity_class) = in_entity_class AND (c.entity_id IN (select entity_id FROM entity_to_contact WHERE contact ILIKE ANY(t_contact_info)) OR '' ILIKE ALL(t_contact_info) OR t_contact_info IS NULL) AND ((in_address IS NULL AND in_city IS NULL AND in_state IS NULL AND in_country IS NULL) OR (c.entity_id IN (select entity_id FROM entity_to_location WHERE location_id IN (SELECT id FROM location WHERE (line_one @@ plainto_tsquery( in_address) OR line_two @@ plainto_tsquery( in_address) OR line_three @@ plainto_tsquery( in_address)) AND city ILIKE '%' || coalesce(in_city, '') || '%' AND state ILIKE '%' || coalesce(in_state, '') || '%' AND mail_code ILIKE '%' || coalesce(in_mail_code, '') || '%' AND country_id IN (SELECT id FROM country WHERE name ilike in_country OR short_name ilike in_country))))) AND (ec.business_id = coalesce(in_business_id, ec.business_id) OR (ec.business_id IS NULL AND in_business_id IS NULL)) AND (ec.startdate <= coalesce(in_active_date_to, ec.startdate) OR (ec.startdate IS NULL)) AND (ec.enddate >= coalesce(in_active_date_from, ec.enddate) OR (ec.enddate IS NULL)) AND (ec.meta_number like in_meta_number || '%' OR in_meta_number IS NULL) AND (in_notes IS NULL OR e.id in ( SELECT entity_id from entity_note WHERE note @@ plainto_tsquery(in_notes)) OR ec.id IN (select ref_key FROM eca_note WHERE note @@ plainto_tsquery(in_notes))) LOOP RETURN NEXT out_row; END LOOP; END;
Returns a list of contact classes ordered by ID.
DECLARE out_row RECORD; BEGIN FOR out_row IN SELECT * FROM contact_class ORDER BY id LOOP RETURN NEXT out_row; END LOOP; END;
Provides default rules for setting reconciliation labels. Currently saves a label of accno ||'--' || description.
DECLARE v_chart_id int; BEGIN -- Check for existence of the account already PERFORM * FROM cr_coa_to_account WHERE account = in_accno; IF NOT FOUND THEN -- This is a new account. Insert the relevant data. SELECT id INTO v_chart_id FROM chart WHERE accno = in_accno; INSERT INTO cr_coa_to_account (chart_id, account) VALUES (v_chart_id, in_accno||'--'||in_description); END IF; -- Already found, no need to do anything. =) END;
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;
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;
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;
This function return each year inside transdate in transactions. Currently it uses a sparse index scan because the number of rows returned is very small and the table can be very large.
DECLARE next_record int; BEGIN SELECT MIN(EXTRACT ('YEAR' FROM transdate))::INT INTO next_record FROM acc_trans; LOOP EXIT WHEN next_record IS NULL; RETURN NEXT next_record; SELECT MIN(EXTRACT ('YEAR' FROM transdate))::INT AS YEAR INTO next_record FROM acc_trans WHERE EXTRACT ('YEAR' FROM transdate) > next_record; END LOOP; END;
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;
This function return the default currency asigned by the program.
DECLARE defaultcurrency defaults.value%TYPE; BEGIN SELECT INTO defaultcurrency substr(value,1,3) FROM defaults WHERE setting_key = 'curr'; RETURN NEXT defaultcurrency; END;
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;
begin delete from yearend where trans_id = old.id; return NULL; end;
Searches for drafts. in_type may be any of 'ar', 'ap', or 'gl'.
DECLARE out_row RECORD; BEGIN FOR out_row IN SELECT trans.id, trans.transdate, trans.invoice, trans.reference, trans.description, sum(case when lower(in_type) = 'ap' AND chart.link = 'AP' THEN line.amount WHEN lower(in_type) = 'ar' AND chart.link = 'AR' THEN line.amount * -1 WHEN lower(in_type) = 'gl' AND line.amount > 0 THEN line.amount ELSE 0 END) as amount FROM ( SELECT id, transdate, reference, description, false as invoice, approved from gl WHERE lower(in_type) = 'gl' UNION SELECT id, transdate, invnumber as reference, (SELECT name FROM eca__get_entity(entity_credit_account)), invoice, approved from ap WHERE lower(in_type) = 'ap' UNION SELECT id, transdate, invnumber as reference, description, invoice, approved from ar WHERE lower(in_type) = 'ar' ) trans JOIN acc_trans line ON (trans.id = line.trans_id) JOIN chart ON (line.chart_id = chart.id and charttype = 'A') LEFT JOIN voucher v ON (v.trans_id = trans.id) WHERE (in_from_date IS NULL or trans.transdate >= in_from_date) AND (in_to_date IS NULL or trans.transdate <= in_to_date) AND trans.approved IS FALSE AND v.id IS NULL GROUP BY trans.id, trans.transdate, trans.description, trans.reference, trans.invoice HAVING (in_with_accno IS NULL or in_with_accno = ANY(as_array(chart.accno))) ORDER BY trans.reference LOOP RETURN NEXT out_row; END LOOP; END;
Deletes the draft from the book. Only will delete unapproved transactions. Otherwise an exception is raised and the transaction terminated.
declare t_table text; begin SELECT table_name into t_table FROM transactions where id = in_id; IF (t_table = 'ar') THEN PERFORM cogs__add_for_ar_line(id) FROM invoice WHERE trans_id = in_id; UPDATE ar set approved = true where id = in_id; ELSIF (t_table = 'ap') THEN PERFORM cogs__add_for_ap_line(id) FROM invoice WHERE trans_id = in_id; UPDATE ap set approved = true where id = in_id; ELSIF (t_table = 'gl') THEN UPDATE gl set approved = true where id = in_id; ELSE raise exception 'Invalid table % in draft_approve for transaction %', t_table, in_id; END IF; IF NOT FOUND THEN RETURN FALSE; END IF; UPDATE transactions SET approved_by = (select entity_id FROM users WHERE username = SESSION_USER), approved_at = now() WHERE id = in_id; RETURN TRUE; END;
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;
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;
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;
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;
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;
SELECT * FROM entity_credit_account WHERE entity_class = $2 AND meta_number = $1;
Returns a set of (only one) entity to which the entity credit account is attached.
declare v_row entity; BEGIN SELECT entity.* INTO v_row FROM entity_credit_account JOIN entity ON entity_credit_account.entity_id = entity.id WHERE entity_credit_account.id = in_credit_id; IF NOT FOUND THEN raise exception 'Could not find entity with ID %', in_credit_id; ELSE return next v_row; END IF; END;
This returns the pricematrix for the customer or vendor (entity_credit_account identified by in_id), orderd by partnumber, validfrom
SELECT pc.parts_id, p.partnumber, p.description, pc.credit_id, pc.pricebreak, pc.sellprice, NULL, NULL::int, NULL, pc.validfrom, pc.validto, pc.curr, pc.entry_id FROM partscustomer pc JOIN parts p on pc.parts_id = p.id JOIN entity_credit_account eca ON pc.credit_id = eca.id WHERE pc.credit_id = $1 AND eca.entity_class = 2 UNION SELECT pv.parts_id, p.partnumber, p.description, pv.credit_id, NULL, NULL, pv.lastcost, pv.leadtime::int, pv.partnumber, NULL, NULL, pv.curr, pv.entry_id FROM partsvendor pv JOIN parts p on pv.parts_id = p.id JOIN entity_credit_account eca ON pv.credit_id = eca.id WHERE pv.credit_id = $1 and eca.entity_class = 1 ORDER BY partnumber, validfrom
SELECT pc.parts_id, p.partnumber, p.description, pc.credit_id, pc.pricebreak, pc.sellprice, NULL::numeric, NULL::int, NULL::text, pc.validfrom, pc.validto, pc.curr, pc.entry_id FROM partscustomer pc JOIN parts p on pc.parts_id = p.id JOIN entity_credit_account eca ON pc.pricegroup_id = eca.pricegroup_id WHERE eca.id = $1 AND eca.entity_class = 2
Returns a set of taxable account id's.
select * from eca_tax where eca_id = $1;
This produces a history detail report, i.e. a list of all products purchased by a customer over a specific date range. meta_number is an exact match, as are in_open and inc_closed. All other fields allow for partial matches. NULL matches all values.
SELECT eca.id, e.name, eca.meta_number, a.id as invoice_id, a.invnumber, a.curr::text, p.id AS parts_id, p.partnumber, i.description, i.qty, i.unit::text, i.sellprice, i.discount, i.deliverydate, null::int as project_id, null::text as projectnumber, i.serialnumber, case when $16 = 1 then ex.buy else ex.sell end as exchange_rate, ee.id as salesperson_id, ep.last_name || ', ' || ep.first_name as salesperson_name FROM (select * from entity_credit_account where meta_number = $2 UNION select * from entity_credit_account WHERE $2 is null ) eca -- broken into unions for performance join entity e on eca.entity_id = e.id JOIN (select invnumber, curr, transdate, entity_credit_account, id, person_id, notes FROM ar where $16 = 2 and $13 = 'i' and (($17 and amount = paid) or ($18 and amount <> paid)) UNION select invnumber, curr, transdate, entity_credit_account, id, person_id, notes FROM ap where $16 = 1 and $13 = 'i' and (($17 and amount = paid) or ($18 and amount <> paid)) union select ordnumber, curr, transdate, entity_credit_account, id, person_id, notes from oe where ($16= 1 and oe.oe_class_id = 2 and $13 = 'o' and quotation is not true) and (($17 and not closed) or ($18 and closed)) union select ordnumber, curr, transdate, entity_credit_account, id, person_id, notes from oe where ($16= 2 and oe.oe_class_id = 1 and $13 = 'o' and quotation is not true) and (($17 and not closed) or ($18 and closed)) union select quonumber, curr, transdate, entity_credit_account, id, person_id, notes from oe where($16= 1 and oe.oe_class_id = 4 and $13 = 'q' and quotation is true) and (($17 and not closed) or ($18 and closed)) union select quonumber, curr, transdate, entity_credit_account, id, person_id, notes from oe where($16= 2 and oe.oe_class_id = 4 and $13 = 'q' and quotation is true) and (($17 and not closed) or ($18 and closed)) ) a ON (a.entity_credit_account = eca.id) -- broken into unions -- for performance JOIN ( select id, trans_id, parts_id, qty, description, unit, discount, deliverydate, serialnumber, sellprice FROM invoice where $13 = 'i' union select id, trans_id, parts_id, qty, description, unit, discount, reqdate, serialnumber, sellprice FROM orderitems where $13 <> 'i' ) i on i.trans_id = a.id JOIN parts p ON (p.id = i.parts_id) LEFT JOIN exchangerate ex ON (ex.transdate = a.transdate) LEFT JOIN entity ee ON (a.person_id = ee.id) LEFT JOIN person ep ON (ep.entity_id = ee.id) -- these filters don't perform as well on large databases WHERE (e.name ilike '%' || $1 || '%' or $1 is null) and ($3 is null or eca.id in (select credit_id from eca_to_contact where contact ilike '%' || $3 || '%')) -- and (($4 is null and $5 is null and $6 is null and $7 is null) -- or eca.id in -- (select credit_id from eca_to_location -- where location_id in -- (select id from location -- where ($4 is null or line_one ilike '%' || $4 || '%' -- or line_two ilike '%' || $4 || '%') -- and ($5 is null or city -- ilike '%' || $5 || '%') -- and ($6 is null or state -- ilike '%' || $6 || '%') -- and ($7 is null or mail_code -- ilike '%' || $7 || '%') -- and ($10 is null or country_id = $10)) -- ) -- ) -- and (a.transdate >= $11 or $11 is null) -- and (a.transdate <= $12 or $12 is null) -- and (eca.startdate >= $14 or $14 is null) -- and (eca.startdate <= $15 or $15 is null) -- and (a.notes @@ plainto_tsquery($9) or $9 is null) ORDER BY eca.meta_number;
Creates a summary account (no quantities, just parts group by invoice). meta_number must match exactly or be NULL. inc_open and inc_closed are exact matches too. All other values specify ranges or may match partially.
SELECT id, name, meta_number, null::int, null::text, curr, parts_id, partnumber, description, sum(qty), unit, null::numeric, null::numeric, null::date, null::int, null::text, null::text, null::numeric, null::int, null::text FROM eca__history($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18) group by id, name, meta_number, curr, parts_id, partnumber, description, unit order by meta_number;
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;
Returns a list of locations attached to the credit account.
DECLARE out_row RECORD; BEGIN FOR out_row IN SELECT l.id, l.line_one, l.line_two, l.line_three, l.city, l.state, l.mail_code, c.id, c.name, lc.id, lc.class FROM location l JOIN eca_to_location ctl ON (ctl.location_id = l.id) JOIN location_class lc ON (ctl.location_class = lc.id) JOIN country c ON (c.id = l.country_id) WHERE ctl.credit_id = in_credit_id ORDER BY lc.id, l.id, c.name LOOP RETURN NEXT out_row; END LOOP; END;
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;
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;
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;
Saves the contact record at the entity credit account level. Returns 1.
DECLARE out_id int; BEGIN PERFORM * FROM eca_to_contact WHERE credit_id = in_credit_id AND contact_class_id = in_old_contact_class AND contact = in_old_contact; IF FOUND THEN UPDATE eca_to_contact SET contact = in_contact, description = in_description, contact_class_id = in_class_id WHERE credit_id = in_credit_id AND contact_class_id = in_old_contact_class AND contact = in_old_contact; ELSE INSERT INTO eca_to_contact(credit_id, contact_class_id, description, contact) VALUES (in_credit_id, in_class_id, in_description, in_contact); END IF; RETURN 1; END;
Saves an entity credit account-level note. Such a note is valid for only one credit account. Returns the id of the note.
DECLARE out_id int; BEGIN -- TODO, change this to create vector too INSERT INTO eca_note (ref_key, note_class, note, vector, subject) VALUES (in_credit_id, 3, in_note, '', in_subject); SELECT currval('note_id_seq') INTO out_id; RETURN out_id; END;
DECLARE retval eca__pricematrix; t_insert bool; BEGIN t_insert := false; PERFORM * FROM entity_credit_account WHERE id = in_credit_id AND entity_class = 1; IF FOUND THEN -- VENDOR UPDATE partsvendor SET lastcost = in_price, leadtime = in_lead_time, partnumber = in_partnumber, curr = in_curr WHERE credit_id = in_credit_id AND entry_id = in_entry_id; IF NOT FOUND THEN INSERT INTO partsvendor (parts_id, credit_id, lastcost, leadtime, partnumber, curr) VALUES (in_parts_id, in_credit_id, in_price, in_leadtime::int2, in_partnumber, in_curr); END IF; SELECT pv.parts_id, p.partnumber, p.description, pv.credit_id, NULL, NULL, pv.lastcost, pv.leadtime::int, pv.partnumber, NULL, NULL, pv.curr, pv.entry_id INTO retval FROM partsvendor pv JOIN parts p ON p.id = pv.parts_id WHERE parts_id = in_parts_id and credit_id = in_credit_id; RETURN retval; END IF; PERFORM * FROM entity_credit_account WHERE id = in_credit_id AND entity_class = 2; IF FOUND THEN -- CUSTOMER UPDATE partscustomer SET pricebreak = in_pricebreak, sellprice = in_price, validfrom = in_validfrom, validto = in_validto, curr = in_curr WHERE entry_id = in_entry_id and credit_id = in_credit_id; IF NOT FOUND THEN INSERT INTO partscustomer (parts_id, credit_id, sellprice, validfrom, validto, curr) VALUES (in_parts_id, in_credit_id, in_price, in_validfrom, in_validto, in_curr); t_insert := true; END IF; SELECT pc.parts_id, p.partnumber, p.description, pc.credit_id, pc.pricebreak, pc.sellprice, NULL, NULL, NULL, pc.validfrom, pc.validto, pc.curr, pc.entry_id INTO retval FROM partscustomer pc JOIN parts p on pc.parts_id = p.id WHERE entry_id = CASE WHEN t_insert THEN currval('partscustomer_entry_id_seq') ELSE in_entry_id END; RETURN retval; END IF; RAISE EXCEPTION 'No valid entity credit account found'; END;
Sets the tax values for the customer or vendor. The entity credit account must exist before calling this function, and must have a type of either 1 or 2.
DECLARE eca entity_credit_account; iter int; BEGIN IF in_tax_ids = '{}' THEN RETURN NULL; END IF; SELECT * FROM entity_credit_account into eca WHERE id = in_id; DELETE FROM eca_tax WHERE eca_id = in_id; FOR iter in array_lower(in_tax_ids, 1) .. array_upper(in_tax_ids, 1) LOOP INSERT INTO eca_tax (eca_id, chart_id) values (in_id, in_tax_ids[iter]); END LOOP; RETURN TRUE; end;
BEGIN IF TG_OP = 'INSERT' THEN INSERT INTO business_unit(class_id, description, credit_id) VALUES (7 - NEW.entity_class, NEW.meta_number, NEW.id); ELSIF TG_OP = 'UPDATE' THEN IF new.meta_number <> old.meta_number THEN UPDATE business_unit SET description = new.meta_number WHERE class_id = 7 - NEW.entity_class AND credit_id = new.id; END IF; ELSIF TG_OP = 'DELETE'THEN DELETE FROM business_unit WHERE class_id = 7 - NEW.entity_class AND credit_id = old_id; RETURN OLD; END IF; RETURN NEW; END;
SELECT p.entity_id, e.control_code, p.id, s.salutation, s.id, p.first_name, p.middle_name, p.last_name, ee.startdate, ee.enddate, ee.role, ee.ssn, ee.sales, ee.manager_id, mp.first_name, mp.last_name, ee.employeenumber, ee.dob, e.country_id FROM person p JOIN entity_employee ee on (ee.entity_id = p.entity_id) JOIN entity e ON (p.entity_id = e.id) LEFT JOIN salutation s on (p.salutation_id = s.id) LEFT JOIN person mp ON ee.manager_id = p.entity_id WHERE ee.role = 'manager' ORDER BY ee.employeenumber;
Returns an employee_result tuple with information specified by the entity_id.
SELECT p.entity_id, e.control_code, p.id, s.salutation, s.id, p.first_name, p.middle_name, p.last_name, ee.startdate, ee.enddate, ee.role, ee.ssn, ee.sales, ee.manager_id, mp.first_name, mp.last_name, ee.employeenumber, ee.dob, e.country_id FROM person p JOIN entity_employee ee on (ee.entity_id = p.entity_id) JOIN entity e ON (p.entity_id = e.id) LEFT JOIN salutation s on (p.salutation_id = s.id) LEFT JOIN person mp ON ee.manager_id = p.entity_id WHERE p.entity_id = $1;
Returns username, user_id, etc. information if the employee is a user.
SELECT * FROM users WHERE entity_id = $1;
Returns a list of managers, that is employees with the 'manager' role set.
DECLARE emp employees%ROWTYPE; BEGIN FOR emp IN SELECT e.salutation, e.first_name, e.last_name, ee.* FROM entity_employee ee JOIN entity e on e.id = ee.entity_id WHERE ee.sales = 't'::bool AND ee.role='manager' AND ee.entity_id <> coalesce(in_id, -1) ORDER BY name LOOP RETURN NEXT emp; END LOOP; END;
Saves an employeerecord with the specified information.
DECLARE out_id INT; BEGIN UPDATE entity_employee SET startdate = coalesce(in_start_date, now()::date), enddate = in_end_date, dob = in_dob, role = in_role, ssn = in_ssn, manager_id = in_manager_id, employeenumber = in_employeenumber WHERE entity_id = in_entity_id; out_id = in_entity_id; IF NOT FOUND THEN INSERT INTO entity_employee (startdate, enddate, dob, role, ssn, manager_id, employeenumber, entity_id) VALUES (coalesce(in_start_date, now()::date), in_end_date, in_dob, in_role, in_ssn, in_manager_id, in_employeenumber, in_entity_id); RETURN in_entity_id; END IF; RETURN out_id; END;
Returns a list of employee_result records matching the search criteria. employeenumber is an exact match. stardate_from and startdate_to specify the start dates for employee searches All others are partial matches. NULLs match all values.
SELECT p.entity_id, e.control_code, p.id, s.salutation, s.id, p.first_name, p.middle_name, p.last_name, ee.startdate, ee.enddate, ee.role, ee.ssn, ee.sales, ee.manager_id, mp.first_name, mp.last_name, ee.employeenumber, ee.dob, e.country_id FROM person p JOIN entity e ON p.entity_id = e.id JOIN entity_employee ee on (ee.entity_id = p.entity_id) LEFT JOIN salutation s on (p.salutation_id = s.id) LEFT JOIN person mp ON ee.manager_id = p.entity_id WHERE ($7 is null or p.entity_id in (select ref_key from entity_note WHERE note ilike '%' || $7 || '%')) and ($1 is null or $1 = ee.employeenumber) and ($2 is null or $2 <= ee.startdate) and ($3 is null or $3 >= ee.startdate) and ($4 is null or p.first_name ilike '%' || $4 || '%') and ($5 is null or p.middle_name ilike '%' || $5 || '%') and ($6 is null or p.last_name ilike '%' || $6 || '%');
DECLARE emp employee_search%ROWTYPE; BEGIN FOR emp IN SELECT * FROM employee_search WHERE coalesce(startdate, 'infinity'::timestamp) >= coalesce(in_startdateto, '-infinity'::timestamp) AND coalesce(startdate, '-infinity'::timestamp) <= coalesce(in_startdatefrom, 'infinity'::timestamp) AND coalesce(enddate, '-infinity'::timestamp) <= coalesce(in_enddateto, 'infinity'::timestamp) AND coalesce(enddate, 'infinity'::timestamp) >= coalesce(in_enddatefrom, '-infinity'::timestamp) AND (name % in_name OR note % in_notes) AND (sales = 't' OR coalesce(in_sales, 'f') = 'f') LOOP RETURN NEXT emp; END LOOP; return; END;
INSERT INTO entity_to_location (entity_id,location_id) SELECT entity_id, $2 FROM person WHERE id = $1;
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;
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;
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;
Returns a set of (only one) entity record with the entity id.
declare v_row entity; BEGIN -- Removing the exception when not found handling. Applications are -- perfectly capable of handling whether an entity was not found. No need -- for a database-level exception here. Moreover such results may be useful -- --CT SELECT * INTO v_row FROM entity WHERE id = in_entity_id; return next v_row; END;
Lists all bank accounts for the entity.
DECLARE out_row entity_bank_account%ROWTYPE; BEGIN FOR out_row IN SELECT * from entity_bank_account where entity_id = in_entity_id LOOP RETURN NEXT out_row; END LOOP; END;
Returns a list of entity classes, ordered by assigned ids
DECLARE out_row entity_class; BEGIN FOR out_row IN SELECT * FROM entity_class LEFT JOIN defaults ON setting_key = 'roll_prefix' WHERE active and pg_has_role(SESSION_USER, coalesce(defaults.value, 'lsmb_' || current_database() || '__') || 'contact_class_' || lower(regexp_replace(class, ' ', '_')), 'USAGE') ORDER BY id LOOP RETURN NEXT out_row; END LOOP; END;
Lists all contact info for the entity.
DECLARE out_row contact_list; BEGIN FOR out_row IN SELECT cl.class, cl.id, c.description, c.contact FROM entity_to_contact c JOIN contact_class cl ON (c.contact_class_id = cl.id) WHERE c.entity_id = in_entity_id LOOP return next out_row; END LOOP; END;
Returns a list of entity credit account entries for the entity and of the entity class.
DECLARE out_row entity_credit_retrieve; BEGIN FOR out_row IN SELECT ec.id, e.id, ec.entity_class, ec.discount, ec.discount_terms, ec.taxincluded, ec.creditlimit, ec.terms, ec.meta_number, ec.description, ec.business_id, ec.language_code, ec.pricegroup_id, ec.curr, ec.startdate, ec.enddate, ec.ar_ap_account_id, ec.cash_account_id, ec.discount_account_id, ec.threshold, e.control_code, ec.id, ec.pay_to_name, ec.taxform_id FROM entity e JOIN entity_credit_account ec ON (e.id = ec.entity_id) WHERE e.id = in_entity_id AND ec.entity_class = CASE WHEN in_entity_class = 3 THEN 2 WHEN in_entity_class IS NULL THEN ec.entity_class ELSE in_entity_class END LOOP RETURN NEXT out_row; END LOOP; END;
Lists all locations for an entity.
DECLARE out_row RECORD; BEGIN FOR out_row IN SELECT l.id, l.line_one, l.line_two, l.line_three, l.city, l.state, l.mail_code, c.id, c.name, lc.id, lc.class FROM location l JOIN entity_to_location ctl ON (ctl.location_id = l.id) JOIN location_class lc ON (ctl.location_class = lc.id) JOIN country c ON (c.id = l.country_id) WHERE ctl.entity_id = in_entity_id ORDER BY lc.id, l.id, c.name LOOP RETURN NEXT out_row; END LOOP; END;
Returns a set of notes (including content) attached to the entity.
DECLARE out_row record; BEGIN FOR out_row IN SELECT * FROM entity_note WHERE ref_key = in_entity_id ORDER BY created LOOP RETURN NEXT out_row; END LOOP; END;
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;
Saves bank account to the credit account.
DECLARE out_id int; BEGIN UPDATE entity_bank_account SET bic = in_bic, iban = in_iban WHERE id = in_bank_account_id; IF FOUND THEN out_id = in_bank_account_id; ELSE INSERT INTO entity_bank_account(entity_id, bic, iban) VALUES(in_entity_id, in_bic, in_iban); SELECT CURRVAL('entity_bank_account_id_seq') INTO out_id ; END IF; IF in_credit_id IS NOT NULL THEN UPDATE entity_credit_account SET bank_account = out_id WHERE id = in_credit_id; END IF; RETURN out_id; END;
Saves company contact information. The return value is meaningless.
DECLARE out_id int; BEGIN DELETE FROM entity_to_contact WHERE entity_id = in_entity_id AND contact = in_old_contact AND contact_class_id = in_old_class_id; INSERT INTO entity_to_contact (entity_id, contact_class_id, description, contact) VALUES (in_entity_id, in_class_id, in_description, in_contact); RETURN 1; END;
Saves an entity-level note. Such a note is valid for all credit accounts attached to that entity. Returns the id of the note.
DECLARE out_id int; BEGIN -- TODO, change this to create vector too INSERT INTO entity_note (ref_key, note_class, entity_id, note, vector, subject) VALUES (in_entity_id, 1, in_entity_id, in_note, '', in_subject); SELECT currval('note_id_seq') INTO out_id; RETURN out_id; END;
Returns the entity credit account info.
SELECT * FROM entity_credit_account WHERE id = $1;
Returns an entity credit id, based on entity_id, entity_class, and meta_number. This is the preferred way to locate an account if all three of these are known
DECLARE out_var int; BEGIN SELECT id INTO out_var FROM entity_credit_account WHERE entity_id = in_entity_id AND in_entity_class = entity_class AND in_meta_number = meta_number; RETURN out_var; END;
Returns the credit id from the meta_number and entity_class.
DECLARE out_credit_id int; BEGIN SELECT id INTO out_credit_id FROM entity_credit_account WHERE meta_number = in_meta_number AND entity_class = in_account_class; RETURN out_credit_id; END;
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;
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;
Creates checkpoints for each account at a specific date. Books are considered closed when they occur before the latest checkpoint timewise. This means that balances (and credit/debit amounts) can be calculated starting at a checkpoint and moving forward (thus providing a mechanism for expunging old data while keeping balances correct at some future point).
DECLARE ret_val int; approval_check int; cp_date date; BEGIN IF in_end_date > now()::date THEN RAISE EXCEPTION 'Invalid date: Must be earlier than present'; END IF; SELECT count(*) into approval_check FROM acc_trans ac JOIN ( select id, approved, transdate FROM ar UNION SELECT id, approved, transdate FROM gl UNION SELECT id, approved, transdate FROM ap ) gl ON (gl.id = ac.trans_id) WHERE (ac.approved IS NOT TRUE AND ac.transdate <= in_end_date) OR (gl.approved IS NOT TRUE AND gl.transdate <= in_end_date); if approval_check > 0 THEN RAISE EXCEPTION 'Unapproved transactions in closed period'; END IF; SELECT max(end_date) INTO cp_date FROM account_checkpoint WHERE end_date < in_end_date; INSERT INTO account_checkpoint (end_date, account_id, amount, debits, credits) SELECT in_end_date, COALESCE(a.chart_id, cp.account_id), COALESCE(SUM (a.amount),0) + coalesce(MAX (cp.amount), 0), COALESCE(SUM (CASE WHEN (a.amount < 0) THEN a.amount ELSE 0 END), 0) + COALESCE( MIN (cp.debits), 0), COALESCE(SUM (CASE WHEN (a.amount > 0) THEN a.amount ELSE 0 END), 0) + COALESCE( MAX (cp.credits), 0) FROM (SELECT * FROM acc_trans WHERE transdate <= in_end_date AND transdate > COALESCE(cp_date, '1200-01-01')) a FULL OUTER JOIN ( select account_id, end_date, amount, debits, credits from account_checkpoint WHERE end_date = cp_date ) cp on (a.chart_id = cp.account_id) group by COALESCE(a.chart_id, cp.account_id); SELECT count(*) INTO ret_val FROM account_checkpoint where end_date = in_end_date; return ret_val; END;
Lists equity accounts for the retained earnings dropdown.
SELECT * FROM account WHERE category = 'Q' ORDER BY accno;
Removes checkpoints and reverses yearend transactions on in_end_date
BEGIN PERFORM count(*) FROM account_checkpoint WHERE end_date = in_end_date; IF NOT FOUND THEN RETURN FALSE; END IF; DELETE FROM account_checkpoint WHERE end_date = in_end_date; PERFORM count(*) FROM yearend WHERE transdate = in_end_date and reversed is not true; IF FOUND THEN INSERT INTO gl (reference, description, approved) SELECT 'Reversing ' || reference, 'Reversing ' || description, true FROM gl WHERE id = (select trans_id from yearend where transdate = in_end_date and reversed is not true); INSERT INTO acc_trans (chart_id, amount, transdate, trans_id, approved) SELECT chart_id, amount * -1, currval('id'), true FROM acc_trans where trans_id = (select trans_id from yearend where transdate = in_end_date and reversed is not true); UPDATE yearend SET reversed = true where transdate = in_end_date and reversed is not true; END IF; DELETE FROM account_checkpoint WHERE end_date = in_end_date; RETURN TRUE; END;
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;
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;
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;
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;
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;
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;
Retrieves the file information specified including content.
SELECT * FROM file_base where id = $1 and file_class = $2;
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)
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);
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;
This function retrieves a list of file attachments on a specified object.
select * from file_links where ref_key = $1 and dest_class = $2;
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;
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;
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;
This opens a form, and returns the id of the form opened.
DECLARE usertest bool; BEGIN SELECT count(*) = 1 INTO usertest FROM session WHERE session_id = in_session_id AND users_id IN (select id from users WHERE username = SESSION_USER); IF usertest is not true THEN RAISE EXCEPTION 'Invalid session'; END IF; INSERT INTO open_forms (session_id) VALUES (in_session_id); RETURN currval('open_forms_id_seq'); END;
SELECT coalesce((select description FROM language WHERE code = (SELECT substring(value, 1, 2) FROM defaults WHERE setting_key = 'default_language')), 'english');
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;
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;
Gets a set of all valid account_link descriptions.
SELECT * FROM account_link_description;
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;
SELECT CASE WHEN count(*) > 0 THEN true ELSE false END FROM unnest($2) r WHERE t @> array[$1];
SELECT bool_and(in_tree(e, $2)) FROM unnest($1) e;
SELECT l.parts_id, p.partnumber, p.description, l.counted, l.expected, l.counted - l.expected FROM inventory_report_line l JOIN parts p ON l.parts_id = p.id WHERE l.report_id = $1;
SELECT r.id, r.transdate, r.source, r.ar_trans_id, r.ap_trans_id, ar.invnumber, ap.invnumber FROM inventory_report r JOIN inventory_report_line l ON l.report_id = r.id LEFT JOIN ar ON ar.id = r.ar_trans_id LEFT JOIN ap ON ap.id = r.ap_trans_id WHERE r.id = $1;
SELECT r.id, r.transdate, r.source, r.ar_trans_id, r.ap_trans_id, ar.invnumber, ap.invnumber FROM inventory_report r JOIN inventory_report_line l ON l.report_id = r.id JOIN parts p ON l.parts_id = p.id LEFT JOIN ar ON ar.id = r.ar_trans_id LEFT JOIN ap ON ap.id = r.ap_trans_id WHERE ($1 is null or $1 <= r.transdate) AND ($2 is null OR $2 >= r.transdate) AND ($3 IS NULL OR plainto_tsquery($3) @@ tsvector(p.partnumber)) AND ($4 IS NULL OR source LIKE $4 || '%');
DECLARE retval ap; BEGIN SELECT * INTO retval FROM ap WHERE entity_credit_id = (select id from entity_credit_account where entity_class = 1 AND meta_number = in_meta_number) AND invnumber = in_invoice_number; RETURN retval; END;
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;
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);
Returns true if the two dates are in the same year, false otherwise.
SELECT extract ('YEAR' from $1) = extract ('YEAR' from $2);
SELECT value FROM menu_attribute where node_id = 74 and attribute = 'rowcount';
BEGIN UPDATE menu_attribute set value = $1 where node_id = 74 and attribute='rowcount'; IF NOT FOUND THEN INSERT INTO menu_attribute (node_id, attribute, value) values (74, 'rowcount', $1); END IF; RETURN $1; END;
DECLARE retval journal_entry; BEGIN INSERT INTO journal_entry (source, description, entry_type, transaction_date, approved, is_template) VALUES (in_source, in_description, in_entry_type, in_transaction_date, coalesce(in_approved, false), coalesce(in_is_template, false)); SELECT * INTO retval FROM journal_entry WHERE id = currval('journal_id_seq'); RETURN retval; END;
DECLARE retval journal_line; BEGIN INSERT INTO journal_line(account_id, journal_id, amount, cleared, memo) VALUES (in_account_id, in_journal_id, in_amount, coalesce(in_cleared, false), in_memo); INSERT INTO business_unit_jl(entry_id, bu_class, bu_id) SELECT currval('journal_line_line_id_seq'), business_unit_class, bu FROM business_unit WHERE id = any(in_business_units); SELECT * INTO retval FROM journal_line where line_id = currval('journal_line_line_id_seq'); return retval; END;
SELECT * FROM journal_entry where id = $1;
SELECT * FROM eca_invoice where journal_id = $1;
select * from journal_line where journal_id = $1;
DECLARE retval eca_invoice; BEGIN INSERT INTO eca_invoice (order_id, journal_id, on_hold, reverse, credit_id, language_code) VALUES (in_order_id, in_journal_id, coalesce(in_on_hold, false), in_reverse, in_credit_id, in_language_code); SELECT * INTO retval FROM eca_invoice WHERE journal_id = in_journal_id; RETURN retval; END;
DECLARE retval journal_search_result; BEGIN FOR retval IN SELECT j.id, j.source, j.description, j.entry_type, j.transaction_date, j.approved, j.is_template, eca.meta_number, e.name, ec.class FROM journal_entry j LEFT JOIN eca_invoice i ON (i.journal_id = j.id) LEFT JOIN entity_credit_account eca ON (eca.id = credit_id) LEFT JOIN entity e ON (eca.entity_id = e.id) LEFT JOIN entity_class ec ON (eca.entity_class = ec.id) WHERE (in_source IS NULL OR in_source = j.source) AND (in_description IS NULL or in_description = j.description) AND (in_entry_type is null or in_entry_type = j.entry_type) and (in_transaction_date is null or in_transaction_date = j.transaction_date) and j.approved = coalesce(in_approved, true) and j.is_template = coalesce(in_is_template, false) and (in_department_id is null or j.department_id = in_department_id) and (in_meta_number is null or eca.meta_number = in_meta_number) and (in_entity_class is null or eca.entity_class = in_entity_class) LOOP RETURN NEXT retval; END LOOP; END;
SELECT sum(amount) = 0 FROM journal_line WHERE journal_id = $1;
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;
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);
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;
UPDATE location set active = false, inactive_date = now() WHERE id = $1; SELECT * FROM location WHERE id = 1;
Returns the location specified by in_id.
DECLARE out_location location%ROWTYPE; BEGIN SELECT * INTO out_location FROM location WHERE id = in_id; RETURN out_location; END;
DELETES the location specified by in_id. Does not return a value.
BEGIN DELETE FROM location WHERE id = in_id; END;
Returns all locations, ordered by country, state, and city.
DECLARE out_location location%ROWTYPE; BEGIN FOR out_location IN SELECT * FROM location ORDER BY country, state, city LOOP RETURN NEXT out_location; END LOOP; END;
Lists location classes, by default in order entered.
DECLARE out_row RECORD; BEGIN FOR out_row IN SELECT * FROM location_class ORDER BY id LOOP RETURN NEXT out_row; END LOOP; END;
Lists countries, by default in alphabetical order.
DECLARE out_row RECORD; BEGIN FOR out_row IN SELECT * FROM country ORDER BY name LOOP RETURN NEXT out_row; END LOOP; END;
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;
Returns matching locations. All matches may be partial.
DECLARE out_location location%ROWTYPE; BEGIN FOR out_location IN SELECT * FROM location WHERE address1 ilike '%' || in_address1 || '%' AND address2 ilike '%' || in_address2 || '%' AND in_city ilike '%' || in_city || '%' AND in_state ilike '%' || in_state || '%' AND in_zipcode ilike '%' || in_zipcode || '%' AND in_country ilike '%' || in_country || '%' LOOP RETURN NEXT out_location; END LOOP; END;
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;
Retrieves a single module's info by id.
SELECT * FROM lsmb_module where id = $1;
Returns a list of all defined modules, ordered by id.
SELECT * FROM lsmb_module ORDER BY id
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;
This function returns the complete menu tree. It is used to generate nested menus for the web interface.
DECLARE item menu_item; arg menu_attribute%ROWTYPE; BEGIN FOR item IN WITH RECURSIVE tree (path, id, parent, level, positions) AS (select id::text as path, id, parent, 0 as level, position::text FROM menu_node where parent is null UNION select path || ',' || n.id::text, n.id, n.parent, t.level + 1, t.positions || ',' || n.position FROM menu_node n JOIN tree t ON t.id = n.parent) SELECT n.position, n.id, c.level, n.label, c.path, n.parent, to_args(array[ma.attribute, ma.value]) FROM tree c JOIN menu_node n USING(id) JOIN menu_attribute ma ON (n.id = ma.node_id) WHERE n.id IN (select node_id FROM menu_acl acl LEFT JOIN pg_roles pr on pr.rolname = acl.role_name WHERE CASE WHEN role_name ilike 'public' THEN true WHEN rolname IS NULL THEN FALSE ELSE pg_has_role(rolname, 'USAGE') END GROUP BY node_id HAVING bool_and(CASE WHEN acl_type ilike 'DENY' THEN FALSE WHEN acl_type ilike 'ALLOW' THEN TRUE END)) or exists (select cn.id, cc.path FROM tree cc JOIN menu_node cn USING(id) WHERE cn.id IN (select node_id FROM menu_acl acl LEFT JOIN pg_roles pr on pr.rolname = acl.role_name WHERE CASE WHEN rolname ilike 'public' THEN true WHEN rolname IS NULL THEN FALSE ELSE pg_has_role(rolname, 'USAGE') END GROUP BY node_id HAVING bool_and(CASE WHEN acl_type ilike 'DENY' THEN false WHEN acl_type ilike 'ALLOW' THEN TRUE END)) and cc.path::text like c.path::text || ',%') GROUP BY n.position, n.id, c.level, n.label, c.path, c.positions, n.parent ORDER BY string_to_array(c.positions, ',')::int[] LOOP RETURN NEXT item; END LOOP; END;
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;
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 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;
Simple way to cast a Perl string to a date format of known type.
select $1;
SELECT * FROM parts WHERE id = $1;
SELECT * FROM PARTS WHERE partnumber = $1 and obsolete is not true;
SELECT * FROM parts WHERE ($1 IS NULL OR (partnumber like $1 || '%')) AND ($2 IS NULL OR (description @@ plainto_tsquery(get_default_lang()::regconfig, $2))) AND not obsolete ORDER BY partnumber;
Reverses a payment. All fields are mandatory except batch_id and voucher_id because they determine the identity of the payment to be reversed.
DECLARE pay_row record; t_voucher_id int; t_voucher_inserted bool; t_currs text[]; t_rev_fx numeric; t_fxgain_id int; t_fxloss_id int; t_paid_fx numeric; BEGIN SELECT * INTO t_rev_fx FROM currency_get_exchangerate( in_currency, in_date_reversed, in_account_class); SELECT * INTO t_paid_fx FROM currency_get_exchangerate( in_currency, in_date_paid, in_account_class); select value::int INTO t_fxgain_id FROM setting_get('fxgain_accno_id'); select value::int INTO t_fxloss_id FROM setting_get('fxloss_accno_id'); SELECT string_to_array(value, ':') into t_currs from defaults where setting_key = 'curr'; IF in_currency IS NULL OR in_currency = t_currs[1] THEN t_rev_fx := 1; t_paid_fx := 1; ELSIF t_rev_fx IS NULL THEN t_rev_fx := in_exchangerate; PERFORM payments_set_exchangerate(in_account_class, in_exchangerate, in_currency, in_date_reversed); ELSIF t_rev_fx <> in_exchangerate THEN RAISE EXCEPTION 'Exchange rate different than on file'; END IF; IF t_rev_fx IS NULL THEN RAISE EXCEPTION 'No exchangerate provided and not default currency'; END IF; IF in_batch_id IS NOT NULL THEN t_voucher_id := nextval('voucher_id_seq'); t_voucher_inserted := FALSE; END IF; FOR pay_row IN SELECT a.*, c.ar_ap_account_id, arap.curr, arap.fxrate FROM acc_trans a JOIN (select id, curr, entity_credit_account, CASE WHEN curr = t_currs[1] THEN 1 ELSE buy END as fxrate FROM ar LEFT JOIN exchangerate USING (transdate, curr) WHERE in_account_class = 2 UNION SELECT id, curr, entity_credit_account, CASE WHEN curr = t_currs[1] THEN 1 ELSE sell END as fxrate FROM ap LEFT JOIN exchangerate USING (transdate, curr) WHERE in_account_class = 1 ) arap ON (a.trans_id = arap.id) JOIN entity_credit_account c ON (arap.entity_credit_account = c.id) JOIN account ch ON (a.chart_id = ch.id) WHERE a.source IS NOT DISTINCT FROM in_source AND a.transdate = in_date_paid AND in_credit_id = arap.entity_credit_account AND in_cash_accno = ch.accno and in_voucher_id IS NOT DISTINCT FROM voucher_id LOOP IF pay_row.curr = t_currs[1] THEN pay_row.fxrate = 1; END IF; IF in_batch_id IS NOT NULL AND t_voucher_inserted IS NOT TRUE THEN INSERT INTO voucher (id, trans_id, batch_id, batch_class) VALUES (t_voucher_id, pay_row.trans_id, in_batch_id, CASE WHEN in_account_class = 1 THEN 4 WHEN in_account_class = 2 THEN 7 END); t_voucher_inserted := TRUE; END IF; INSERT INTO acc_trans (trans_id, chart_id, amount, transdate, source, memo, approved, voucher_id) VALUES (pay_row.trans_id, pay_row.chart_id, pay_row.amount / t_paid_fx * -1 * t_rev_fx, in_date_reversed, in_source, 'Reversing ' || COALESCE(in_source, ''), case when in_batch_id is not null then false else true end, t_voucher_id), (pay_row.trans_id, pay_row.ar_ap_account_id, pay_row.amount / t_paid_fx * pay_row.fxrate, in_date_reversed, in_source, 'Reversing ' || COALESCE(in_source, ''), case when in_batch_id is not null then false else true end, t_voucher_id), (pay_row.trans_id, case when pay_row.fxrate > t_rev_fx THEN t_fxloss_id ELSE t_fxgain_id END, pay_row.amount / t_paid_fx * (t_rev_fx - pay_row.fxrate), in_date_reversed, in_source, 'Reversing ' || COALESCE(in_source, ''), case when in_batch_id is not null then false else true end, t_voucher_id); END LOOP; RETURN 1; END;
This searches for payments. in_date_to and _date_from specify the acceptable date range. All other matches are exact except that null matches all values. Currently (and to support earlier data) we define a payment as a collection of acc_trans records against the same credit account and cash account, on the same day with the same source number, and optionally the same voucher id.
DECLARE out_row payment_record; BEGIN FOR out_row IN select sum(CASE WHEN c.entity_class = 1 then a.amount ELSE a.amount * -1 END), c.meta_number, c.id, co.legal_name, compound_array(ARRAY[ARRAY[ch.id::text, ch.accno, ch.description]]), a.source, b.control_code, b.description, a.voucher_id, a.transdate FROM entity_credit_account c JOIN ( select entity_credit_account, id, curr FROM ar WHERE in_account_class = 2 UNION SELECT entity_credit_account, id, curr FROM ap WHERE in_account_class = 1 ) arap ON (arap.entity_credit_account = c.id) JOIN acc_trans a ON (arap.id = a.trans_id) JOIN chart ch ON (ch.id = a.chart_id) JOIN company co ON (c.entity_id = co.entity_id) LEFT JOIN voucher v ON (v.id = a.voucher_id) LEFT JOIN batch b ON (b.id = v.batch_id) WHERE (ch.accno = in_cash_accno) AND (in_currency IS NULL OR in_currency = arap.curr) AND (c.id = in_credit_id OR in_credit_id IS NULL) AND (a.transdate >= in_date_from OR in_date_from IS NULL) AND (a.transdate <= in_date_to OR in_date_to IS NULL) AND (source = in_source OR in_source IS NULL) GROUP BY c.meta_number, c.id, co.legal_name, a.transdate, a.source, a.memo, b.id, b.control_code, b.description, voucher_id ORDER BY a.transdate, c.meta_number, a.source LOOP RETURN NEXT out_row; END LOOP; END;
This posts the payments for large batch workflows. Note that in_transactions is a two-dimensional numeric array. Of each sub-array, the first element is the (integer) transaction id, and the second is the amount for that transaction.
DECLARE out_count int; t_voucher_id int; t_trans_id int; t_amount numeric; t_ar_ap_id int; t_cash_id int; t_currs text[]; t_exchangerate numeric; t_cash_sign int; BEGIN SELECT * INTO t_exchangerate FROM currency_get_exchangerate( in_currency, in_payment_date, in_account_class); IF in_batch_id IS NULL THEN -- t_voucher_id := NULL; RAISE EXCEPTION 'Bulk Post Must be from Batch!'; ELSE INSERT INTO voucher (batch_id, batch_class, trans_id) values (in_batch_id, (SELECT batch_class_id FROM batch WHERE id = in_batch_id), in_transactions[1][1]); t_voucher_id := currval('voucher_id_seq'); END IF; SELECT string_to_array(value, ':') into t_currs from defaults where setting_key = 'curr'; IF (in_currency IS NULL OR in_currency = t_currs[1]) THEN t_exchangerate := 1; ELSIF t_exchangerate IS NULL THEN t_exchangerate := in_exchangerate; PERFORM payments_set_exchangerate(in_account_class, in_exchangerate, in_currency, in_payment_date); ELSIF t_exchangerate <> in_exchangerate THEN RAISE EXCEPTION 'Exchange rate different than on file'; END IF; IF t_exchangerate IS NULL THEN RAISE EXCEPTION 'No exchangerate provided and not default currency'; END IF; CREATE TEMPORARY TABLE bulk_payments_in (id int, amount numeric, fxrate numeric, gain_loss_accno int); select id into t_ar_ap_id from chart where accno = in_ar_ap_accno; select id into t_cash_id from chart where accno = in_cash_accno; FOR out_count IN array_lower(in_transactions, 1) .. array_upper(in_transactions, 1) LOOP -- Fill the bulk payments table INSERT INTO bulk_payments_in(id, amount) VALUES (in_transactions[out_count][1], in_transactions[out_count][2]); END LOOP; IF in_account_class = 1 THEN t_cash_sign := 1; ELSE t_cash_sign := -1; END IF; IF (in_currency IS NULL OR in_currency = t_currs[1]) THEN UPDATE bulk_payments_in SET fxrate = 1; ELSE UPDATE bulk_payments_in SET fxrate = (SELECT CASE WHEN in_account_class = 1 THEN sell ELSE buy END FROM exchangerate e JOIN (SELECT transdate, id, curr FROM ar UNION SELECT transdate, id, curr FROM ap) a ON (e.transdate = a.transdate AND e.curr = a.curr) WHERE a.id = bulk_payments_in.id); UPDATE bulk_payments_in SET gain_loss_accno = (SELECT value::int FROM defaults WHERE setting_key = 'fxgain_accno_id') WHERE ((t_exchangerate - bulk_payments_in.fxrate) * t_cash_sign) < 0; UPDATE bulk_payments_in SET gain_loss_accno = (SELECT value::int FROM defaults WHERE setting_key = 'fxloss_accno_id') WHERE ((t_exchangerate - bulk_payments_in.fxrate) * t_cash_sign) > 0; -- explicitly leave zero gain/loss accno_id entries at NULL -- so we have an easy check for which END IF; -- Insert cash side INSERT INTO acc_trans (trans_id, chart_id, amount, approved, voucher_id, transdate, source) SELECT id, t_cash_id, amount * t_cash_sign * t_exchangerate/fxrate, CASE WHEN t_voucher_id IS NULL THEN true ELSE false END, t_voucher_id, in_payment_date, in_source FROM bulk_payments_in where amount <> 0; -- early payment discounts INSERT INTO acc_trans (trans_id, chart_id, amount, approved, voucher_id, transdate, source) SELECT bpi.id, eca.discount_account_id, amount * t_cash_sign * t_exchangerate/fxrate / (1 - discount::numeric/100) * (discount::numeric/100), CASE WHEN t_voucher_id IS NULL THEN true ELSE false END, t_voucher_id, in_payment_date, in_source FROM bulk_payments_in bpi JOIN (select entity_credit_account, id, transdate FROM ar WHERE in_account_class = 2 UNION SELECT entity_credit_account, id, transdate FROM ap WHERE in_account_class = 1) gl ON gl.id = bpi.id JOIN entity_credit_account eca ON gl.entity_credit_account = eca.id WHERE bpi.amount <> 0 AND extract('days' from age(gl.transdate)) < eca.discount_terms; INSERT INTO acc_trans (trans_id, chart_id, amount, approved, voucher_id, transdate, source) SELECT bpi.id, t_ar_ap_id, amount * t_cash_sign * -1 * t_exchangerate/fxrate / (1 - discount::numeric/100) * (discount::numeric/100), CASE WHEN t_voucher_id IS NULL THEN true ELSE false END, t_voucher_id, in_payment_date, in_source FROM bulk_payments_in bpi JOIN (select entity_credit_account, id, transdate FROM ar WHERE in_account_class = 2 UNION SELECT entity_credit_account, id, transdate FROM ap WHERE in_account_class = 1) gl ON gl.id = bpi.id JOIN entity_credit_account eca ON gl.entity_credit_account = eca.id WHERE bpi.amount <> 0 AND extract('days' from age(gl.transdate)) < eca.discount_terms; -- Insert ar/ap side INSERT INTO acc_trans (trans_id, chart_id, amount, approved, voucher_id, transdate, source) SELECT id, t_ar_ap_id, amount * -1 * t_cash_sign, CASE WHEN t_voucher_id IS NULL THEN true ELSE false END, t_voucher_id, in_payment_date, in_source FROM bulk_payments_in where amount <> 0; -- Insert fx gain/loss effects, if applicable INSERT INTO acc_trans (trans_id, chart_id, amount, approved, voucher_id, transdate, source) SELECT id, gain_loss_accno, amount * t_cash_sign * (1 - t_exchangerate/fxrate), CASE WHEN t_voucher_id IS NULL THEN true ELSE false END, t_voucher_id, in_payment_date, in_source FROM bulk_payments_in WHERE amount <> 0 AND gain_loss_accno IS NOT NULL; DROP TABLE bulk_payments_in; perform unlock_all(); return out_count; END;
This function finds a payment based on the id and retrieves the record, it is usefull for printing payments :)
DECLARE out_payment payment_header_item; BEGIN FOR out_payment IN SELECT p.id as payment_id, p.reference as payment_reference, p.payment_date, c.legal_name as legal_name, am.amount as amount, em.first_name, em.last_name, p.currency, p.notes FROM payment p JOIN entity_employee ent_em ON (ent_em.entity_id = p.employee_id) JOIN person em ON (ent_em.entity_id = em.entity_id) JOIN entity_credit_account eca ON (eca.id = p.entity_credit_id) JOIN company c ON (c.entity_id = eca.entity_id) JOIN payment_links pl ON (p.id = pl.payment_id) LEFT JOIN ( SELECT sum(a.amount) as amount FROM acc_trans a JOIN account acc ON (a.chart_id = acc.id) JOIN account_link al ON (acc.id =al.account_id) JOIN payment_links pl ON (pl.entry_id=a.entry_id) WHERE al.description in ('AP_paid', 'AP_discount', 'AR_paid', 'AR_discount') and ((in_account_class = 1 AND al.description like 'AP%') or (in_account_class = 2 AND al.description like 'AR%')) ) am ON (true) WHERE p.id = in_payment_id LOOP RETURN NEXT out_payment; END LOOP; END;
This function finds a payment based on the id and retrieves all the line records, it is usefull for printing payments and build reports :)
DECLARE out_payment_line payment_line_item; BEGIN FOR out_payment_line IN SELECT pl.payment_id, ac.entry_id, pl.type as link_type, ac.trans_id, a.invnumber as invoice_number, ac.chart_id, ch.accno as chart_accno, ch.description as chart_description, ch.link as chart_link, ac.amount, ac.transdate as trans_date, ac.source, ac.cleared_on, ac.fx_transaction, ac.project_id, ac.memo, ac.invoice_id, ac.approved, ac.cleared_on, ac.reconciled_on FROM acc_trans ac JOIN payment_links pl ON (pl.entry_id = ac.entry_id ) JOIN chart ch ON (ch.id = ac.chart_id) LEFT JOIN (SELECT id,invnumber FROM ar WHERE in_account_class = 2 UNION SELECT id,invnumber FROM ap WHERE in_account_class = 1 ) a ON (ac.trans_id = a.id) WHERE pl.payment_id = in_payment_id LOOP RETURN NEXT out_payment_line; END LOOP; END;
This function takes a single argument (1 for vendor, 2 for customer as always) and returns all entities with accounts of the appropriate type.
DECLARE out_entity entity%ROWTYPE; BEGIN FOR out_entity IN SELECT ec.id, e.name, e.entity_class, e.created FROM entity e JOIN entity_credit_account ec ON (ec.entity_id = e.id) WHERE e.entity_class = in_account_class LOOP RETURN NEXT out_entity; END LOOP; END;
This function takes the following arguments (all prefaced with in_ in the db): account_class: 1 for vendor, 2 for customer business_type: integer of business.id. currency: char(3) of currency (for example 'USD') date_from, date_to: These dates are inclusive. batch_id: For payment batches, where fees are concerned. ar_ap_accno: The AR/AP account number. This then returns a set of contact information with a 2 dimensional array cnsisting of outstanding invoices. Note that the payment selection logic is that this returns all invoices which are either approved or in the batch_id specified. It also locks the invoices using the LedgerSMB discretionary locking framework, and if not possible, returns the username of the individual who has the lock.
DECLARE payment_item payment_contact_invoice; BEGIN FOR payment_item IN SELECT c.id AS contact_id, e.control_code as econtrol_code, c.description as eca_description, e.name AS contact_name, c.meta_number AS account_number, sum( case when u.username IS NULL or u.username = SESSION_USER THEN coalesce(p.due::numeric, 0) - CASE WHEN c.discount_terms > extract('days' FROM age(a.transdate)) THEN 0 ELSE (coalesce(p.due::numeric, 0)) * coalesce(c.discount::numeric, 0) / 100 END ELSE 0::numeric END) AS total_due, compound_array(ARRAY[[ a.id::text, a.invnumber, a.transdate::text, a.amount::text, (a.amount - p.due)::text, (CASE WHEN c.discount_terms < extract('days' FROM age(a.transdate)) THEN 0 ELSE (coalesce(p.due, 0) * coalesce(c.discount, 0) / 100) END)::text, (coalesce(p.due, 0) - (CASE WHEN c.discount_terms < extract('days' FROM age(a.transdate)) THEN 0 ELSE (coalesce(p.due, 0)) * coalesce(c.discount, 0) / 100 END))::text, case when u.username IS NOT NULL and u.username <> SESSION_USER THEN 0::text ELSE 1::text END, COALESCE(u.username, 0::text) ]]), sum(case when a.batch_id = in_batch_id then 1 else 0 END), bool_and(lock_record(a.id, (select max(session_id) FROM "session" where users_id = ( select id from users WHERE username = SESSION_USER)))) FROM entity e JOIN entity_credit_account c ON (e.id = c.entity_id) JOIN (SELECT ap.id, invnumber, transdate, amount, entity_id, curr, 1 as invoice_class, entity_credit_account, on_hold, v.batch_id, approved, paid FROM ap LEFT JOIN (select * from voucher where batch_class = 1) v ON (ap.id = v.trans_id) WHERE in_account_class = 1 AND (v.batch_class = 1 or v.batch_id IS NULL) UNION SELECT ar.id, invnumber, transdate, amount, entity_id, curr, 2 as invoice_class, entity_credit_account, on_hold, v.batch_id, approved, paid FROM ar LEFT JOIN (select * from voucher where batch_class = 2) v ON (ar.id = v.trans_id) WHERE in_account_class = 2 AND (v.batch_class = 2 or v.batch_id IS NULL) ORDER BY transdate ) a ON (a.entity_credit_account = c.id) JOIN transactions t ON (a.id = t.id) JOIN (SELECT acc_trans.trans_id, sum(CASE WHEN in_account_class = 1 THEN amount WHEN in_account_class = 2 THEN amount * -1 END) AS due FROM acc_trans JOIN account coa ON (coa.id = acc_trans.chart_id) JOIN account_link al ON (al.account_id = coa.id) LEFT JOIN voucher v ON (acc_trans.voucher_id = v.id) WHERE ((al.description = 'AP' AND in_account_class = 1) OR (al.description = 'AR' AND in_account_class = 2)) AND (approved IS TRUE or v.batch_class IN (3, 6)) GROUP BY acc_trans.trans_id) p ON (a.id = p.trans_id) LEFT JOIN "session" s ON (s."session_id" = t.locked_by) LEFT JOIN users u ON (u.id = s.users_id) WHERE (a.batch_id = in_batch_id OR (a.invoice_class = in_account_class AND a.approved AND due <> 0 AND NOT a.on_hold AND a.curr = in_currency AND EXISTS (select trans_id FROM acc_trans WHERE trans_id = a.id AND chart_id = (SELECT id from account WHERE accno = in_ar_ap_accno) ))) AND (in_meta_number IS NULL OR in_meta_number = c.meta_number) GROUP BY c.id, e.name, c.meta_number, c.threshold, e.control_code, c.description HAVING (sum(p.due) >= c.threshold OR sum(case when a.batch_id = in_batch_id then 1 else 0 END) > 0) ORDER BY c.meta_number ASC LOOP RETURN NEXT payment_item; END LOOP; END;
DECLARE out_overpayment payment_overpayments_available_amount; BEGIN FOR out_overpayment IN SELECT chart_id, accno, chart_description, abs(sum(available)) FROM overpayments WHERE payment_class = in_account_class AND entity_credit_id = in_entity_credit_id AND available <> 0 GROUP BY chart_id, accno, chart_description LOOP RETURN NEXT out_overpayment; END LOOP; END;
Returns payment information on the entity credit account as required to for discount calculations and payment processing.
SELECT ec.id, cp.legal_name || coalesce(':' || ec.description,'') as name, e.entity_class, ec.discount_account_id, ec.meta_number FROM entity_credit_account ec JOIN entity e ON (ec.entity_id = e.id) JOIN company cp ON (cp.entity_id = e.id) WHERE ec.id = $1;
Returns a minimal set of information about customer or vendor accounts as needed for discount calculations and the like.
DECLARE out_entity payment_vc_info; BEGIN FOR out_entity IN SELECT ec.id, cp.legal_name || coalesce(':' || ec.description,'') as name, e.entity_class, ec.discount_account_id, ec.meta_number FROM entity_credit_account ec JOIN entity e ON (ec.entity_id = e.id) JOIN company cp ON (cp.entity_id = e.id) WHERE ec.entity_class = in_account_class AND (cp.legal_name ilike coalesce('%'||in_vc_name||'%','%%') OR cp.tax_id = in_vc_idn) LOOP RETURN NEXT out_entity; END LOOP; END;
This function takes a single argument (1 for vendor, 2 for customer as always) and returns all entities with open accounts of the appropriate type.
DECLARE out_entity entity%ROWTYPE; BEGIN FOR out_entity IN SELECT ec.id, cp.legal_name as name, e.entity_class, e.created FROM entity e JOIN entity_credit_account ec ON (ec.entity_id = e.id) JOIN company cp ON (cp.entity_id = e.id) WHERE ec.entity_class = in_account_class AND CASE WHEN in_account_class = 1 THEN ec.id IN (SELECT entity_credit_account FROM acc_trans JOIN chart ON (acc_trans.chart_id = chart.id) JOIN ap ON (acc_trans.trans_id = ap.id) WHERE link = 'AP' GROUP BY chart_id, trans_id, entity_credit_account HAVING SUM(acc_trans.amount) <> 0) WHEN in_account_class = 2 THEN ec.id IN (SELECT entity_credit_account FROM acc_trans JOIN chart ON (acc_trans.chart_id = chart.id) JOIN ar ON (acc_trans.trans_id = ar.id) WHERE link = 'AR' GROUP BY chart_id, trans_id, entity_credit_account HAVING SUM(acc_trans.amount) <> 0) END LOOP RETURN NEXT out_entity; END LOOP; END;
This function is based on payment_get_open_invoices and returns only one invoice if the in_invnumber is set. if no in_invnumber is passed this function behaves the same as payment_get_open_invoices
DECLARE payment_inv payment_invoice; BEGIN FOR payment_inv IN SELECT * from payment_get_open_invoices(in_account_class, in_entity_credit_id, in_curr, in_datefrom, in_dateto, in_amountfrom, in_amountto, in_department_id) WHERE (invnumber like in_invnumber OR in_invnumber IS NULL) LOOP RETURN NEXT payment_inv; END LOOP; END;
This function is the base for get_open_invoice and returns all open invoices for the entity_credit_id it has a lot of options to enable filtering and use the same logic for entity_class_id and currency.
DECLARE payment_inv payment_invoice; BEGIN FOR payment_inv IN SELECT a.id AS invoice_id, a.invnumber AS invnumber,a.invoice AS invoice, a.transdate AS invoice_date, a.amount AS amount, a.amount/ (CASE WHEN a.curr = (SELECT * from defaults_get_defaultcurrency()) THEN 1 ELSE (CASE WHEN in_account_class = 2 THEN ex.buy ELSE ex.sell END) END) as amount_fx, (CASE WHEN c.discount_terms < extract('days' FROM age(a.transdate)) THEN 0 ELSE (coalesce(ac.due, a.amount)) * coalesce(c.discount, 0) / 100 END) AS discount, (CASE WHEN c.discount_terms < extract('days' FROM age(a.transdate)) THEN 0 ELSE (coalesce(ac.due, a.amount)) * coalesce(c.discount, 0) / 100 END)/ (CASE WHEN a.curr = (SELECT * from defaults_get_defaultcurrency()) THEN 1 ELSE (CASE WHEN in_account_class = 2 THEN ex.buy ELSE ex.sell END) END) as discount_fx, ac.due - (CASE WHEN c.discount_terms < extract('days' FROM age(a.transdate)) THEN 0 ELSE (coalesce(ac.due, a.amount)) * coalesce(c.discount, 0) / 100 END) AS due, (ac.due - (CASE WHEN c.discount_terms < extract('days' FROM age(a.transdate)) THEN 0 ELSE (coalesce(ac.due, a.amount)) * coalesce(c.discount, 0) / 100 END))/ (CASE WHEN a.curr = (SELECT * from defaults_get_defaultcurrency()) THEN 1 ELSE (CASE WHEN in_account_class = 2 THEN ex.buy ELSE ex.sell END) END) AS due_fx, (CASE WHEN a.curr = (SELECT * from defaults_get_defaultcurrency()) THEN 1 ELSE (CASE WHEN in_account_class = 2 THEN ex.buy ELSE ex.sell END) END) AS exchangerate --TODO HV prepare drop entity_id from ap,ar --FROM (SELECT id, invnumber, transdate, amount, entity_id, FROM (SELECT id, invnumber, invoice, transdate, amount, 1 as invoice_class, curr, entity_credit_account, department_id, approved FROM ap UNION --SELECT id, invnumber, transdate, amount, entity_id, SELECT id, invnumber, invoice, transdate, amount, 2 AS invoice_class, curr, entity_credit_account, department_id, approved FROM ar ) a JOIN (SELECT trans_id, chart_id, sum(CASE WHEN in_account_class = 1 THEN amount WHEN in_account_class = 2 THEN amount * -1 END) as due FROM acc_trans GROUP BY trans_id, chart_id) ac ON (ac.trans_id = a.id) JOIN chart ON (chart.id = ac.chart_id) LEFT JOIN exchangerate ex ON ( ex.transdate = a.transdate AND ex.curr = a.curr ) JOIN entity_credit_account c ON (c.id = a.entity_credit_account) -- OR (a.entity_credit_account IS NULL and a.entity_id = c.entity_id)) WHERE ((chart.link = 'AP' AND in_account_class = 1) OR (chart.link = 'AR' AND in_account_class = 2)) AND a.invoice_class = in_account_class AND c.entity_class = in_account_class AND c.id = in_entity_credit_id --### short term: ignore fractional cent differences AND a.curr = in_curr AND (a.transdate >= in_datefrom OR in_datefrom IS NULL) AND (a.transdate <= in_dateto OR in_dateto IS NULL) AND (a.amount >= in_amountfrom OR in_amountfrom IS NULL) AND (a.amount <= in_amountto OR in_amountto IS NULL) AND (a.department_id = in_department_id OR in_department_id IS NULL) AND due <> 0 AND a.approved = true GROUP BY a.invnumber, a.transdate, a.amount, amount_fx, discount, discount_fx, ac.due, a.id, c.discount_terms, ex.buy, ex.sell, a.curr, a.invoice LOOP RETURN NEXT payment_inv; END LOOP; END;
DECLARE out_entity payment_vc_info; BEGIN FOR out_entity IN SELECT DISTINCT entity_credit_id, legal_name, e.entity_class, discount, o.meta_number FROM overpayments o JOIN entity e ON (e.id=o.entity_id) WHERE available <> 0 AND in_account_class = payment_class LOOP RETURN NEXT out_entity; END LOOP; END;
Returns a list of available overpayments
DECLARE out_overpayment overpayments%ROWTYPE; BEGIN FOR out_overpayment IN SELECT DISTINCT * FROM overpayments WHERE payment_class = in_account_class AND entity_credit_id = in_entity_credit_id AND available <> 0 AND (in_chart_id IS NULL OR chart_id = in_chart_id ) ORDER BY payment_date LOOP RETURN NEXT out_overpayment; END LOOP; END;
This function returns vendor or customer info
DECLARE out_row payment_location_result; BEGIN FOR out_row IN SELECT l.id, l.line_one, l.line_two, l.line_three, l.city, l.state, l.mail_code, c.name, lc.class FROM location l JOIN entity_to_location ctl ON (ctl.location_id = l.id) JOIN entity cp ON (ctl.entity_id = cp.id) JOIN location_class lc ON (ctl.location_class = lc.id) JOIN country c ON (c.id = l.country_id) JOIN entity_credit_account ec ON (ec.entity_id = cp.entity_id) WHERE ec.id = in_entity_credit_id AND lc.id = in_location_class_id ORDER BY lc.id, l.id, c.name LOOP RETURN NEXT out_row; END LOOP; END;
Posts a payment. in_op_* arrays are cross-indexed with eachother. Other arrays are cross-indexed with eachother. This API will probably change in 1.4 as we start looking at using more custom complex types and arrays of those (requires Pg 8.4 or higher).
DECLARE var_payment_id int; DECLARE var_gl_id int; DECLARE var_entry record; DECLARE var_entry_id int[]; DECLARE out_count int; DECLARE coa_id record; DECLARE var_employee int; DECLARE var_account_id int; DECLARE default_currency char(3); DECLARE current_exchangerate numeric; DECLARE old_exchangerate numeric; DECLARE fx_gain_loss_amount numeric; BEGIN IF array_upper(in_amount, 1) <> array_upper(in_cash_account_id, 1) THEN RAISE EXCEPTION 'Wrong number of accounts'; END IF; SELECT * INTO default_currency FROM defaults_get_defaultcurrency(); SELECT * INTO current_exchangerate FROM currency_get_exchangerate(in_curr, in_datepaid, in_account_class); SELECT INTO var_employee p.id FROM users u JOIN person p ON (u.entity_id=p.entity_id) WHERE username = SESSION_USER LIMIT 1; -- -- WE HAVE TO INSERT THE PAYMENT, USING THE GL INFORMATION -- THE ID IS GENERATED BY payment_id_seq -- INSERT INTO payment (reference, payment_class, payment_date, employee_id, currency, notes, department_id, entity_credit_id) VALUES ((CASE WHEN in_account_class = 1 THEN setting_increment('rcptnumber') -- I FOUND THIS ON sql/modules/Settings.sql ELSE -- and it is very usefull setting_increment('paynumber') END), in_account_class, in_datepaid, var_employee, in_curr, in_notes, in_department_id, in_entity_credit_id); SELECT currval('payment_id_seq') INTO var_payment_id; -- WE'LL NEED THIS VALUE TO USE payment_link table -- WE'LL NEED THIS VALUE TO JOIN WITH PAYMENT -- NOW COMES THE HEAVY PART, STORING ALL THE POSSIBLE TRANSACTIONS... -- -- FIRST WE SHOULD INSERT THE CASH ACCOUNTS -- -- WE SHOULD HAVE THE DATA STORED AS (ACCNO, AMOUNT), SO IF (array_upper(in_cash_account_id, 1) > 0) THEN FOR out_count IN array_lower(in_cash_account_id, 1) .. array_upper(in_cash_account_id, 1) LOOP INSERT INTO acc_trans (chart_id, amount, trans_id, transdate, approved, source, memo) VALUES (in_cash_account_id[out_count], CASE WHEN in_account_class = 1 THEN in_amount[out_count]*current_exchangerate ELSE (in_amount[out_count]*current_exchangerate)* - 1 END, in_transaction_id[out_count], in_datepaid, coalesce(in_approved, true), in_source[out_count], in_memo[out_count]); INSERT INTO payment_links VALUES (var_payment_id, currval('acc_trans_entry_id_seq'), 1); IF (in_ovp_payment_id IS NOT NULL AND in_ovp_payment_id[out_count] IS NOT NULL) THEN INSERT INTO payment_links VALUES (in_ovp_payment_id[out_count], currval('acc_trans_entry_id_seq'), 0); END IF; END LOOP; -- NOW LETS HANDLE THE AR/AP ACCOUNTS -- WE RECEIVED THE TRANSACTIONS_ID AND WE CAN OBTAIN THE ACCOUNT FROM THERE FOR out_count IN array_lower(in_transaction_id, 1) .. array_upper(in_transaction_id, 1) LOOP SELECT INTO var_account_id chart_id FROM acc_trans as ac JOIN chart as c ON (c.id = ac.chart_id) WHERE trans_id = in_transaction_id[out_count] AND ( c.link = 'AP' OR c.link = 'AR' ); -- We need to know the exchangerate of this transaction -- ### BUG: we don't have a guarantee that the transaction is -- the same currency as in_curr, so, we can't use -- current_exchangerate as the basis for fx gain/loss -- calculations IF (in_curr = default_currency) THEN old_exchangerate := 1; ELSIF (in_account_class = 2) THEN SELECT buy INTO old_exchangerate FROM exchangerate e JOIN ar a ON (a.transdate = e.transdate) AND (a.curr = e.curr) WHERE a.id = in_transaction_id[out_count]; ELSE SELECT sell INTO old_exchangerate FROM exchangerate e JOIN ap a ON (a.transdate = e.transdate) AND (a.curr = e.curr) WHERE a.id = in_transaction_id[out_count]; END IF; -- Now we post the AP/AR transaction INSERT INTO acc_trans (chart_id, amount, trans_id, transdate, approved, source, memo) VALUES (var_account_id, CASE WHEN in_account_class = 1 THEN (in_amount[out_count]*old_exchangerate) * -1 ELSE in_amount[out_count]*old_exchangerate END, in_transaction_id[out_count], in_datepaid, coalesce(in_approved, true), in_source[out_count], in_memo[out_count]); -- Lets set the gain/loss, if fx_gain_loss_amount equals zero then we dont need to post -- any transaction fx_gain_loss_amount := in_amount[out_count]*current_exchangerate - in_amount[out_count]*old_exchangerate; IF (in_account_class = 1) THEN -- in case of vendor invoices, the invoice amounts have been negated, do the same with the diff fx_gain_loss_amount := fx_gain_loss_amount * -1; END IF; IF (fx_gain_loss_amount < 0) THEN INSERT INTO acc_trans (chart_id, amount, trans_id, transdate, approved, source) VALUES ((select value::int from defaults WHERE setting_key = 'fxgain_accno_id'), fx_gain_loss_amount, in_transaction_id[out_count], in_datepaid, coalesce(in_approved, true), in_source[out_count]); ELSIF (fx_gain_loss_amount > 0) THEN INSERT INTO acc_trans (chart_id, amount, trans_id, transdate, approved, source) VALUES ((select value::int from defaults WHERE setting_key = 'fxloss_accno_id'), fx_gain_loss_amount, in_transaction_id[out_count], in_datepaid, coalesce(in_approved, true), in_source[out_count]); END IF; -- Now we set the links INSERT INTO payment_links VALUES (var_payment_id, currval('acc_trans_entry_id_seq'), 1); END LOOP; END IF; -- END IF -- -- WE NEED TO HANDLE THE OVERPAYMENTS NOW -- -- -- FIRST WE HAVE TO MAKE THE GL TO HOLD THE OVERPAYMENT TRANSACTIONS -- THE ID IS GENERATED BY gl_id_seq -- IF (array_upper(in_op_cash_account_id, 1) > 0) THEN INSERT INTO gl (reference, description, transdate, person_id, notes, approved, department_id) VALUES (setting_increment('glnumber'), in_gl_description, in_datepaid, var_employee, in_notes, in_approved, in_department_id); SELECT currval('id') INTO var_gl_id; -- -- WE NEED TO SET THE GL_ID FIELD ON PAYMENT'S TABLE -- UPDATE payment SET gl_id = var_gl_id WHERE id = var_payment_id; -- NOW COMES THE HEAVY PART, STORING ALL THE POSSIBLE TRANSACTIONS... -- -- FIRST WE SHOULD INSERT THE OVERPAYMENT CASH ACCOUNTS -- FOR out_count IN array_lower(in_op_cash_account_id, 1) .. array_upper(in_op_cash_account_id, 1) LOOP INSERT INTO acc_trans (chart_id, amount, trans_id, transdate, approved, source, memo) VALUES (in_op_cash_account_id[out_count], CASE WHEN in_account_class = 1 THEN in_op_amount[out_count] ELSE in_op_amount[out_count] * - 1 END, var_gl_id, in_datepaid, coalesce(in_approved, true), in_op_source[out_count], in_op_memo[out_count]); INSERT INTO payment_links VALUES (var_payment_id, currval('acc_trans_entry_id_seq'), 2); END LOOP; -- NOW LETS HANDLE THE OVERPAYMENT ACCOUNTS FOR out_count IN array_lower(in_op_account_id, 1) .. array_upper(in_op_account_id, 1) LOOP INSERT INTO acc_trans (chart_id, amount, trans_id, transdate, approved, source, memo) VALUES (in_op_account_id[out_count], CASE WHEN in_account_class = 1 THEN in_op_amount[out_count] * -1 ELSE in_op_amount[out_count] END, var_gl_id, in_datepaid, coalesce(in_approved, true), in_op_source[out_count], in_op_memo[out_count]); INSERT INTO payment_links VALUES (var_payment_id, currval('acc_trans_entry_id_seq'), 2); END LOOP; END IF; return var_payment_id; END;
Returns all information on a payment type by the id. This should be renamed to account for its behavior in future versions.
DECLARE out_row payment_type%ROWTYPE; BEGIN FOR out_row IN SELECT * FROM payment_type where id=in_payment_type_id LOOP RETURN NEXT out_row; END LOOP; END;
DECLARE out_row payment_type%ROWTYPE; BEGIN FOR out_row IN SELECT * FROM payment_type LOOP RETURN NEXT out_row; END LOOP; END;
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;
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;
Returns dates for year to date, and last year.
SELECT * FROM periods ORDER BY id
Deletes a contact record specified for the person. Returns true if a record was found and deleted, false if not.
BEGIN DELETE FROM entity_to_contact WHERE person_id = (SELECT entity_id FROM person WHERE id = in_person_id) and contact_class_id = in_contact_class_id and contact= in_contact; RETURN FOUND; END;
Deletes a location mapping to a person. Returns true if found, false if no data deleted.
BEGIN DELETE FROM entity_to_location WHERE person_id = (select entity_id from person where id = in_person_id) AND location_id = in_location_id AND location_class = in_location_class; RETURN FOUND; END;
SELECT e.id, e.control_code, e.name, e.country_id, c.name, p.first_name, p.middle_name, p.last_name FROM entity e JOIN country c ON c.id = e.country_id JOIN person p ON p.entity_id = e.id WHERE e.id = $1;
SELECT e.id, e.control_code, e.name, e.country_id, c.name, p.first_name, p.middle_name, p.last_name FROM entity e JOIN country c ON c.id = e.country_id JOIN person p ON p.entity_id = e.id WHERE e.control_code = $1;
Returns the entity_id of the current, logged in user.
SELECT entity_id from users where username = SESSION_USER;
Lists bank accounts for a person
DECLARE out_row entity_bank_account%ROWTYPE; BEGIN FOR out_row IN SELECT * from entity_bank_account where entity_id = in_entity_id LOOP RETURN NEXT out_row; END LOOP; END;
Returns a list of contacts attached to the function.
DECLARE out_row RECORD; BEGIN FOR out_row IN SELECT cc.class, cc.id, c.description, c.contact FROM entity_to_contact c JOIN contact_class cc ON (c.contact_class_id = cc.id) JOIN person p ON (c.person_id = p.entity_id) WHERE p.entity_id = in_entity_id LOOP RETURN NEXT out_row; END LOOP; END;
Returns a list of languages ordered by code
SELECT * FROM language ORDER BY code ASC
Returns a list of locations specified attached to the person.
DECLARE out_row RECORD; BEGIN FOR out_row IN SELECT l.id, l.line_one, l.line_two, l.line_three, l.city, l.state, l.mail_code, c.id, c.name, lc.id, lc.class FROM location l JOIN entity_to_location ctl ON (ctl.location_id = l.id) JOIN person p ON (ctl.person_id = p.entity_id) JOIN location_class lc ON (ctl.location_class = lc.id) JOIN country c ON (c.id = l.country_id) WHERE p.entity_id = in_entity_id ORDER BY lc.id, l.id, c.name LOOP RETURN NEXT out_row; END LOOP; END;
Returns a list of notes attached to a person.
DECLARE out_row record; BEGIN FOR out_row IN SELECT * FROM entity_note WHERE ref_key = in_entity_id ORDER BY created LOOP RETURN NEXT out_row; END LOOP; END;
Returns a list of salutations ordered by id.
SELECT * FROM salutation ORDER BY id ASC
Saves the person with the information specified. Returns the entity_id of the record saved.
DECLARE e_id int; e entity; loc location; l_id int; p_id int; BEGIN select * into e from entity where id = in_entity_id and entity_class = 3; e_id := in_entity_id; IF FOUND THEN UPDATE entity SET name = in_first_name || ' ' || in_last_name, country_id = in_country_id WHERE id = in_entity_id; ELSE INSERT INTO entity (name, entity_class, country_id) values (in_first_name || ' ' || in_last_name, 3, in_country_id); e_id := currval('entity_id_seq'); END IF; UPDATE person SET salutation_id = in_salutation_id, first_name = in_first_name, last_name = in_last_name, middle_name = in_middle_name WHERE entity_id = in_entity_id; IF FOUND THEN RETURN in_entity_id; ELSE -- Do an insert INSERT INTO person (salutation_id, first_name, last_name, entity_id) VALUES (in_salutation_id, in_first_name, in_last_name, e_id); RETURN e_id; END IF; END;
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;
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;
WITH gl (id) AS ( SELECT id FROM ap WHERE approved is true AND entity_credit_account = $1 UNION ALL SELECT id FROM ar WHERE approved is true AND entity_credit_account = $1 ) SELECT a.id, a.accno, a.description, a.category, ah.id, ah.accno, ah.description, CASE WHEN a.category = 'E' THEN -1 ELSE 1 END * sum(ac.amount) FROM account a JOIN account_heading ah on a.heading = ah.id JOIN acc_trans ac ON a.id = ac.chart_id JOIN gl ON ac.trans_id = gl.id WHERE ac.approved is true AND ($2 IS NULL OR ac.transdate >= $2) AND ($3 IS NULL OR ac.transdate <= $3) AND a.category IN ('I', 'E') GROUP BY a.id, a.accno, a.description, a.category, ah.id, ah.accno, ah.description ORDER BY a.category DESC, a.accno ASC;
WITH RECURSIVE bu_tree (id, parent, path) AS ( SELECT id, null, row(array[id])::tree_record FROM business_unit WHERE id = any($3) UNION ALL SELECT bu.id, parent, row((path).t || bu.id)::tree_record FROM business_unit bu JOIN bu_tree ON bu.parent_id = bu_tree.id ) SELECT a.id, a.accno, a.description, a.category, ah.id, ah.accno, ah.description, CASE WHEN a.category = 'E' THEN -1 ELSE 1 END * sum(ac.amount) FROM account a JOIN account_heading ah on a.heading = ah.id JOIN acc_trans ac ON a.id = ac.chart_id AND ac.approved JOIN tx_report gl ON ac.trans_id = gl.id LEFT JOIN (select array_agg(path) as bu_ids, entry_id FROM business_unit_ac buac JOIN bu_tree ON bu_tree.id = buac.bu_id GROUP BY buac.entry_id) bu ON (ac.entry_id = bu.entry_id) WHERE ac.approved is true AND ($1 IS NULL OR ac.transdate >= $1) AND ($2 IS NULL OR ac.transdate <= $2) AND ($3 = '{}' OR $3 is null or in_tree($3, bu_ids)) AND a.category IN ('I', 'E') GROUP BY a.id, a.accno, a.description, a.category, ah.id, ah.accno, ah.description ORDER BY a.category DESC, a.accno ASC;
WITH RECURSIVE bu_tree (id, parent, path) AS ( SELECT id, null, row(array[id])::tree_record FROM business_unit WHERE id = any($3) UNION ALL SELECT bu.id, parent, row((path).t || bu.id)::tree_record FROM business_unit bu JOIN bu_tree ON bu.parent_id = bu_tree.id ) SELECT a.id, a.accno, a.description, a.category, ah.id, ah.accno, ah.description, CASE WHEN a.category = 'E' THEN -1 ELSE 1 END * sum(ac.amount * ca.portion) FROM account a JOIN account_heading ah on a.heading = ah.id JOIN acc_trans ac ON a.id = ac.chart_id AND ac.approved JOIN tx_report gl ON ac.trans_id = gl.id JOIN cash_impact ca ON gl.id = ca.id LEFT JOIN (select array_agg(path) as bu_ids, entry_id FROM business_unit_ac buac JOIN bu_tree ON bu_tree.id = buac.bu_id GROUP BY entry_id) bu ON (ac.entry_id = bu.entry_id) WHERE ac.approved is true AND ($3 = '{}' OR $3 is null or in_tree($3, bu_ids)) AND ($1 IS NULL OR ac.transdate >= $1) AND ($2 IS NULL OR ac.transdate <= $2) AND a.category IN ('I', 'E') GROUP BY a.id, a.accno, a.description, a.category, ah.id, ah.accno, ah.description ORDER BY a.category DESC, a.accno ASC;
SELECT a.id, a.accno, a.description, a.category, ah.id, ah.accno, ah.description, CASE WHEN a.category = 'E' THEN -1 ELSE 1 END * sum(ac.amount) FROM account a JOIN account_heading ah on a.heading = ah.id JOIN acc_trans ac ON a.id = ac.chart_id WHERE ac.approved AND ac.trans_id = $1 AND a.category IN ('I', 'E') GROUP BY a.id, a.accno, a.description, a.category, ah.id, ah.accno, ah.description ORDER BY a.category DESC, a.accno ASC;
WITH RECURSIVE bu_tree (id, parent, path) AS ( SELECT id, null, row(array[id])::tree_record FROM business_unit WHERE id = any($4) UNION ALL SELECT bu.id, parent, row((path).t || bu.id)::tree_record FROM business_unit bu JOIN bu_tree ON bu.parent_id = bu_tree.id ) SELECT a.id, a.accno, a.description, a.category, ah.id, ah.accno, ah.description, sum(ac.amount) * -1 FROM account a JOIN account_heading ah on a.heading = ah.id JOIN acc_trans ac ON ac.chart_id = a.id JOIN invoice i ON i.id = ac.invoice_id JOIN account_link l ON l.account_id = a.id JOIN ar ON ar.id = ac.trans_id LEFT JOIN (select as_array(bu.path) as bu_ids, entry_id from business_unit_inv bui JOIN bu_tree bu ON bui.bu_id = bu.id GROUP BY entry_id) bui ON bui.entry_id = i.id WHERE i.parts_id = $3 AND (ac.transdate >= $1 OR $1 IS NULL) AND (ac.transdate <= $2 OR $2 IS NULL) AND ar.approved AND l.description = 'IC_expense' AND ($4 is null or $4 = '{}' OR in_tree($4, bu_ids)) GROUP BY a.id, a.accno, a.description, a.category, ah.id, ah.accno, ah.description UNION SELECT a.id, a.accno, a.description, a.category, ah.id, ah.accno, ah.description, sum(i.sellprice * i.qty * (1 - coalesce(i.discount, 0))) FROM parts p JOIN invoice i ON i.id = p.id JOIN acc_trans ac ON ac.invoice_id = i.id JOIN account a ON p.income_accno_id = a.id JOIN ar ON ar.id = ac.trans_id JOIN account_heading ah on a.heading = ah.id LEFT JOIN (select as_array(bu.path) as bu_ids, entry_id from business_unit_inv bui JOIN bu_tree bu ON bui.bu_id = bu.id GROUP BY entry_id) bui ON bui.entry_id = i.id WHERE i.parts_id = $3 AND (ac.transdate >= $1 OR $1 IS NULL) AND (ac.transdate <= $2 OR $2 IS NULL) AND ar.approved AND ($4 is null or $4 = '{}' OR in_tree($4, bu_ids)) GROUP BY a.id, a.accno, a.description, a.category, ah.id, ah.accno, ah.description
Returns an alphabetically ordered pricegroup list.
SELECT * FROM pricegroup ORDER BY pricegroup;
SELECT * FROM pricegroup;
delete from partscustomer where entry_id = $1 and credit_id = $2; delete from partsvendor where entry_id = $1 and credit_id = $2; select true;
DECLARE retval eca__pricematrix; t_insert bool; t_entity_class int; BEGIN t_insert := false; SELECT entity_class INTO t_entity_class FROM entity_credit_account WHERE id = in_credit_id; IF t_entity_class = 1 THEN -- VENDOR UPDATE partsvendor SET lastcost = in_price, leadtime = in_lead_time, partnumber = in_partnumber, curr = in_curr WHERE credit_id = in_credit_id AND entry_id = in_entry_id; IF NOT FOUND THEN INSERT INTO partsvendor (parts_id, credit_id, lastcost, leadtime, partnumber, curr) VALUES (in_parts_id, in_credit_id, in_price, in_leadtime::int2, in_partnumber, in_curr); END IF; SELECT pv.parts_id, p.partnumber, p.description, pv.credit_id, NULL, NULL, pv.lastcost, pv.leadtime::int, pv.partnumber, NULL, NULL, pv.curr, pv.entry_id INTO retval FROM partsvendor pv JOIN parts p ON p.id = pv.parts_id WHERE parts_id = in_parts_id and credit_id = in_credit_id; RETURN retval; ELSIF t_entity_class = 2 THEN -- CUSTOMER UPDATE partscustomer SET pricebreak = in_pricebreak, sellprice = in_price, validfrom = in_validfrom, validto = in_validto, curr = in_curr WHERE entry_id = in_entry_id and credit_id = in_credit_id; IF NOT FOUND THEN INSERT INTO partscustomer (parts_id, credit_id, sellprice, validfrom, validto, curr) VALUES (in_parts_id, in_credit_id, in_price, in_validfrom, in_validto, in_curr); t_insert := true; END IF; SELECT pc.parts_id, p.partnumber, p.description, pc.credit_id, pc.pricebreak, pc.sellprice, NULL, NULL, NULL, pc.validfrom, pc.validto, pc.curr, pc.entry_id INTO retval FROM partscustomer pc JOIN parts p on pc.parts_id = p.id WHERE entry_id = CASE WHEN t_insert THEN currval('partscustomer_entry_id_seq') ELSE in_entry_id END; RETURN retval; ELSE RAISE EXCEPTION 'No valid entity credit account found'; END IF; END;
returns set of accounts set up for reconciliation. Currently we pull the account number and description from the account table.
SELECT coa.accno || ' ' || coa.description as name, coa.accno, coa.id as id FROM account coa JOIN cr_coa_to_account cta ON cta.chart_id = coa.id ORDER BY coa.accno;
This function is used for automatically matching entries from an external source like a bank-produced csv file. This function is very sensitive to ordering of inputs. NULL or empty in_scn values MUST be submitted after meaningful scns. It is also highly recommended that within each category, one submits in order of amount. We should therefore wrap it in another function which can operate on a set, perhaps in 1.4....
DECLARE in_account int; la RECORD; t_errorcode INT; our_value NUMERIC; lid INT; in_count int; t_scn TEXT; t_uid int; t_prefix text; t_amount numeric; BEGIN SELECT CASE WHEN a.category in ('A', 'E') THEN in_amount * -1 ELSE in_amount END into t_amount FROM cr_report r JOIN account a ON r.chart_id = a.id WHERE r.id = in_report_id; SELECT value into t_prefix FROM defaults WHERE setting_key = 'check_prefix'; t_uid := person__get_my_entity_id(); IF in_scn = '' THEN t_scn := NULL; ELSE t_scn := t_prefix || in_scn; END IF; IF t_scn IS NOT NULL THEN -- could this be changed to update, if not found insert? SELECT count(*) INTO in_count FROM cr_report_line WHERE scn ilike t_scn AND report_id = in_report_id AND their_balance = 0; IF in_count = 0 THEN INSERT INTO cr_report_line (report_id, scn, their_balance, our_balance, clear_time, "user", trans_type) VALUES (in_report_id, t_scn, t_amount, 0, in_date, t_uid, in_type); ELSIF in_count = 1 THEN UPDATE cr_report_line SET their_balance = t_amount, clear_time = in_date, cleared = true WHERE t_scn = scn AND report_id = in_report_id AND their_balance = 0; ELSE SELECT count(*) INTO in_count FROM cr_report_line WHERE t_scn ilike scn AND report_id = in_report_id AND our_value = t_amount and their_balance = 0; IF in_count = 0 THEN -- no match among many of values SELECT id INTO lid FROM cr_report_line WHERE t_scn ilike scn AND report_id = in_report_id ORDER BY our_balance ASC limit 1; UPDATE cr_report_line SET their_balance = t_amount, clear_time = in_date, trans_type = in_type, cleared = true WHERE id = lid; ELSIF in_count = 1 THEN -- EXECT MATCH UPDATE cr_report_line SET their_balance = t_amount, trans_type = in_type, clear_time = in_date, cleared = true WHERE t_scn = scn AND report_id = in_report_id AND our_value = t_amount AND their_balance = 0; ELSE -- More than one match SELECT id INTO lid FROM cr_report_line WHERE t_scn ilike scn AND report_id = in_report_id AND our_value = t_amount ORDER BY id ASC limit 1; UPDATE cr_report_line SET their_balance = t_amount, trans_type = in_type, cleared = true, clear_time = in_date WHERE id = lid; END IF; END IF; ELSE -- scn IS NULL, check on amount instead SELECT count(*) INTO in_count FROM cr_report_line WHERE report_id = in_report_id AND our_balance = t_amount AND their_balance = 0 and post_date = in_date and scn NOT LIKE t_prefix || '%'; IF in_count = 0 THEN -- no match INSERT INTO cr_report_line (report_id, scn, their_balance, our_balance, clear_time, "user", trans_type) VALUES (in_report_id, t_scn, t_amount, 0, in_date, t_uid, in_type); ELSIF in_count = 1 THEN -- perfect match UPDATE cr_report_line SET their_balance = t_amount, trans_type = in_type, clear_time = in_date, cleared = true WHERE report_id = in_report_id AND our_balance = t_amount AND their_balance = 0 and in_scn NOT LIKE t_prefix || '%'; ELSE -- more than one match SELECT min(id) INTO lid FROM cr_report_line WHERE report_id = in_report_id AND our_balance = t_amount AND their_balance = 0 and post_date = in_date AND scn NOT LIKE t_prefix || '%' LIMIT 1; UPDATE cr_report_line SET their_balance = t_amount, trans_type = in_type, clear_time = in_date, cleared = true WHERE id = lid; END IF; END IF; return 1; END;
This function allows a user to delete his or her own unsubmitted, unapproved reconciliation reports only. This is designed to allow a user to back out of the reconciliation process without cluttering up the search results for others.
BEGIN DELETE FROM cr_report_line WHERE report_id = in_report_id AND report_id IN (SELECT id FROM cr_report WHERE entered_username = SESSION_USER AND submitted IS NOT TRUE and approved IS NOT TRUE); DELETE FROM cr_report WHERE id = in_report_id AND entered_username = SESSION_USER AND submitted IS NOT TRUE AND approved IS NOT TRUE; RETURN FOUND; END;
This function deletes any specified unapproved transaction.
BEGIN DELETE FROM cr_report_line WHERE report_id = in_report_id AND report_id IN (SELECT id FROM cr_report WHERE approved IS NOT TRUE); DELETE FROM cr_report WHERE id = in_report_id AND approved IS NOT TRUE; RETURN FOUND; END;
Gets the cleared balance of the account specified by chart_id. This is specified in normal format (i.e. positive numbers for debits for asset and espense accounts, and positive numbers for credits in other accounts Note that currently contra accounts will show negative balances.
select CASE WHEN c.category in('A', 'E') THEN sum(ac.amount) * -1 ELSE sum(ac.amount) END FROM account c JOIN acc_trans ac ON (ac.chart_id = c.id) JOIN (select id from ar where approved union select id from ap where approved union select id from gl where approved) g on (g.id = ac.trans_id) WHERE c.id = $1 AND ac.cleared is true and ac.approved is true GROUP BY c.id, c.category;
Gets the current balance of all approved transactions against a specific account. For asset and expense accounts this is the debit balance, for others this is the credit balance.
DECLARE outval NUMERIC; BEGIN SELECT CASE WHEN (select category FROM account WHERE id = in_account_id) IN ('A', 'E') THEN sum(a.amount) * -1 ELSE sum(a.amount) END FROM acc_trans a JOIN ( SELECT id FROM ar WHERE approved is true UNION SELECT id FROM ap WHERE approved is true UNION SELECT id FROM gl WHERE approved is true ) gl ON a.trans_id = gl.id WHERE a.approved IS TRUE AND a.chart_id = in_account_id AND a.transdate <= in_date; RETURN outval; END;
Retrieves all header info from the reconciliation report.
DECLARE row cr_report; BEGIN SELECT * INTO row FROM cr_report where id = in_report_id AND scn = -1; IF NOT FOUND THEN -- I think this is a fairly major error condition RAISE EXCEPTION 'Bad report id.'; ELSE return next row; END IF; END;
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;
Ensures that the list of pending transactions in the report is up to date.
DECLARE gl_row RECORD; t_recon_fx BOOL; BEGIN SELECT recon_fx INTO t_recon_fx FROM cr_report WHERE id = in_report_id; INSERT INTO cr_report_line (report_id, scn, their_balance, our_balance, "user", voucher_id, ledger_id, post_date) SELECT in_report_id, CASE WHEN ac.source IS NULL OR ac.source = '' THEN gl.ref ELSE ac.source END, 0, sum(amount / CASE WHEN t_recon_fx IS NOT TRUE OR gl.table = 'gl' THEN 1 WHEN t_recon_fx and gl.table = 'ap' THEN ex.sell WHEN t_recon_fx and gl.table = 'ar' THEN ex.buy END) AS amount, (select entity_id from users where username = CURRENT_USER), ac.voucher_id, min(ac.entry_id), ac.transdate FROM acc_trans ac JOIN transactions t on (ac.trans_id = t.id) JOIN (select id, entity_credit_account::text as ref, curr, transdate, 'ar' as table FROM ar where approved UNION select id, entity_credit_account::text, curr, transdate, 'ap' as table FROM ap WHERE approved UNION select id, reference, '', transdate, 'gl' as table FROM gl WHERE approved) gl ON (gl.table = t.table_name AND gl.id = t.id) LEFT JOIN cr_report_line rl ON (rl.report_id = in_report_id AND ((rl.ledger_id = ac.entry_id AND ac.voucher_id IS NULL) OR (rl.voucher_id = ac.voucher_id))) LEFT JOIN exchangerate ex ON gl.transdate = ex.transdate WHERE ac.cleared IS FALSE AND ac.approved IS TRUE AND ac.chart_id = in_chart_id AND ac.transdate <= in_end_date AND ((t_recon_fx is not true and ac.fx_transaction is not true) OR (t_recon_fx is true AND (gl.table <> 'gl' OR ac.fx_transaction IS TRUE))) GROUP BY gl.ref, ac.source, ac.transdate, ac.memo, ac.voucher_id, gl.table, case when gl.table = 'gl' then gl.id else 1 end HAVING count(rl.id) = 0; UPDATE cr_report set updated = now(), their_total = coalesce(in_their_total, their_total) where id = in_report_id; RETURN in_report_id; END;
Marks the report approved and marks all cleared transactions in it cleared.
-- Does some basic checks before allowing the approval to go through; -- moves the approval to "cr_report_line", I guess, or some other "final" table. -- -- Pending may just be a single flag in the database to mark that it is -- not finalized. Will need to discuss with Chris. DECLARE current_row RECORD; completed cr_report_line; total_errors INT; in_user TEXT; ac_entries int[]; BEGIN in_user := current_user; -- so far, so good. Different user, and no errors remain. Therefore, -- we can move it to completed reports. -- -- User may not be necessary - I would think it better to use the -- in_user, to note who approved the report, than the user who -- filed it. This may require clunkier syntax.. -- ac_entries := '{}'; update cr_report set approved = 't', approved_by = person__get_my_entity_id(), approved_username = SESSION_USER where id = in_report_id; FOR current_row IN SELECT compound_array(entries) AS entries FROM ( select as_array(ac.entry_id) as entries FROM acc_trans ac JOIN transactions t on (ac.trans_id = t.id) JOIN (select id, entity_credit_account::text as ref, 'ar' as table FROM ar UNION select id, entity_credit_account::text, 'ap' as table FROM ap UNION select id, reference, 'gl' as table FROM gl) gl ON (gl.table = t.table_name AND gl.id = t.id) LEFT JOIN cr_report_line rl ON (rl.report_id = in_report_id AND ((rl.ledger_id = ac.entry_id AND ac.voucher_id IS NULL) OR (rl.voucher_id = ac.voucher_id)) and rl.cleared is true) WHERE ac.cleared IS FALSE AND ac.chart_id = (select chart_id from cr_report where id = in_report_id) GROUP BY gl.ref, ac.source, ac.transdate, ac.memo, ac.voucher_id, gl.table HAVING count(rl.report_id) > 0) a LOOP ac_entries := ac_entries || current_row.entries; END LOOP; UPDATE acc_trans SET cleared = TRUE where entry_id = any(ac_entries); return 1; END;
Returns the details of the report.
DECLARE row cr_report_line; BEGIN FOR row IN select * from cr_report_line where report_id = in_report_id order by scn, post_date LOOP RETURN NEXT row; END LOOP; END;
Pulls the payee information for the reconciliation report.
DECLARE row recon_payee; BEGIN FOR row IN select * from recon_payee where report_id = in_report_id order by scn, post_date LOOP RETURN NEXT row; END LOOP; END;
DECLARE row cr_report; BEGIN select * into row from cr_report where id = in_report_id; RETURN row; END;
Sets which lines of the report are cleared.
BEGIN UPDATE cr_report_line SET cleared = false WHERE report_id = in_report_id; UPDATE cr_report_line SET cleared = true WHERE report_id = in_report_id AND id = ANY(in_line_ids); RETURN found; END;
Searches for reconciliation reports. NULLs match all values. in_date_to and in_date_from give a range of reports. All other inputs are exact matches.
DECLARE report cr_report; BEGIN FOR report IN SELECT r.* FROM cr_report r JOIN account c ON (r.chart_id = c.id) WHERE (in_date_from IS NULL OR in_date_from <= end_date) and (in_date_to IS NULL OR in_date_to >= end_date) AND (in_balance_from IS NULL or in_balance_from <= their_total ) AND (in_balance_to IS NULL OR in_balance_to >= their_total) AND (in_chart_id IS NULL OR in_chart_id = chart_id) AND (in_submitted IS NULL or in_submitted = submitted) AND (in_approved IS NULL OR in_approved = approved) AND (r.deleted IS FALSE) ORDER BY c.accno, end_date, their_total LOOP RETURN NEXT report; END LOOP; END;
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;
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;
WITH ac (chart_id, amount) AS ( SELECT chart_id, CASE WHEN acc_trans.approved and gl.approved THEN amount ELSE 0 END FROM acc_trans JOIN (select id, approved from ar union all select id, approved from ap union all select id, approved from gl) gl ON gl.id = acc_trans.trans_id ), l(account_id, link) AS ( SELECT account_id, array_to_string(array_agg(description), ':') FROM account_link GROUP BY account_id ) SELECT a.id, a.is_heading, a.accno, a.description, a.gifi_accno, CASE WHEN sum(ac.amount) < 0 THEN sum(amount) * -1 ELSE null::numeric END, CASE WHEN sum(ac.amount) > 0 THEN sum(amount) ELSE null::numeric END, count(ac.*), l.link FROM (SELECT id,false as is_heading, accno, description, gifi_accno FROM account UNION SELECT id, true, accno, description, null::text FROM account_heading) a LEFT JOIN ac ON ac.chart_id = a.id AND not a.is_heading LEFT JOIN l ON l.account_id = a.id AND NOT a.is_heading GROUP BY a.id, a.is_heading, a.accno, a.description, a.gifi_accno, l.link ORDER BY a.accno;
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;
DECLARE retval gl_report_item; t_balance numeric; t_chart_id int; BEGIN IF in_from_date IS NULL THEN t_balance := 0; ELSIF in_accno IS NOT NULL THEN SELECT id INTO t_chart_id FROM account WHERE accno = in_accno; t_balance := account__obtain_balance(in_from_date , (select id from account where accno = in_accno)); ELSE t_balance := null; END IF; FOR retval IN WITH RECURSIVE bu_tree (id, path) AS ( SELECT id, id::text AS path FROM business_unit WHERE parent_id is null UNION SELECT bu.id, bu_tree.path || ',' || bu.id FROM business_unit bu JOIN bu_tree ON bu_tree.id = bu.parent_id ) SELECT g.id, g.type, g.invoice, g.reference, g.description, ac.transdate, ac.source, ac.amount, c.accno, c.gifi_accno, g.till, ac.cleared, ac.memo, c.description AS accname, ac.chart_id, ac.entry_id, sum(ac.amount) over (rows unbounded preceding) + t_balance as running_balance, compound_array(ARRAY[ARRAY[bac.class_id, bac.bu_id]]) FROM (select id, 'gl' as type, false as invoice, reference, description, approved, null::text as till FROM gl UNION SELECT ar.id, 'ar', invoice, invnumber, e.name, approved, till FROM ar JOIN entity_credit_account eca ON ar.entity_credit_account = eca.id JOIN entity e ON e.id = eca.entity_id UNION SELECT ap.id, 'ap', invoice, invnumber, e.name, approved, null as till FROM ap JOIN entity_credit_account eca ON ap.entity_credit_account = eca.id JOIN entity e ON e.id = eca.entity_id) g JOIN acc_trans ac ON ac.trans_id = g.id JOIN account c ON ac.chart_id = c.id LEFT JOIN business_unit_ac bac ON ac.entry_id = bac.entry_id LEFT JOIN bu_tree ON bac.bu_id = bu_tree.id WHERE (g.reference ilike in_reference || '%' or in_reference is null) AND (c.accno = in_accno OR in_accno IS NULL) AND (ac.source ilike '%' || in_source || '%' OR in_source is null) AND (ac.memo ilike '%' || in_memo || '%' OR in_memo is null) AND (in_description IS NULL OR g.description @@ plainto_tsquery(get_default_lang()::regconfig, in_description)) AND (transdate BETWEEN in_from_date AND in_to_date OR (transdate >= in_from_date AND in_to_date IS NULL) OR (transdate <= in_to_date AND in_from_date IS NULL) OR (in_to_date IS NULL AND in_from_date IS NULL)) AND (in_approved is false OR (g.approved AND ac.approved)) AND (in_from_amount IS NULL OR ac.amount >= in_from_amount) AND (in_to_amount IS NULL OR ac.amount <= in_to_amount) AND (in_category = c.category OR in_category IS NULL) GROUP BY g.id, g.type, g.invoice, g.reference, g.description, ac.transdate, ac.source, ac.amount, c.accno, c.gifi_accno, g.till, ac.cleared, ac.memo, c.description, ac.chart_id, ac.entry_id, ac.trans_id HAVING in_business_units is null or in_business_units <@ compound_array(string_to_array(bu_tree.path, ',')::int[]) ORDER BY ac.transdate, ac.trans_id, c.accno LOOP RETURN NEXT retval; END LOOP; END;
DECLARE item report_aging_item; BEGIN FOR item IN WITH RECURSIVE bu_tree (id, path) AS ( SELECT id, id::text AS path FROM business_unit WHERE id = any(in_business_units) UNION SELECT bu.id, bu_tree.path || ',' || bu.id FROM business_unit bu JOIN bu_tree ON bu_tree.id = bu.parent_id ) SELECT c.entity_id, c.meta_number, e.name, e.name as contact_name, a.invnumber, a.transdate, a.till, a.ordnumber, a.ponumber, a.notes, CASE WHEN a.age/30 = 0 THEN (a.sign * sum(ac.amount)) ELSE 0 END as c0, CASE WHEN a.age/30 = 1 THEN (a.sign * sum(ac.amount)) ELSE 0 END as c30, CASE WHEN a.age/30 = 2 THEN (a.sign * sum(ac.amount)) ELSE 0 END as c60, CASE WHEN a.age/30 > 2 THEN (a.sign * sum(ac.amount)) ELSE 0 END as c90, a.duedate, a.id, a.curr, COALESCE((SELECT sell FROM exchangerate ex WHERE a.curr = ex.curr AND ex.transdate = a.transdate), 1) AS exchangerate, (SELECT compound_array(ARRAY[[p.partnumber, i.description, i.qty::text]]) FROM parts p JOIN invoice i ON (i.parts_id = p.id) WHERE i.trans_id = a.id) AS line_items, (coalesce(in_to_date, now())::date - a.transdate) as age FROM (select id, invnumber, till, ordnumber, amount, duedate, curr, ponumber, notes, entity_credit_account, -1 AS sign, transdate, CASE WHEN in_use_duedate THEN coalesce(in_to_date, now())::date - duedate ELSE coalesce(in_to_date, now())::date - transdate END as age FROM ar WHERE in_entity_class = 2 UNION SELECT id, invnumber, null, ordnumber, amount, duedate, curr, ponumber, notes, entity_credit_account, 1 as sign, transdate, CASE WHEN in_use_duedate THEN coalesce(in_to_date, now())::date - duedate ELSE coalesce(in_to_date, now())::date - transdate END as age FROM ap WHERE in_entity_class = 1) a JOIN acc_trans ac ON ac.trans_id = a.id JOIN account acc ON ac.chart_id = acc.id JOIN account_link acl ON acl.account_id = acc.id AND ((in_entity_class = 1 AND acl.description = 'AP') OR (in_entity_class = 2 AND acl.description = 'AR')) JOIN entity_credit_account c ON a.entity_credit_account = c.id JOIN entity e ON (e.id = c.entity_id) LEFT JOIN business_unit_ac buac ON ac.entry_id = buac.entry_id LEFT JOIN bu_tree ON buac.bu_id = bu_tree.id LEFT JOIN entity_to_location e2l ON e.id = e2l.entity_id AND e2l.location_class = 3 LEFT JOIN location l ON l.id = e2l.location_id LEFT JOIN country ON (country.id = l.country_id) WHERE (e.id = in_entity_id OR in_entity_id IS NULL) AND (in_accno IS NULL or acc.accno = in_accno) GROUP BY c.entity_id, c.meta_number, e.name, l.line_one, l.line_two, l.line_three, l.city, l.state, l.mail_code, country.name, a.invnumber, a.transdate, a.till, a.ordnumber, a.ponumber, a.notes, a.amount, a.sign, a.duedate, a.id, a.curr, a.age HAVING in_business_units is null or in_business_units <@ compound_array(string_to_array(bu_tree.path, ',')::int[]) ORDER BY entity_id, curr, transdate, invnumber LOOP return next item; END LOOP; END;
SELECT entity_id, account_number, name, contact_name, null::text, null::date, null::text, null::text, null::text, null::text, sum(c0), sum(c30), sum(c60), sum(c90), null::date, null::int, curr, null::numeric, null::text[], null::int FROM report__invoice_aging_detail($1, $2, $3, $4, $5, $6) GROUP BY entity_id, account_number, name, contact_name, curr ORDER BY account_number
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;
Saves tax form information. Returns true or raises exception.
BEGIN INSERT INTO country_tax_form(country_id, form_name) values (in_country_code, in_taxform_name); RETURN true; END;
Returns a session row. If no session exists, it returns null
DECLARE out_row session%ROWTYPE; BEGIN DELETE FROM session WHERE last_used < now() - coalesce((SELECT value FROM defaults WHERE setting_key = 'session_timeout')::interval, '90 minutes'::interval); UPDATE session SET last_used = now() WHERE session_id = in_session_id AND token = in_token AND users_id = (select id from users where username = SESSION_USER); IF FOUND THEN SELECT * INTO out_row FROM session WHERE session_id = in_session_id; ELSE DELETE FROM SESSION WHERE users_id IN (select id from users where username = SESSION_USER); -- the above query also releases all discretionary locks by the -- session PERFORM * FROM defaults WHERE setting_key = 'auto_logout' and value = '1'; IF FOUND THEN RAISE NOTICE 'auto logout'; RETURN NULL; ELSE INSERT INTO session (users_id, token) SELECT id, md5(random()::text) FROM users WHERE username = SESSION_USER; SELECT * INTO out_row FROM SESSION WHERE users_id = (select id from users where username = SESSION_USER); RETURN out_row; END IF; END IF; RETURN out_row; END;
Returns an array of currencies from the defaults table.
SELECT string_to_array(value, ':') from defaults where setting_key = 'curr';
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;
Returns the value of the setting in the defaults table.
SELECT * FROM defaults WHERE setting_key = $1;
Returns a set of settings for default accounts.
DECLARE account defaults%ROWTYPE; BEGIN FOR account IN SELECT * FROM defaults WHERE setting_key like '%accno_id' ORDER BY setting_key LOOP RETURN NEXT account; END LOOP; END;
This function takes a value for a sequence in the defaults table and increments it. Leading zeroes and spaces are preserved as placeholders. Currently <?lsmb parsing is not supported in this routine though it may be added at a later date.
DECLARE base_value VARCHAR; raw_value VARCHAR; increment INTEGER; inc_length INTEGER; new_value VARCHAR; BEGIN SELECT value INTO raw_value FROM defaults WHERE setting_key = in_key FOR UPDATE; SELECT substring(raw_value from '(' || E'\\' || 'd*)(' || E'\\' || 'D*|<' || E'\\' || '?lsmb [^<>] ' || E'\\' || '?>)*$') INTO base_value; IF base_value like '0%' THEN increment := base_value::integer + 1; SELECT char_length(increment::text) INTO inc_length; SELECT overlay(base_value placing increment::varchar from (select char_length(base_value) - inc_length + 1) for inc_length) INTO new_value; ELSE new_value := base_value::integer + 1; END IF; SELECT regexp_replace(raw_value, base_value, new_value) INTO new_value; UPDATE defaults SET value = new_value WHERE setting_key = in_key; return new_value; END;
Retrieves specified tax form information from the database.
SELECT * FROM country_tax_form where id = $1;
Returns a set of all tax forms, ordered by country_id and id
SELECT * FROM country_tax_form ORDER BY country_id, id;
Returns a list of tax forms with an added field, country_name, to specify the name of the country.
SELECT t.id, t.form_name, t.country_id, c.name, t.default_reportable FROM country_tax_form t JOIN country c ON c.id = t.country_id ORDER BY c.name, t.form_name;
Saves tax form information to the database.
BEGIN UPDATE country_tax_form SET country_id = in_country_id, form_name =in_form_name, default_reportable = coalesce(in_default_reportable,false) WHERE id = in_id; IF FOUND THEN RETURN in_id; END IF; insert into country_tax_form(country_id,form_name, default_reportable) values (in_country_id, in_form_name, coalesce(in_default_reportable, false)); RETURN currval('country_tax_form_id_seq'); END;
This provides a list of invoices and transactions that a report hits. This is intended to allow an organization to adjust what is reported on the 1099 before printing them.
DECLARE out_row tax_form_report_detail_item; BEGIN FOR out_row IN SELECT entity_credit_account.id, company.legal_name, company.entity_id, entity_credit_account.entity_class, entity.control_code, entity_credit_account.meta_number, sum(CASE WHEN gl.amount = 0 then 0 when relation = 'acc_trans' THEN ac.reportable_amount * pmt.amount / gl.amount ELSE 0 END * CASE WHEN gl.class = 'ap' THEN -1 else 1 end), sum(CASE WHEN gl.amount = 0 then 0 WHEN relation = 'invoice' THEN ac.reportable_amount * pmt.amount / gl.amount ELSE 0 END * CASE WHEN gl.class = 'ar' THEN -1 else 1 end), SUM(CASE WHEN gl.amount = 0 THEN 0 ELSE ac.reportable_amount * pmt.amount / gl.amount END * CASE WHEN gl.class = 'ap' THEN -1 else 1 end * CASE WHEN relation = 'invoice' THEN -1 ELSE 1 END), gl.invnumber, gl.duedate::text, gl.id FROM (select id, entity_credit_account, invnumber, duedate, amount, transdate, 'ar' as class FROM ar UNION select id, entity_credit_account, invnumber, duedate, amount, transdate, 'ap' as class FROM ap ) gl JOIN (select trans_id, 'acc_trans' as relation, sum(amount) as amount, sum(case when atf.reportable then amount else 0 end) as reportable_amount FROM acc_trans LEFT JOIN ac_tax_form atf ON (acc_trans.entry_id = atf.entry_id) GROUP BY trans_id UNION select trans_id, 'invoice' as relation, sum(sellprice * qty) as amount, sum(case when itf.reportable then sellprice * qty else 0 end) as reportable_amount FROM invoice LEFT JOIN invoice_tax_form itf ON (invoice.id = itf.invoice_id) GROUP BY trans_id ) ac ON (ac.trans_id = gl.id) JOIN entity_credit_account ON (gl.entity_credit_account = entity_credit_account.id) JOIN entity ON (entity.id = entity_credit_account.entity_id) JOIN company ON (entity.id = company.entity_id) JOIN country_tax_form ON (entity_credit_account.taxform_id = country_tax_form.id) JOIN (SELECT ac.trans_id, sum(ac.amount) as amount, as_array(entry_id) as entry_ids, as_array(chart_id) as chart_ids, count(*) as num FROM acc_trans ac where chart_id in (select account_id from account_link where description like '%paid') AND transdate BETWEEN in_begin AND in_end group by ac.trans_id ) pmt ON (pmt.trans_id = gl.id) WHERE country_tax_form.id = in_tax_form_id AND meta_number = in_meta_number GROUP BY legal_name, meta_number, company.entity_id, entity_credit_account.entity_class, entity.control_code, gl.invnumber, gl.duedate, gl.id, entity_credit_account.id LOOP RETURN NEXT out_row; END LOOP; END;
This provides the total reportable value per vendor. As per 1099 forms, these are cash-basis documents and show amounts paid.
DECLARE out_row tax_form_report_item; BEGIN FOR out_row IN SELECT entity_credit_account.id, company.legal_name, company.entity_id, entity_credit_account.entity_class, entity.control_code, entity_credit_account.meta_number, sum(CASE WHEN gl.amount = 0 THEN 0 WHEN relation = 'acc_trans' THEN ac.reportable_amount * pmt.amount / gl.amount ELSE 0 END * CASE WHEN gl.class = 'ap' THEN -1 else 1 end), sum(CASE WHEN gl.amount = 0 THEN 0 WHEN relation = 'invoice' THEN ac.reportable_amount * pmt.amount / gl.amount ELSE 0 END * CASE WHEN gl.class = 'ar' THEN -1 else 1 end), sum(CASE WHEN gl.amount = 0 THEN 0 ELSE ac.reportable_amount * pmt.amount / gl.amount END * CASE WHEN gl.class = 'ap' THEN -1 else 1 end * CASE WHEN ac.relation = 'invoice' then -1 else 1 end) FROM (select id, transdate, entity_credit_account, invoice, amount, 'ar' as class FROM ar UNION select id, transdate, entity_credit_account, invoice, amount, 'ap' as class from ap ) gl JOIN (select trans_id, 'acc_trans' as relation, sum(amount) as amount, sum(case when atf.reportable then amount else 0 end) as reportable_amount FROM acc_trans LEFT JOIN ac_tax_form atf ON (acc_trans.entry_id = atf.entry_id) GROUP BY trans_id UNION select trans_id, 'invoice' as relation, sum(sellprice * qty) as amount, sum(case when itf.reportable then sellprice * qty else 0 end) as reportable_amount FROM invoice LEFT JOIN invoice_tax_form itf ON (invoice.id = itf.invoice_id) GROUP BY trans_id ) ac ON (ac.trans_id = gl.id AND ((gl.invoice is true and ac.relation='invoice') OR (gl.invoice is false and ac.relation='acc_trans'))) JOIN (SELECT ac.trans_id, sum(ac.amount) as amount, as_array(entry_id) as entry_ids, as_array(chart_id) as chart_ids, count(*) as num FROM acc_trans ac where chart_id in (select account_id from account_link where description like '%paid') AND transdate BETWEEN in_begin AND in_end group by ac.trans_id ) pmt ON (pmt.trans_id = gl.id) JOIN entity_credit_account ON (gl.entity_credit_account = entity_credit_account.id) JOIN entity ON (entity.id = entity_credit_account.entity_id) JOIN company ON (entity.id = company.entity_id) JOIN country_tax_form ON (entity_credit_account.taxform_id = country_tax_form.id) WHERE country_tax_form.id = in_tax_form_id GROUP BY legal_name, meta_number, company.entity_id, entity_credit_account.entity_class, entity.control_code, entity_credit_account.id LOOP RETURN NEXT out_row; END LOOP; END;
DECLARE r_eclass entity_class; roll_pfx text; BEGIN IF TG_OP = 'DELETE' THEN RETURN OLD; ELSE SELECT value INTO roll_pfx FROM defaults WHERE setting_key = 'roll_prefix'; SELECT * INTO r_eclass from entity_class WHERE id = NEW.entity_class; IF pg_has_role(SESSION_USER, coalesce(roll_pfx, 'lsmb_' || current_database() || '__') || 'contact_class_' || lower(regexp_replace( r_eclass.class, ' ', '_')), 'USAGE') THEN RETURN NEW; ELSE RAISE EXCEPTION 'Access Denied for class'; END IF; END IF; END;
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;
Turns a setof ARRAY[key,value] into an ARRAY[key||'='||value, key||'='||value,...]
aggregate_dummy
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;
BEGIN IF NEW.onhand >= NEW.rop THEN NOTIFY parts_short; END IF; RETURN NEW; END;
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;
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;
Alloes a user to change his or her own password. The password is set to expire setting_get('password_duration') days after the password change.
DECLARE t_expires timestamp; t_password_duration text; BEGIN SELECT value INTO t_password_duration FROM defaults WHERE setting_key = 'password_duration'; IF t_password_duration IS NULL or t_password_duration='' THEN t_expires := 'infinity'; ELSE t_expires := now() + (t_password_duration::numeric::text || ' days')::interval; END IF; UPDATE users SET notify_password = DEFAULT where username = SESSION_USER; EXECUTE 'ALTER USER ' || quote_ident(SESSION_USER) || ' with ENCRYPTED password ' || quote_literal(in_new_password) || ' VALID UNTIL '|| quote_literal(t_expires); return 1; END;
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;
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';
select * from user_listable;
Returns the preferences row for the user.
declare v_row user_preference; BEGIN select * into v_row from user_preference where id = in_user_id; IF NOT FOUND THEN RAISE EXCEPTION 'Could not find user preferences for id %', in_user_id; ELSE return next v_row; END IF; END;
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;
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;
Retrieves a list of vouchers and amounts attached to the batch.
declare voucher_item record; BEGIN FOR voucher_item IN SELECT v.id, a.invoice, a.invnumber, e.name, v.batch_id, v.trans_id, a.amount, a.transdate, 'Payable' FROM voucher v JOIN ap a ON (v.trans_id = a.id) JOIN entity_credit_account eca ON (eca.id = a.entity_credit_account) JOIN entity e ON (eca.entity_id = e.id) WHERE v.batch_id = in_batch_id AND v.batch_class = (select id from batch_class WHERE class = 'ap') UNION SELECT v.id, a.invoice, a.invnumber, e.name, v.batch_id, v.trans_id, a.amount, a.transdate, 'Receivable' FROM voucher v JOIN ar a ON (v.trans_id = a.id) JOIN entity_credit_account eca ON (eca.id = a.entity_credit_account) JOIN entity e ON (eca.entity_id = e.id) WHERE v.batch_id = in_batch_id AND v.batch_class = (select id from batch_class WHERE class = 'ar') UNION ALL -- TODO: Add the class labels to the class table. SELECT v.id, false, a.source, cr.meta_number || '--' || co.legal_name , v.batch_id, v.trans_id, sum(CASE WHEN bc.class LIKE 'payment%' THEN a.amount * -1 ELSE a.amount END), a.transdate, CASE WHEN bc.class = 'payment' THEN 'Payment' WHEN bc.class = 'payment_reversal' THEN 'Payment Reversal' END FROM voucher v JOIN acc_trans a ON (v.id = a.voucher_id) JOIN batch_class bc ON (bc.id = v.batch_class) JOIN chart c ON (a.chart_id = c.id) JOIN ap ON (ap.id = a.trans_id) JOIN entity_credit_account cr ON (ap.entity_credit_account = cr.id) JOIN company co ON (cr.entity_id = co.entity_id) WHERE v.batch_id = in_batch_id AND a.voucher_id = v.id AND (bc.class like 'payment%' AND c.link = 'AP') GROUP BY v.id, a.source, cr.meta_number, co.legal_name , v.batch_id, v.trans_id, a.transdate, bc.class UNION ALL SELECT v.id, false, a.source, a.memo, v.batch_id, v.trans_id, CASE WHEN bc.class LIKE 'receipt%' THEN sum(a.amount) * -1 ELSE sum(a.amount) END, a.transdate, CASE WHEN bc.class = 'receipt' THEN 'Receipt' WHEN bc.class = 'receipt_reversal' THEN 'Receipt Reversal' END FROM voucher v JOIN acc_trans a ON (v.id = a.voucher_id) JOIN batch_class bc ON (bc.id = v.batch_class) JOIN chart c ON (a.chart_id = c.id) JOIN ar ON (ar.id = a.trans_id) JOIN entity_credit_account cr ON (ar.entity_credit_account = cr.id) JOIN company co ON (cr.entity_id = co.entity_id) WHERE v.batch_id = in_batch_id AND a.voucher_id = v.id AND (bc.class like 'receipt%' AND c.link = 'AR') GROUP BY v.id, a.source, cr.meta_number, co.legal_name , a.memo, v.batch_id, v.trans_id, a.transdate, bc.class UNION ALL SELECT v.id, false, g.reference, g.description, v.batch_id, v.trans_id, sum(a.amount), g.transdate, 'GL' FROM voucher v JOIN gl g ON (g.id = v.trans_id) JOIN acc_trans a ON (v.trans_id = a.trans_id) WHERE a.amount > 0 AND v.batch_id = in_batch_id AND v.batch_class IN (select id from batch_class where class = 'gl') GROUP BY v.id, g.reference, g.description, v.batch_id, v.trans_id, g.transdate ORDER BY 7, 1 LOOP RETURN NEXT voucher_item; END LOOP; END;
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;
SELECT * FROM warehouse order by description;
Mapping journal_line to country_tax_form for reporting purposes.
F-Key | Name | Type | Description |
---|---|---|---|
public.acc_trans.entry_id | entry_id | integer | PRIMARY KEY |
reportable | boolean |
This table stores line items for financial transactions. Please note that payments in 1.3 are not full-fledged transactions.
F-Key | Name | Type | Description |
---|---|---|---|
public.transactions.id | trans_id | integer | NOT NULL |
public.account.id | chart_id | integer | NOT NULL |
amount | numeric | NOT NULL | |
transdate | date | DEFAULT ('now'::text)::date | |
source | text |
Document Source identifier for individual line items, usually used for payments. |
|
cleared | boolean | DEFAULT false | |
fx_transaction | boolean | DEFAULT false | |
memo | text | ||
invoice_id | integer | ||
approved | boolean | DEFAULT true | |
cleared_on | date | ||
reconciled_on | date | ||
public.voucher.id | voucher_id | integer | |
entry_id | serial | PRIMARY KEY |
Tables referencing this one via Foreign Key Constraints:
ac_transdate_year_idx date_part('YEAR'::text, transdate) acc_trans_voucher_id_idx voucher_idThis table stores the main account info.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | UNIQUE NOT NULL | |
accno | text | PRIMARY KEY | |
description | text | ||
is_temp | boolean |
NOT NULL
DEFAULT false
Only affects equity accounts. If set, close at end of year. |
|
category | character(1) | NOT NULL | |
gifi_accno | text | ||
public.account_heading.id | heading | integer | NOT NULL |
contra | boolean | NOT NULL DEFAULT false | |
tax | boolean | NOT NULL DEFAULT false | |
obsolete | boolean | NOT NULL DEFAULT false |
Tables referencing this one via Foreign Key Constraints:
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.
F-Key | Name | Type | Description |
---|---|---|---|
end_date | date | PRIMARY KEY | |
public.account.id | account_id | integer | PRIMARY KEY |
amount | numeric | NOT NULL | |
id | serial | UNIQUE NOT NULL | |
debits | numeric | ||
credits | numeric |
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.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | UNIQUE NOT NULL | |
accno | text | PRIMARY KEY | |
public.account_heading.id | parent_id | integer | |
description | text |
Tables referencing this one via Foreign Key Constraints:
F-Key | Name | Type | Description |
---|---|---|---|
public.account.id | account_id | integer | PRIMARY KEY |
public.account_link_description.description | description | text | PRIMARY KEY |
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.
F-Key | Name | Type | Description |
---|---|---|---|
description | text | PRIMARY KEY | |
summary | boolean | NOT NULL | |
custom | boolean | NOT NULL |
Tables referencing this one via Foreign Key Constraints:
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
F-Key | Name | Type | Description |
---|---|---|---|
public.transactions.id | id | integer | PRIMARY KEY DEFAULT nextval('id'::regclass) |
invnumber | text |
Text identifier for the invoice. Must be unique. |
|
transdate | date | DEFAULT ('now'::text)::date | |
public.entity.id | entity_id | integer | |
taxincluded | boolean | DEFAULT false | |
amount | numeric |
This stores the total amount (including taxes) for the transaction. |
|
netamount | numeric |
Total amount excluding taxes for the transaction. |
|
paid | numeric | ||
datepaid | date | ||
duedate | date | ||
invoice | boolean |
DEFAULT false
True if the transaction tracks goods/services purchase using the invoice table. False otherwise. |
|
ordnumber | text |
Order Number |
|
curr | character(3) |
3 letters to identify the currency. |
|
notes | text |
These notes are displayed on the invoice when printed or emailed |
|
public.entity_employee.entity_id | person_id | integer |
Person who created the transaction |
till | character varying(20) | ||
quonumber | text |
Quotation Number |
|
intnotes | text |
These notes are not displayed when the invoice is printed or emailed and may be updated without reposting hte invocie. |
|
shipvia | text | ||
language_code | character varying(6) | ||
ponumber | text |
Purchase Order Number |
|
shippingpoint | text | ||
on_hold | boolean | DEFAULT false | |
approved | boolean |
DEFAULT true
Only show in financial reports if true. |
|
reverse | boolean |
DEFAULT false
If true numbers are displayed after multiplying by -1 |
|
terms | smallint | ||
description | text | ||
force_closed | boolean |
Not exposed to the UI, but can be set to prevent an invoice from showing up for payment or in outstanding reports. |
|
public.entity_credit_account.id | entity_credit_account | integer |
NOT NULL
reference for the vendor account used. |
Name | Constraint |
---|---|
ap_check | CHECK ((((amount IS NULL) AND (curr IS NULL)) OR ((amount IS NOT NULL) AND (curr IS NOT NULL)))) |
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
F-Key | Name | Type | Description |
---|---|---|---|
public.transactions.id | id | integer | PRIMARY KEY DEFAULT nextval('id'::regclass) |
invnumber | text |
UNIQUE
Text identifier for the invoice. Must be unique. |
|
transdate | date | DEFAULT ('now'::text)::date | |
public.entity.id | entity_id | integer | |
taxincluded | boolean | ||
amount | numeric |
This stores the total amount (including taxes) for the transaction. |
|
netamount | numeric |
Total amount excluding taxes for the transaction. |
|
paid | numeric | ||
datepaid | date | ||
duedate | date | ||
invoice | boolean |
DEFAULT false
True if the transaction tracks goods/services purchase using the invoice table. False otherwise. |
|
shippingpoint | text | ||
terms | smallint | ||
notes | text |
These notes are displayed on the invoice when printed or emailed |
|
curr | character(3) |
3 letters to identify the currency. |
|
ordnumber | text |
Order Number |
|
public.entity_employee.entity_id | person_id | integer |
Person who created the transaction |
till | character varying(20) | ||
quonumber | text |
Quotation Number |
|
intnotes | text |
These notes are not displayed when the invoice is printed or emailed and may be updated without reposting hte invocie. |
|
shipvia | text | ||
language_code | character varying(6) | ||
ponumber | text |
Purchase Order Number |
|
on_hold | boolean | DEFAULT false | |
reverse | boolean |
DEFAULT false
If true numbers are displayed after multiplying by -1 |
|
approved | boolean |
DEFAULT true
Only show in financial reports if true. |
|
public.entity_credit_account.id | entity_credit_account | integer |
NOT NULL
reference for the customer account used. |
force_closed | boolean |
Not exposed to the UI, but can be set to prevent an invoice from showing up for payment or in outstanding reports. |
|
description | text | ||
is_return | boolean | DEFAULT false |
Name | Constraint |
---|---|
ar_check | CHECK ((((amount IS NULL) AND (curr IS NULL)) OR ((amount IS NOT NULL) AND (curr IS NOT NULL)))) |
Holds mapping for parts that are members of assemblies.
F-Key | Name | Type | Description |
---|---|---|---|
public.parts.id | id | integer |
PRIMARY KEY
This is the id of the assembly the part is being mapped to. |
public.parts.id | parts_id | integer |
PRIMARY KEY
ID of part that is a member of the assembly. |
qty | numeric | ||
bom | boolean | ||
adj | boolean |
The account fields here set the defaults for the individual asset items. They are non-authoritative.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | UNIQUE NOT NULL | |
label | text | PRIMARY KEY | |
public.account.id | asset_account_id | integer | |
public.account.id | dep_account_id | integer | |
public.asset_dep_method.id | method | integer |
Tables referencing this one via Foreign Key Constraints:
Stores asset depreciation methods, and their relevant stored procedures. The fixed asset system is such depreciation methods can be plugged in via this table.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | UNIQUE NOT NULL | |
method | text |
PRIMARY KEY
These are keyed to specific stored procedures. Currently only "straight_line" is supported |
|
sproc | text |
UNIQUE
NOT NULL
The sproc mentioned here is a stored procedure which must have the following arguments: (in_asset_ids int[], in_report_date date, in_report_id int). Here in_asset_ids are the assets to be depreciated, in_report_date is the date of the report, and in_report_id is the id of the report. The sproc MUST insert the relevant lines into asset_report_line. |
|
unit_label | text | NOT NULL | |
short_name | text | UNIQUE NOT NULL | |
public.asset_unit_class.id | unit_class | integer | NOT NULL |
Tables referencing this one via Foreign Key Constraints:
F-Key | Name | Type | Description |
---|---|---|---|
label | text | PRIMARY KEY | |
id | serial | UNIQUE NOT NULL | |
multiple | integer | ||
short_label | character(1) |
Name | Constraint |
---|---|
asset_disposal_method_multiple_check | CHECK ((multiple = ANY (ARRAY[1, 0, (-1)]))) |
Tables referencing this one via Foreign Key Constraints:
Stores details of asset items. The account fields here are authoritative, while the ones in the asset_class table are defaults.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
description | text | ||
tag | text |
UNIQUE#1
NOT NULL
This can be plugged into other routines to generate it automatically via ALTER TABLE .... SET DEFAULT..... |
|
purchase_value | numeric | ||
salvage_value | numeric | ||
usable_life | numeric | ||
purchase_date | date | NOT NULL | |
start_depreciation | date | NOT NULL | |
public.warehouse.id | location_id | integer | |
public.business_unit.id | department_id | integer | |
public.eca_invoice.journal_id | invoice_id | integer | |
public.account.id | asset_account_id | integer | |
public.account.id | dep_account_id | integer | |
public.account.id | exp_account_id | integer | |
public.asset_item.id | obsolete_by | integer | UNIQUE#1 |
public.asset_class.id | asset_class_id | integer |
Tables referencing this one via Foreign Key Constraints:
F-Key | Name | Type | Description |
---|---|---|---|
id | integer | NOT NULL DEFAULT nextval('note_id_seq'::regclass) | |
note_class | integer | NOT NULL DEFAULT 4 | |
note | text | NOT NULL | |
vector | tsvector | NOT NULL DEFAULT ''::tsvector | |
created | timestamp without time zone | NOT NULL DEFAULT now() | |
created_by | text | DEFAULT "session_user"() | |
public.asset_item.id | ref_key | integer | NOT NULL |
subject | text |
Table public.asset_note Inherits note,
Name | Constraint |
---|---|
asset_note_note_class_check | CHECK ((note_class = 4)) |
Asset reports are discrete sets of depreciation or disposal transctions, and each one may be turned into no more than one GL transaction.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
report_date | date | ||
public.journal_entry.id | gl_id | bigint | UNIQUE |
public.asset_class.id | asset_class | bigint | |
public.asset_report_class.id | report_class | integer | |
public.entity.id | entered_by | bigint | NOT NULL DEFAULT person__get_my_entity_id() |
public.entity.id | approved_by | bigint | |
entered_at | timestamp without time zone | DEFAULT now() | |
approved_at | timestamp without time zone | ||
depreciated_qty | numeric | ||
dont_approve | boolean | DEFAULT false | |
submitted | boolean | NOT NULL DEFAULT false |
Tables referencing this one via Foreign Key Constraints:
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.
F-Key | Name | Type | Description |
---|---|---|---|
id | integer | UNIQUE NOT NULL | |
class | text | PRIMARY KEY |
Tables referencing this one via Foreign Key Constraints:
F-Key | Name | Type | Description |
---|---|---|---|
public.asset_item.id | asset_id | bigint | PRIMARY KEY |
public.asset_report.id | report_id | bigint | PRIMARY KEY |
amount | numeric | ||
public.business_unit.id | department_id | integer |
In case assets are moved between departments, we have to store this here. |
public.warehouse.id | warehouse_id | integer |
Maps disposal method to line items in the asset disposal report.
F-Key | Name | Type | Description |
---|---|---|---|
public.asset_report.id | report_id | integer | PRIMARY KEY |
public.asset_item.id | asset_id | integer | PRIMARY KEY |
public.asset_disposal_method.id | disposal_method_id | integer | PRIMARY KEY |
percent_disposed | numeric |
F-Key | Name | Type | Description |
---|---|---|---|
id | integer | UNIQUE NOT NULL | |
class | text | PRIMARY KEY |
Tables referencing this one via Foreign Key Constraints:
This stores information on who entered or updated rows in the ar, ap, or gl tables.
F-Key | Name | Type | Description |
---|---|---|---|
trans_id | integer | ||
tablename | text | ||
reference | text | ||
formname | text | ||
action | text | ||
transdate | timestamp without time zone | DEFAULT now() | |
public.person.entity_id | person_id | integer | NOT NULL |
entry_id | bigserial | PRIMARY KEY |
Stores batch header info. Batches are groups of vouchers that are posted together.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
public.batch_class.id | batch_class_id | integer |
NOT NULL
Note that this field is largely used for sorting the vouchers. A given batch is NOT restricted to this type. |
control_code | text | NOT NULL | |
description | text | ||
default_date | date | NOT NULL | |
approved_on | date | ||
public.entity_employee.entity_id | approved_by | integer | |
public.entity_employee.entity_id | created_by | integer | |
public.session.session_id | locked_by | integer | |
created_on | date | DEFAULT now() |
Name | Constraint |
---|---|
batch_control_code_check | CHECK ((length(control_code) > 0)) |
Tables referencing this one via Foreign Key Constraints:
These values are hard-coded. Please coordinate before adding standard values. Values from 900 to 999 are reserved for local use.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | UNIQUE NOT NULL | |
class | character varying | PRIMARY KEY |
Tables referencing this one via Foreign Key Constraints:
F-Key | Name | Type | Description |
---|---|---|---|
public.business_unit_class.id | bu_class_id | integer | PRIMARY KEY |
public.lsmb_module.id | module_id | integer | PRIMARY KEY |
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | UNIQUE NOT NULL | |
start_date | date | NOT NULL | |
end_date | date | NOT NULL | |
reference | text | PRIMARY KEY | |
description | text | NOT NULL | |
public.entity.id | entered_by | integer | NOT NULL DEFAULT person__get_my_entity_id() |
public.entity.id | approved_by | integer | |
public.entity.id | obsolete_by | integer | |
entered_at | timestamp without time zone | NOT NULL DEFAULT now() | |
approved_at | timestamp without time zone | ||
obsolete_at | timestamp without time zone |
Name | Constraint |
---|---|
budget_info_check | CHECK ((start_date < end_date)) |
Tables referencing this one via Foreign Key Constraints:
F-Key | Name | Type | Description |
---|---|---|---|
public.budget_info.id | budget_id | integer | PRIMARY KEY |
public.account.id | account_id | integer | PRIMARY KEY |
description | text | ||
amount | numeric | NOT NULL |
F-Key | Name | Type | Description |
---|---|---|---|
id | integer | PRIMARY KEY DEFAULT nextval('note_id_seq'::regclass) | |
note_class | integer | NOT NULL DEFAULT 6 | |
note | text | NOT NULL | |
vector | tsvector | NOT NULL DEFAULT ''::tsvector | |
created | timestamp without time zone | NOT NULL DEFAULT now() | |
created_by | text | DEFAULT "session_user"() | |
public.budget_info.id | ref_key | integer | NOT NULL |
subject | text |
Table public.budget_note Inherits note,
Name | Constraint |
---|---|
budget_note_note_class_check | CHECK ((note_class = 6)) |
F-Key | Name | Type | Description |
---|---|---|---|
public.budget_info.id | budget_id | integer | UNIQUE PRIMARY KEY |
public.business_unit.id | bu_id | integer | NOT NULL |
public.business_unit_class.id | bu_class | integer | PRIMARY KEY |
Groups of Customers assigned joint discounts.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
description | text | ||
discount | numeric |
Tracks Projects, Departments, Funds, Etc.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | UNIQUE#1 PRIMARY KEY | |
public.business_unit_class.id | class_id | integer | UNIQUE#2 UNIQUE#1 NOT NULL |
control_code | text | UNIQUE#2 | |
description | text | ||
start_date | date | ||
end_date | date | ||
public.business_unit.id | parent_id | integer | |
public.entity_credit_account.id | credit_id | integer |
Tables referencing this one via Foreign Key Constraints:
F-Key | Name | Type | Description |
---|---|---|---|
public.acc_trans.entry_id | entry_id | integer | PRIMARY KEY |
public.business_unit_class.id public.business_unit.class_id#1 | class_id | integer | PRIMARY KEY |
public.business_unit.id#1 | bu_id | integer | PRIMARY KEY |
Consolidates projects and departments, and allows this to be extended for funds accounting and other purposes.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | UNIQUE NOT NULL | |
label | text | PRIMARY KEY | |
active | boolean | NOT NULL DEFAULT false | |
ordering | integer |
Tables referencing this one via Foreign Key Constraints:
F-Key | Name | Type | Description |
---|---|---|---|
public.invoice.id | entry_id | integer | PRIMARY KEY |
public.business_unit_class.id public.business_unit.class_id#1 | class_id | integer | PRIMARY KEY |
public.business_unit.id#1 | bu_id | integer | PRIMARY KEY |
F-Key | Name | Type | Description |
---|---|---|---|
public.journal_line.id | entry_id | integer | PRIMARY KEY |
public.business_unit_class.id | bu_class | integer | PRIMARY KEY |
public.business_unit.id | bu_id | integer | NOT NULL |
F-Key | Name | Type | Description |
---|---|---|---|
public.orderitems.id | entry_id | integer | PRIMARY KEY |
public.business_unit_class.id public.business_unit.class_id#1 | class_id | integer | PRIMARY KEY |
public.business_unit.id#1 | bu_id | integer | PRIMARY KEY |
Translation information for projects, departments, etc.
F-Key | Name | Type | Description |
---|---|---|---|
public.business_unit.id | trans_id | integer | PRIMARY KEY |
language_code | character varying(6) | PRIMARY KEY | |
description | text |
Table public.business_unit_translation Inherits translation,
This view is used by cash basis reports to determine the fraction of a transaction to be counted.
F-Key | Name | Type | Description |
---|---|---|---|
id | integer | ||
portion | numeric | ||
rel | text | ||
transdate | date |
( SELECT gl.id , 1::numeric AS portion ,'gl'::text AS rel , gl.transdate FROM gl UNION ALLSELECT gl.id , CASE WHEN (gl.amount = (0)::numeric ) THEN (0)::numeric WHEN (gl.transdate = ac.transdate) THEN ( (1)::numeric + (sum (ac.amount) / gl.amount ) ) ELSE ( (1)::numeric - ( (gl.amount - sum (ac.amount) ) / gl.amount ) ) END AS portion ,'ar'::text AS rel , ac.transdate FROM ( (ar gl JOIN acc_trans ac ON ( (ac.trans_id = gl.id) ) ) JOIN account_link al ON ( ( (ac.chart_id = al.account_id) AND (al.description = 'AR'::text) ) ) ) GROUP BY gl.id , gl.amount , ac.transdate ) UNION ALLSELECT gl.id , CASE WHEN (gl.amount = (0)::numeric ) THEN (0)::numeric WHEN (gl.transdate = ac.transdate) THEN ( (1)::numeric - (sum (ac.amount) / gl.amount ) ) ELSE ( (1)::numeric - ( (gl.amount + sum (ac.amount) ) / gl.amount ) ) END AS portion ,'ap'::text AS rel , ac.transdate FROM ( (ap gl JOIN acc_trans ac ON ( (ac.trans_id = gl.id) ) ) JOIN account_link al ON ( ( (ac.chart_id = al.account_id) AND (al.description = 'AP'::text) ) ) ) GROUP BY gl.id , gl.amount , ac.transdate;
Compatibility chart for 1.2 and earlier.
F-Key | Name | Type | Description |
---|---|---|---|
id | integer | ||
accno | text | ||
description | text | ||
charttype | text | ||
category | bpchar | ||
link | text | ||
account_heading | integer | ||
gifi_accno | text | ||
contra | boolean | ||
tax | boolean |
SELECT account_heading.id , account_heading.accno , account_heading.description ,'H'::text AS charttype , NULL::bpchar AS category , NULL::text AS link , NULL::integer AS account_heading , NULL::text AS gifi_accno , false AS contra , false AS tax FROM account_heading UNIONSELECT c.id , c.accno , c.description ,'A'::text AS charttype , c.category , concat_colon (l.description) AS link , c.heading AS account_heading , c.gifi_accno , c.contra , c.tax FROM (account c LEFT JOIN account_link l ON ( (c.id = l.account_id) ) ) GROUP BY c.id , c.accno , c.description , c.category , c.heading , c.gifi_accno , c.contra , c.tax;
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | UNIQUE NOT NULL | |
public.entity.id | entity_id | integer | PRIMARY KEY |
legal_name | text | PRIMARY KEY | |
tax_id | text |
In the US this would be a EIN. |
|
sales_tax_id | text | ||
license_number | text | ||
sic_code | character varying | ||
created | date | NOT NULL DEFAULT ('now'::text)::date |
Name | Constraint |
---|---|
company_legal_name_check | CHECK ((legal_name ~ '[[:alnum:]_]'::text)) |
Tables referencing this one via Foreign Key Constraints:
Stores type of contact information attached to companies and persons. Please coordinate with others before adding new types.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | UNIQUE NOT NULL | |
class | text | PRIMARY KEY |
Name | Constraint |
---|---|
contact_class_class_check | CHECK ((class ~ '[[:alnum:]_]'::text)) |
Tables referencing this one via Foreign Key Constraints:
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
name | text | NOT NULL | |
short_name | text | NOT NULL | |
itu | text |
The ITU Telecommunication Standardization Sector code for calling internationally. For example, the US is 1, Great Britain is 44 |
Name | Constraint |
---|---|
country_name_check | CHECK ((name ~ '[[:alnum:]_]'::text)) |
country_short_name_check | CHECK ((short_name ~ '[[:alnum:]_]'::text)) |
Tables referencing this one via Foreign Key Constraints:
This table was designed for holding information relating to reportable sales or purchases, such as IRS 1099 forms and international equivalents.
F-Key | Name | Type | Description |
---|---|---|---|
public.country.id | country_id | integer | PRIMARY KEY |
form_name | text | PRIMARY KEY | |
id | serial | UNIQUE NOT NULL | |
default_reportable | boolean | NOT NULL DEFAULT false | |
is_accrual | boolean | NOT NULL DEFAULT false |
Tables referencing this one via Foreign Key Constraints:
Provides name mapping for the cash reconciliation screen.
F-Key | Name | Type | Description |
---|---|---|---|
public.account.id | chart_id | integer | NOT NULL |
account | text | NOT NULL |
This table holds header data for cash reports.
F-Key | Name | Type | Description |
---|---|---|---|
id | bigserial | PRIMARY KEY | |
public.account.id | chart_id | integer | NOT NULL |
their_total | numeric | NOT NULL | |
approved | boolean | NOT NULL DEFAULT false | |
submitted | boolean | NOT NULL DEFAULT false | |
end_date | date | NOT NULL DEFAULT now() | |
updated | timestamp without time zone | NOT NULL DEFAULT now() | |
public.entity.id | entered_by | integer | NOT NULL DEFAULT person__get_my_entity_id() |
entered_username | text | NOT NULL DEFAULT "session_user"() | |
deleted | boolean | NOT NULL DEFAULT false | |
public.entity.id | deleted_by | integer | |
public.entity.id | approved_by | integer | |
approved_username | text | ||
recon_fx | boolean | DEFAULT false |
Name | Constraint |
---|---|
cr_report_check | CHECK (((deleted IS NOT TRUE) OR (approved IS NOT TRUE))) |
Tables referencing this one via Foreign Key Constraints:
This stores line item data on transaction lines and whether they are cleared.
F-Key | Name | Type | Description |
---|---|---|---|
id | bigserial | PRIMARY KEY | |
public.cr_report.id | report_id | integer | NOT NULL |
scn | text |
This is the check number. Maps to journal_entry.reference |
|
their_balance | numeric | ||
our_balance | numeric | ||
errorcode | integer | ||
public.entity.id | user | integer | NOT NULL |
clear_time | date | ||
insert_time | timestamp with time zone | NOT NULL DEFAULT now() | |
trans_type | text | ||
post_date | date | ||
public.acc_trans.entry_id | ledger_id | integer | |
voucher_id | integer | ||
overlook | boolean | NOT NULL DEFAULT false | |
cleared | boolean | NOT NULL DEFAULT false |
Deprecated, use only with old code.
F-Key | Name | Type | Description |
---|---|---|---|
field_id | serial | PRIMARY KEY | |
public.custom_table_catalog.table_id | table_id | integer | |
field_name | text |
Deprecated, use only with old code.
F-Key | Name | Type | Description |
---|---|---|---|
table_id | serial | PRIMARY KEY | |
extends | text | ||
table_name | text |
Tables referencing this one via Foreign Key Constraints:
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.
F-Key | Name | Type | Description |
---|---|---|---|
setting_key | text | PRIMARY KEY | |
value | text |
Replaces the rest of the ar and ap tables. Also tracks payments and receipts.
F-Key | Name | Type | Description |
---|---|---|---|
order_id | integer |
Link to order it was created from |
|
public.journal_entry.id | journal_id | integer | PRIMARY KEY |
on_hold | boolean |
DEFAULT false
On hold invoices can not be paid, and overpayments that are on hold cannot be used to pay invoices. |
|
reverse | boolean |
DEFAULT false
When this is set to true, the invoice is shown with opposite normal numbers, i.e. negatives appear as positives, and positives appear as negatives. |
|
public.entity_credit_account.id | credit_id | integer | NOT NULL |
due | date | NOT NULL | |
public.language.code | language_code | character(6) | |
force_closed | boolean |
NOT NULL
DEFAULT false
When this is set to true, the invoice does not show up on outstanding reports and cannot be paid. Overpayments where this is set to true do not appear on outstanding reports and cannot be paid. |
|
order_number | text |
This is the order number of the other party. So for a sales invoice, this would be a purchase order, and for a vendor invoice, this would be a sales order. |
Tables referencing this one via Foreign Key Constraints:
Notes for entity_credit_account entries.
F-Key | Name | Type | Description |
---|---|---|---|
id | integer | PRIMARY KEY DEFAULT nextval('note_id_seq'::regclass) | |
note_class | integer | NOT NULL | |
note | text | NOT NULL | |
vector | tsvector | NOT NULL DEFAULT ''::tsvector | |
created | timestamp without time zone | NOT NULL DEFAULT now() | |
created_by | text | DEFAULT "session_user"() | |
public.entity_credit_account.id | ref_key | integer |
NOT NULL
references entity_credit_account.id |
subject | text |
Table public.eca_note Inherits note,
Name | Constraint |
---|---|
eca_note_note_class_check | CHECK ((note_class = 3)) |
Mapping customers and vendors to taxes.
F-Key | Name | Type | Description |
---|---|---|---|
public.entity_credit_account.id | eca_id | integer | PRIMARY KEY |
public.account.id | chart_id | integer | PRIMARY KEY |
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.
F-Key | Name | Type | Description |
---|---|---|---|
public.entity_credit_account.id | credit_id | integer | PRIMARY KEY |
public.contact_class.id | contact_class_id | integer | PRIMARY KEY |
contact | text | PRIMARY KEY | |
description | text |
Name | Constraint |
---|---|
eca_to_contact_contact_check | CHECK ((contact ~ '[[:alnum:]_]'::text)) |
This table is used for locations bound to contracts. For generic contact addresses, use entity_to_location instead
F-Key | Name | Type | Description |
---|---|---|---|
public.location.id | location_id | integer | PRIMARY KEY |
public.location_class.id | location_class | integer | PRIMARY KEY |
public.entity_credit_account.id | credit_id | integer | PRIMARY KEY |
F-Key | Name | Type | Description |
---|---|---|---|
label | text | PRIMARY KEY | |
id | serial | UNIQUE NOT NULL |
Tables referencing this one via Foreign Key Constraints:
F-Key | Name | Type | Description |
---|---|---|---|
entity_id | integer | ||
startdate | date | ||
enddate | date | ||
role | character varying(20) | ||
ssn | text | ||
sales | boolean | ||
manager_id | integer | ||
employeenumber | character varying(32) | ||
dob | date | ||
manager | text | ||
note | text | ||
name | text |
SELECT e.entity_id , e.startdate , e.enddate , e.role , e.ssn , e.sales , e.manager_id , e.employeenumber , e.dob , em.name AS manager , emn.note , en.name FROM ( ( ( (entity_employee e LEFT JOIN entity en ON ( (e.entity_id = en.id) ) ) LEFT JOIN entity_employee m ON ( (e.manager_id = m.entity_id) ) ) LEFT JOIN entity em ON ( (em.id = m.entity_id) ) ) LEFT JOIN entity_note emn ON ( (emn.ref_key = em.id) ) );
F-Key | Name | Type | Description |
---|---|---|---|
public.entity_employee.entity_id | employee_id | integer | PRIMARY KEY |
public.employee_class.id | ec_id | integer |
F-Key | Name | Type | Description |
---|---|---|---|
salutation | text | ||
first_name | text | ||
last_name | text | ||
entity_id | integer | ||
startdate | date | ||
enddate | date | ||
role | character varying(20) | ||
ssn | text | ||
sales | boolean | ||
manager_id | integer | ||
employeenumber | character varying(32) | ||
dob | date |
SELECT s.salutation , p.first_name , p.last_name , ee.entity_id , ee.startdate , ee.enddate , ee.role , ee.ssn , ee.sales , ee.manager_id , ee.employeenumber , ee.dob FROM ( (person p JOIN entity_employee ee USING (entity_id) ) LEFT JOIN salutation s ON ( (p.salutation_id = s.id) ) );
The primary entity table to map to all contacts
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | UNIQUE NOT NULL | |
name | text |
This is the common name of an entity. If it was a person it may be Joshua Drake, a company Acme Corp. You may also choose to use a domain such as commandprompt.com |
|
public.entity_class.id public.entity_class.id | entity_class | integer | PRIMARY KEY |
created | date | NOT NULL DEFAULT ('now'::text)::date | |
control_code | text | UNIQUE PRIMARY KEY DEFAULT setting_increment('entity_control'::character varying) | |
public.country.id | country_id | integer | NOT NULL |
Name | Constraint |
---|---|
entity_name_check | CHECK ((name ~ '[[:alnum:]_]'::text)) |
Tables referencing this one via Foreign Key Constraints:
This stores bank account information for both companies and persons.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | UNIQUE NOT NULL | |
public.entity.id | entity_id | integer | PRIMARY KEY |
bic | character varying |
PRIMARY KEY
Banking Institution Code, such as routing number of SWIFT code. |
|
iban | character varying |
PRIMARY KEY
International Bank Account Number. used to store the actual account number for the banking institution. |
Tables referencing this one via Foreign Key Constraints:
Defines the class type such as vendor, customer, contact, employee
F-Key | Name | Type | Description |
---|---|---|---|
id | serial |
PRIMARY KEY
The first 7 values are reserved and permanent. Individuals who create new classes, however, should coordinate with others for ranges to use. |
|
class | text | NOT NULL | |
public.country.id | country_id | integer | |
active | boolean | NOT NULL DEFAULT true |
Name | Constraint |
---|---|
entity_class_class_check | CHECK ((class ~ '[[:alnum:]_]'::text)) |
Tables referencing this one via Foreign Key Constraints:
entity_class_idx lower(class)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.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | UNIQUE NOT NULL | |
public.entity.id | entity_id | integer | PRIMARY KEY |
public.entity_class.id | entity_class | integer | PRIMARY KEY |
pay_to_name | text | ||
discount | numeric | ||
description | text | ||
discount_terms | integer | ||
public.account.id | discount_account_id | integer | |
taxincluded | boolean | DEFAULT false | |
creditlimit | numeric | ||
terms | smallint | ||
meta_number | character varying(32) |
PRIMARY KEY
This stores the human readable control code for the customer/vendor record. This is typically called the customer/vendor "account" in the application. |
|
business_id | integer | ||
public.language.code | language_code | character varying(6) | DEFAULT 'en'::character varying |
public.pricegroup.id | pricegroup_id | integer | |
curr | character(3) | ||
startdate | date | DEFAULT ('now'::text)::date | |
enddate | date | ||
threshold | numeric | ||
public.entity_employee.entity_id | employee_id | integer | |
public.person.id | primary_contact | integer | |
public.account.id | ar_ap_account_id | integer | |
public.account.id | cash_account_id | integer | |
public.entity_bank_account.id | bank_account | integer | |
public.country_tax_form.id | taxform_id | integer |
Name | Constraint |
---|---|
entity_credit_account_check | CHECK (((ar_ap_account_id IS NOT NULL) OR (entity_id = 0))) |
entity_credit_account_entity_class_check | CHECK ((entity_class = ANY (ARRAY[1, 2]))) |
Tables referencing this one via Foreign Key Constraints:
This contains employee-specific extensions to person/entity.
F-Key | Name | Type | Description |
---|---|---|---|
public.entity.id | entity_id | integer | PRIMARY KEY |
startdate | date | NOT NULL DEFAULT ('now'::text)::date | |
enddate | date | ||
role | character varying(20) | ||
ssn | text | ||
sales | boolean | DEFAULT false | |
public.entity.id | manager_id | integer | |
employeenumber | character varying(32) | ||
dob | date |
Tables referencing this one via Foreign Key Constraints:
F-Key | Name | Type | Description |
---|---|---|---|
id | integer | PRIMARY KEY DEFAULT nextval('note_id_seq'::regclass) | |
note_class | integer | NOT NULL | |
note | text | NOT NULL | |
vector | tsvector | NOT NULL DEFAULT ''::tsvector | |
created | timestamp without time zone | NOT NULL DEFAULT now() | |
created_by | text | DEFAULT "session_user"() | |
public.entity.id | ref_key | integer | NOT NULL |
subject | text | ||
public.entity.id | entity_id | integer |
Table public.entity_note Inherits note,
Name | Constraint |
---|---|
entity_note_note_class_check | CHECK ((note_class = 1)) |
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.
F-Key | Name | Type | Description |
---|---|---|---|
public.entity.id | entity_id | integer | PRIMARY KEY |
other_name | text | PRIMARY KEY |
Name | Constraint |
---|---|
entity_other_name_other_name_check | CHECK ((other_name ~ '[[:alnum:]_]'::text)) |
This table stores contact information for entities
F-Key | Name | Type | Description |
---|---|---|---|
public.entity.id | entity_id | integer | PRIMARY KEY |
public.contact_class.id | contact_class_id | integer | PRIMARY KEY |
contact | text | PRIMARY KEY | |
description | text |
Name | Constraint |
---|---|
entity_to_contact_contact_check | CHECK ((contact ~ '[[:alnum:]_]'::text)) |
This table is used for locations generic to companies. For contract-bound addresses, use eca_to_location instead
F-Key | Name | Type | Description |
---|---|---|---|
public.location.id | location_id | integer | PRIMARY KEY |
public.location_class.id | location_class | integer | PRIMARY KEY |
public.entity.id | entity_id | integer | PRIMARY KEY |
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).
F-Key | Name | Type | Description |
---|---|---|---|
curr | character(3) | PRIMARY KEY | |
transdate | date | PRIMARY KEY | |
buy | numeric | ||
sell | numeric |
Abstract table, holds no records. Inheriting table store actual file attachment data. Can be queried however to retrieve lists of all files.
F-Key | Name | Type | Description |
---|---|---|---|
content | bytea | NOT NULL | |
public.mime_type.id | mime_type_id | integer | NOT NULL |
file_name | text | PRIMARY KEY | |
description | text | ||
public.entity.id | uploaded_by | integer | NOT NULL |
uploaded_at | timestamp without time zone | NOT NULL DEFAULT now() | |
id | serial | UNIQUE NOT NULL | |
ref_key | integer |
PRIMARY KEY
This column inheriting tables is used to reference the database row for the attachment. Inheriting tables MUST set the foreign key here appropriately. This can also be used to create classifications of other documents, such as by source of automatic import (where the file is not yet attached) or even standard, long-lived documents. |
|
public.file_class.id | file_class | integer | PRIMARY KEY |
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.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | UNIQUE NOT NULL | |
class | text | PRIMARY KEY |
Tables referencing this one via Foreign Key Constraints:
File attachments primarily attached to customer and vendor agreements.
F-Key | Name | Type | Description |
---|---|---|---|
content | bytea | NOT NULL | |
mime_type_id | integer | NOT NULL | |
file_name | text | PRIMARY KEY | |
description | text | ||
uploaded_by | integer | NOT NULL | |
uploaded_at | timestamp without time zone | NOT NULL DEFAULT now() | |
id | integer | UNIQUE NOT NULL DEFAULT nextval('file_base_id_seq'::regclass) | |
public.entity_credit_account.id | ref_key | integer | PRIMARY KEY |
file_class | integer | PRIMARY KEY |
Table public.file_eca Inherits file_base,
Name | Constraint |
---|---|
file_eca_file_class_check | CHECK ((file_class = 5)) |
File attachments primarily attached to entities.
F-Key | Name | Type | Description |
---|---|---|---|
content | bytea | NOT NULL | |
mime_type_id | integer | NOT NULL | |
file_name | text | PRIMARY KEY | |
description | text | ||
uploaded_by | integer | NOT NULL | |
uploaded_at | timestamp without time zone | NOT NULL DEFAULT now() | |
id | integer | UNIQUE NOT NULL DEFAULT nextval('file_base_id_seq'::regclass) | |
public.entity.id | ref_key | integer | PRIMARY KEY |
file_class | integer | PRIMARY KEY |
Table public.file_entity Inherits file_base,
Name | Constraint |
---|---|
file_entity_file_class_check | CHECK ((file_class = 4)) |
F-Key | Name | Type | Description |
---|---|---|---|
file_id | integer | ||
ref_key | integer | ||
reference | text | ||
type | text | ||
dest_class | integer | ||
source_class | integer | ||
dest_ref | integer |
SELECT file_tx_links.file_id , file_tx_links.ref_key , file_tx_links.reference , file_tx_links.type , file_tx_links.dest_class , file_tx_links.source_class , file_tx_links.dest_ref FROM file_tx_links UNIONSELECT file_order_links.file_id , file_order_links.ref_key , file_order_links.reference , file_order_links.oe_class AS type , file_order_links.dest_class , file_order_links.source_class , file_order_links.dest_ref FROM file_order_links;
File attachments primarily attached to orders and quotations.
F-Key | Name | Type | Description |
---|---|---|---|
content | bytea | NOT NULL | |
mime_type_id | integer | NOT NULL | |
file_name | text | PRIMARY KEY | |
description | text | ||
uploaded_by | integer | NOT NULL | |
uploaded_at | timestamp without time zone | NOT NULL DEFAULT now() | |
id | integer | UNIQUE NOT NULL DEFAULT nextval('file_base_id_seq'::regclass) | |
public.oe.id | ref_key | integer | PRIMARY KEY |
file_class | integer | PRIMARY KEY |
Table public.file_order Inherits file_base,
Name | Constraint |
---|---|
file_order_file_class_check | CHECK ((file_class = 2)) |
Tables referencing this one via Foreign Key Constraints:
F-Key | Name | Type | Description |
---|---|---|---|
file_id | integer | ||
ref_key | integer | ||
reference | text | ||
oe_class | text | ||
dest_class | integer | ||
source_class | integer | ||
dest_ref | integer |
SELECT sl.file_id , sl.ref_key , oe.ordnumber AS reference , oc.oe_class , sl.dest_class , sl.source_class , sl.ref_key AS dest_ref FROM ( (file_secondary_attachment sl JOIN oe ON ( (sl.ref_key = oe.id) ) ) JOIN oe_class oc ON ( (oe.oe_class_id = oc.id) ) ) WHERE (sl.source_class = 2);
Secondary links from one order to another, for example to support order consolidation.
F-Key | Name | Type | Description |
---|---|---|---|
public.file_order.id | file_id | integer | PRIMARY KEY |
source_class | integer | PRIMARY KEY | |
public.oe.id | ref_key | integer | PRIMARY KEY |
dest_class | integer | PRIMARY KEY | |
attached_by | integer | NOT NULL | |
attached_at | timestamp without time zone | NOT NULL DEFAULT now() |
Table public.file_order_to_order Inherits file_secondary_attachment,
Name | Constraint |
---|---|
file_order_to_order_dest_class_check | CHECK ((dest_class = 2)) |
file_order_to_order_source_class_check | CHECK ((source_class = 2)) |
Secondary links from orders to transactions, for example to track files when invoices are generated from orders.
F-Key | Name | Type | Description |
---|---|---|---|
public.file_order.id | file_id | integer | PRIMARY KEY |
source_class | integer | PRIMARY KEY | |
public.journal_entry.id | ref_key | integer | PRIMARY KEY |
dest_class | integer | PRIMARY KEY | |
attached_by | integer | NOT NULL | |
attached_at | timestamp without time zone | NOT NULL DEFAULT now() |
Table public.file_order_to_tx Inherits file_secondary_attachment,
Name | Constraint |
---|---|
file_order_to_tx_dest_class_check | CHECK ((dest_class = 1)) |
file_order_to_tx_source_class_check | CHECK ((source_class = 2)) |
File attachments primarily attached to goods and services.
F-Key | Name | Type | Description |
---|---|---|---|
content | bytea | NOT NULL | |
mime_type_id | integer | NOT NULL | |
file_name | text | PRIMARY KEY | |
description | text | ||
uploaded_by | integer | NOT NULL | |
uploaded_at | timestamp without time zone | NOT NULL DEFAULT now() | |
id | integer | UNIQUE NOT NULL DEFAULT nextval('file_base_id_seq'::regclass) | |
public.parts.id | ref_key | integer | PRIMARY KEY |
file_class | integer | PRIMARY KEY |
Table public.file_part Inherits file_base,
Name | Constraint |
---|---|
file_part_file_class_check | CHECK ((file_class = 3)) |
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.
F-Key | Name | Type | Description |
---|---|---|---|
file_id | integer | PRIMARY KEY | |
public.file_class.id | source_class | integer | PRIMARY KEY |
ref_key | integer | PRIMARY KEY | |
public.file_class.id | dest_class | integer | PRIMARY KEY |
public.entity.id | attached_by | integer | NOT NULL |
attached_at | timestamp without time zone | NOT NULL DEFAULT now() |
File attachments primarily attached to AR/AP/GL.
F-Key | Name | Type | Description |
---|---|---|---|
content | bytea | NOT NULL | |
mime_type_id | integer | NOT NULL | |
file_name | text | PRIMARY KEY | |
description | text | ||
uploaded_by | integer | NOT NULL | |
uploaded_at | timestamp without time zone | NOT NULL DEFAULT now() | |
id | integer | UNIQUE NOT NULL DEFAULT nextval('file_base_id_seq'::regclass) | |
public.transactions.id | ref_key | integer | PRIMARY KEY |
file_class | integer | PRIMARY KEY |
Table public.file_transaction Inherits file_base,
Name | Constraint |
---|---|
file_transaction_file_class_check | CHECK ((file_class = 1)) |
Tables referencing this one via Foreign Key Constraints:
F-Key | Name | Type | Description |
---|---|---|---|
file_id | integer | ||
ref_key | integer | ||
reference | text | ||
type | text | ||
dest_class | integer | ||
source_class | integer | ||
dest_ref | integer |
SELECT sl.file_id , sl.ref_key , gl.reference , gl.type , sl.dest_class , sl.source_class , sl.ref_key AS dest_ref FROM (file_secondary_attachment sl JOIN ( ( SELECT gl.id , gl.reference ,'gl'::text AS type FROM gl UNIONSELECT ar.id , ar.invnumber , CASE WHEN ar.invoice THEN 'is'::text ELSE 'ar'::text END AS type FROM ar ) UNIONSELECT ap.id , ap.invnumber , CASE WHEN ap.invoice THEN 'ir'::text ELSE 'ap'::text END AS type FROM ap ) gl ON ( ( (sl.ref_key = gl.id) AND (sl.source_class = 1) ) ) );
Secondary links from journal entries to orders.
F-Key | Name | Type | Description |
---|---|---|---|
public.file_transaction.id | file_id | integer | PRIMARY KEY |
source_class | integer | PRIMARY KEY | |
public.oe.id | ref_key | integer | PRIMARY KEY |
dest_class | integer | PRIMARY KEY | |
attached_by | integer | NOT NULL | |
attached_at | timestamp without time zone | NOT NULL DEFAULT now() |
Table public.file_tx_to_order Inherits file_secondary_attachment,
Name | Constraint |
---|---|
file_tx_to_order_dest_class_check | CHECK ((dest_class = 2)) |
file_tx_to_order_source_class_check | CHECK ((source_class = 1)) |
F-Key | Name | Type | Description |
---|---|---|---|
public.file_class.id | file_class | integer | PRIMARY KEY |
view_name | text | UNIQUE NOT NULL |
GIFI labels for accounts, used in Canada and some EU countries for tax reporting
F-Key | Name | Type | Description |
---|---|---|---|
accno | text | PRIMARY KEY | |
description | text |
This table holds summary information for entries in the general journal. Does not hold summary information in 1.3 for AR or AP entries.
F-Key | Name | Type | Description |
---|---|---|---|
public.transactions.id | id | integer | PRIMARY KEY DEFAULT nextval('id'::regclass) |
reference | text | ||
description | text | ||
transdate | date | DEFAULT ('now'::text)::date | |
public.person.id | person_id | integer |
the person_id of the employee who created the entry. |
notes | text | ||
approved | boolean | DEFAULT true |
Tables referencing this one via Foreign Key Constraints:
This table contains inventory mappings to warehouses, not general inventory management data.
F-Key | Name | Type | Description |
---|---|---|---|
public.entity_employee.entity_id | entity_id | integer | |
warehouse_id | integer | ||
parts_id | integer | ||
trans_id | integer | ||
orderitems_id | integer | ||
qty | numeric | ||
shippingdate | date | ||
entry_id | serial | PRIMARY KEY |
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
transdate | date | NOT NULL | |
source | text | ||
ar_trans_id | integer | ||
ap_trans_id | integer |
Tables referencing this one via Foreign Key Constraints:
F-Key | Name | Type | Description |
---|---|---|---|
public.inventory_report.id | report_id | integer | PRIMARY KEY |
public.parts.id | parts_id | integer | PRIMARY KEY |
counted | numeric | ||
expected | numeric |
Line items of invoices with goods/services attached.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
public.transactions.id | trans_id | integer | |
public.parts.id | parts_id | integer | |
description | text | ||
qty | numeric |
Positive is normal for sales invoices, negative for vendor invoices. |
|
allocated | integer |
Number of allocated items, negative relative to qty. When qty + allocated = 0, then the item is fully used for purposes of COGS calculations. |
|
sellprice | numeric | ||
precision | integer | ||
fxsellprice | numeric | ||
discount | numeric | ||
assemblyitem | boolean | DEFAULT false | |
unit | character varying | ||
deliverydate | date | ||
serialnumber | text | ||
notes | text |
Tables referencing this one via Foreign Key Constraints:
invoice_id_key id invoice_trans_id_key trans_idF-Key | Name | Type | Description |
---|---|---|---|
id | integer | PRIMARY KEY DEFAULT nextval('note_id_seq'::regclass) | |
note_class | integer | NOT NULL | |
note | text | NOT NULL | |
vector | tsvector | NOT NULL DEFAULT ''::tsvector | |
created | timestamp without time zone | NOT NULL DEFAULT now() | |
created_by | text | DEFAULT "session_user"() | |
public.invoice.id | ref_key | integer | NOT NULL |
subject | text |
Table public.invoice_note Inherits note,
invoice_note_id_idx id invoice_note_vectors_idx vectorMaping invoice to country_tax_form.
F-Key | Name | Type | Description |
---|---|---|---|
public.invoice.id | invoice_id | integer | PRIMARY KEY |
reportable | boolean |
Time and materials cards. Materials cards not implemented.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
public.business_unit.id | business_unit_id | integer | |
parts_id | integer | ||
description | text | ||
qty | numeric | ||
allocated | numeric | ||
sellprice | numeric | ||
fxsellprice | numeric | ||
serialnumber | text | ||
checkedin | timestamp with time zone | ||
checkedout | timestamp with time zone | ||
public.person.id | person_id | integer | NOT NULL |
notes | text | ||
total | numeric | NOT NULL | |
non_billable | numeric | NOT NULL |
F-Key | Name | Type | Description |
---|---|---|---|
id | integer | UNIQUE NOT NULL | |
label | text | PRIMARY KEY | |
description | text | NOT NULL | |
is_service | boolean | DEFAULT true | |
is_timecard | boolean | DEFAULT true |
F-Key | Name | Type | Description |
---|---|---|---|
public.business_unit.id | bu_id | integer | PRIMARY KEY |
parts_id | integer |
Job costing/manufacturing here not implemented. |
|
production | numeric | ||
completed | numeric |
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.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
reference | text |
NOT NULL
Invoice number or journal entry number. |
|
description | text | ||
public.session.session_id | locked_by | integer | |
public.journal_type.id | journal | integer | |
post_date | date | NOT NULL DEFAULT now() | |
effective_start | date |
NOT NULL
For transactions whose effects are spread out over a period of time, this is the effective start date for the transaction. To be used by add-ons for automating adjustments. |
|
effective_end | date |
NOT NULL
For transactions whose effects are spread out over a period of time, this is the effective end date for the transaction. To be used by add-ons for automating adjustments. |
|
currency | character(3) | NOT NULL | |
approved | boolean | DEFAULT false | |
is_template | boolean |
DEFAULT false
Set true for template transactions. Templates can never be approved but can be copied into new transactions and are useful for recurrances. |
|
public.entity.id | entered_by | integer | NOT NULL |
public.entity.id | approved_by | integer |
Name | Constraint |
---|---|
journal_entry_check | CHECK (((is_template IS FALSE) OR (approved IS FALSE))) |
Tables referencing this one via Foreign Key Constraints:
Replaces acc_trans as the main account transaction line table.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
public.account.id | account_id | integer | NOT NULL |
public.journal_entry.id | journal_id | integer | NOT NULL |
amount | numeric | NOT NULL | |
cleared | boolean |
NOT NULL
DEFAULT false
Still needed both for legacy data and in case reconciliation data must eventually be purged. |
|
public.cr_report.id | reconciliation_report | integer | |
public.account_link_description.description | line_type | text |
Name | Constraint |
---|---|
journal_line_amount_check | CHECK ((amount <> 'NaN'::numeric)) |
Tables referencing this one via Foreign Key Constraints:
This stores notes attached to journal entries, including payments and invoices.
F-Key | Name | Type | Description |
---|---|---|---|
id | integer | PRIMARY KEY DEFAULT nextval('note_id_seq'::regclass) | |
note_class | integer | NOT NULL | |
note | text | NOT NULL | |
vector | tsvector | NOT NULL DEFAULT ''::tsvector | |
created | timestamp without time zone | NOT NULL DEFAULT now() | |
created_by | text | DEFAULT "session_user"() | |
public.journal_entry.id | ref_key | integer | NOT NULL |
subject | text | ||
internal_only | boolean |
NOT NULL
DEFAULT false
When set to true, does not show up in notes list for invoice templates |
Table public.journal_note Inherits note,
Name | Constraint |
---|---|
journal_note_note_class_check | CHECK ((note_class = 5)) |
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
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | UNIQUE NOT NULL | |
name | text | PRIMARY KEY |
Tables referencing this one via Foreign Key Constraints:
Languages for manual translations and so forth.
F-Key | Name | Type | Description |
---|---|---|---|
code | character varying(6) | PRIMARY KEY | |
description | text |
Tables referencing this one via Foreign Key Constraints:
This table stores addresses, such as shipto and bill to addresses.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
line_one | text | NOT NULL | |
line_two | text | ||
line_three | text | ||
city | text | NOT NULL | |
state | text | ||
public.country.id | country_id | integer | NOT NULL |
mail_code | text | NOT NULL | |
created | date | NOT NULL DEFAULT now() | |
inactive_date | timestamp without time zone | ||
active | boolean | NOT NULL DEFAULT true |
Name | Constraint |
---|---|
location_city_check | CHECK ((city ~ '[[:alnum:]_]'::text)) |
location_line_one_check | CHECK ((line_one ~ '[[:alnum:]_]'::text)) |
location_mail_code_check | CHECK ((mail_code ~ '[[:alnum:]_]'::text)) |
location_state_check | CHECK ((state ~ '[[:alnum:]_]'::text)) |
Tables referencing this one via Foreign Key Constraints:
Individuals seeking to add new location classes should coordinate with others.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | UNIQUE NOT NULL | |
class | text | PRIMARY KEY | |
authoritative | boolean | PRIMARY KEY |
Name | Constraint |
---|---|
location_class_class_check | CHECK ((class ~ '[[:alnum:]_]'::text)) |
Tables referencing this one via Foreign Key Constraints:
F-Key | Name | Type | Description |
---|---|---|---|
role_name | text | PRIMARY KEY |
Tables referencing this one via Foreign Key Constraints:
F-Key | Name | Type | Description |
---|---|---|---|
public.lsmb_group.role_name | group_name | text | PRIMARY KEY |
granted_role | text | PRIMARY KEY |
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.
F-Key | Name | Type | Description |
---|---|---|---|
id | integer | UNIQUE NOT NULL | |
label | text | PRIMARY KEY |
Tables referencing this one via Foreign Key Constraints:
Tracks role assignments in the front end. Not sure why we need this. Will rethink for 1.4.
F-Key | Name | Type | Description |
---|---|---|---|
public.users.id | user_id | integer | NOT NULL |
role | text | NOT NULL |
A single parts entry can have multiple make/model entries. These store manufacturer/model number info.
F-Key | Name | Type | Description |
---|---|---|---|
parts_id | integer | PRIMARY KEY | |
barcode | text | ||
make | text | PRIMARY KEY | |
model | text | PRIMARY KEY |
Provides access control list entries for menu nodes.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | NOT NULL | |
role_name | character varying | PRIMARY KEY | |
acl_type | character varying |
Nodes are hidden unless a role is found of which the user is a member, and where the acl_type for that role type and node is set to 'allow' and no acl is found for any role of which the user is a member, where the acl_type is set to 'deny'. |
|
public.menu_node.id | node_id | integer | PRIMARY KEY |
Name | Constraint |
---|---|
menu_acl_acl_type_check | CHECK ((((acl_type)::text = 'allow'::text) OR ((acl_type)::text = 'deny'::text))) |
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.
F-Key | Name | Type | Description |
---|---|---|---|
public.menu_node.id | node_id | integer | PRIMARY KEY |
attribute | character varying | PRIMARY KEY | |
value | character varying | NOT NULL | |
id | serial | NOT NULL |
A nice human-readable view for investigating the menu tree. Does not show menu attributes or acls.
F-Key | Name | Type | Description |
---|---|---|---|
level | integer | ||
path | text | ||
label | text | ||
id | integer | ||
position | integer |
WITH RECURSIVE tree (path , id , parent , level , positions ) AS ( SELECT (menu_node.id)::text AS path , menu_node.id , menu_node.parent , 0 AS level , (menu_node."position")::text AS "position" FROM menu_node WHERE (menu_node.parent IS NULL) UNIONSELECT ( (t.path || ','::text ) || (n.id)::text ) , n.id , n.parent , (t.level + 1) , ( (t.positions || ','::text ) || n."position" ) FROM (menu_node n JOIN tree t ON ( (t.id = n.parent) ) ) ) SELECT t.level , t.path , (repeat (' '::text , (2 * t.level) ) || (n.label)::text ) AS label , n.id , n."position" FROM (tree t JOIN menu_node n USING (id) ) ORDER BY (string_to_array (t.positions ,','::text ) )::integer[];
This table stores the tree structure of the menu.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
label | character varying | NOT NULL | |
public.menu_node.id | parent | integer | UNIQUE#1 |
position | integer | UNIQUE#1 NOT NULL |
Tables referencing this one via Foreign Key Constraints:
This is a lookup table for storing MIME types.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | UNIQUE NOT NULL | |
mime_type | text | PRIMARY KEY | |
invoice_include | boolean | DEFAULT false |
Tables referencing this one via Foreign Key Constraints:
Tracks ship_to information for orders and invoices.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
public.journal_entry.id | trans_id | integer | |
public.oe.id | oe_id | integer | |
public.location.id | location_id | integer |
This is an abstract table which should have zero rows. It is inherited by other tables for specific notes.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
public.note_class.id | note_class | integer | NOT NULL |
note | text |
NOT NULL
Body of note. |
|
vector | tsvector |
NOT NULL
DEFAULT ''::tsvector
tsvector for full text indexing, requires both setting up tsearch dictionaries and adding triggers to use at present. |
|
created | timestamp without time zone | NOT NULL DEFAULT now() | |
created_by | text | DEFAULT "session_user"() | |
ref_key | integer |
NOT NULL
Subclassed tables use this column as a foreign key against the table storing the record a note is attached to. |
|
subject | text |
Coordinate with others before adding entries.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
class | text | NOT NULL |
Name | Constraint |
---|---|
note_class_class_check | CHECK ((class ~ '[[:alnum:]_]'::text)) |
Tables referencing this one via Foreign Key Constraints:
Header information for: * Sales orders * Purchase Orders * Quotations * Requests for Quotation
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
ordnumber | text | ||
transdate | date | DEFAULT ('now'::text)::date | |
public.entity.id | entity_id | integer | |
amount | numeric | ||
netamount | numeric | ||
reqdate | date | ||
taxincluded | boolean | ||
shippingpoint | text | ||
notes | text | ||
curr | character(3) | ||
public.person.id | person_id | integer | |
closed | boolean | DEFAULT false | |
quotation | boolean | DEFAULT false | |
quonumber | text | ||
intnotes | text | ||
shipvia | text | ||
language_code | character varying(6) | ||
ponumber | text | ||
terms | smallint | ||
public.entity_credit_account.id | entity_credit_account | integer | NOT NULL |
public.oe_class.id | oe_class_id | integer | NOT NULL |
Tables referencing this one via Foreign Key Constraints:
oe_id_key id oe_ordnumber_key ordnumber oe_transdate_key transdateHardwired classifications for orders and quotations. Coordinate before adding.
F-Key | Name | Type | Description |
---|---|---|---|
id | smallint | UNIQUE | |
oe_class | text | PRIMARY KEY |
Name | Constraint |
---|---|
oe_class_id_check | CHECK ((id = ANY (ARRAY[1, 2, 3, 4]))) |
Tables referencing this one via Foreign Key Constraints:
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.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
public.session.session_id | session_id | integer |
Line items for sales/purchase orders and quotations.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
trans_id | integer | ||
parts_id | integer | ||
description | text | ||
qty | numeric | ||
sellprice | numeric | ||
precision | integer | ||
discount | numeric | ||
unit | character varying(5) | ||
reqdate | date | ||
ship | numeric | ||
serialnumber | text | ||
notes | text |
Tables referencing this one via Foreign Key Constraints:
orderitems_id_key id orderitems_trans_id_key trans_idF-Key | Name | Type | Description |
---|---|---|---|
payment_id | integer | ||
payment_reference | text | ||
payment_class | integer | ||
payment_closed | boolean | ||
payment_date | date | ||
chart_id | integer | ||
accno | text | ||
chart_description | text | ||
available | numeric | ||
legal_name | text | ||
entity_credit_id | integer | ||
entity_id | integer | ||
discount | numeric | ||
meta_number | character varying(32) |
SELECT p.id AS payment_id , p.reference AS payment_reference , p.payment_class , p.closed AS payment_closed , p.payment_date , ac.chart_id , c.accno , c.description AS chart_description , abs (sum (ac.amount) ) AS available , cmp.legal_name , eca.id AS entity_credit_id , eca.entity_id , eca.discount , eca.meta_number FROM ( ( ( ( (payment p JOIN payment_links pl ON ( (pl.payment_id = p.id) ) ) JOIN acc_trans ac ON ( (ac.entry_id = pl.entry_id) ) ) JOIN chart c ON ( (c.id = ac.chart_id) ) ) JOIN entity_credit_account eca ON ( (eca.id = p.entity_credit_id) ) ) JOIN company cmp ON ( (cmp.entity_id = eca.entity_id) ) ) WHERE ( ( (p.gl_id IS NOT NULL) AND ( (pl.type = 2) OR (pl.type = 0) ) ) AND (c.link ~~ '%overpayment%'::text) ) GROUP BY p.id , c.accno , p.reference , p.payment_class , p.closed , p.payment_date , ac.chart_id , c.description , cmp.legal_name , eca.id , eca.entity_id , eca.discount , eca.meta_number;
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.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
partnumber | text | ||
description | text | ||
unit | character varying(5) | ||
listprice | numeric | ||
sellprice | numeric | ||
lastcost | numeric | ||
priceupdate | date | DEFAULT ('now'::text)::date | |
weight | numeric | ||
onhand | numeric | ||
notes | text | ||
makemodel | boolean | DEFAULT false | |
assembly | boolean | DEFAULT false | |
alternate | boolean | DEFAULT false | |
rop | numeric |
Re-order point. Used to select parts for short inventory report. |
|
public.account.id | inventory_accno_id | integer | |
public.account.id | income_accno_id | integer | |
public.account.id | expense_accno_id | integer | |
public.account.id | returns_accno_id | integer | |
bin | text |
Text identifier for where a part is stored. |
|
obsolete | boolean | DEFAULT false | |
bom | boolean |
DEFAULT false
Show on Bill of Materials. |
|
image | text |
Hyperlink to product image. |
|
drawing | text | ||
microfiche | text | ||
partsgroup_id | integer | ||
avgcost | numeric |
Tables referencing this one via Foreign Key Constraints:
parts_description_key lower(description) parts_id_key id parts_partnumber_key lower(partnumber)Translation information for parts.
F-Key | Name | Type | Description |
---|---|---|---|
public.parts.id | trans_id | integer | PRIMARY KEY |
language_code | character varying(6) | PRIMARY KEY | |
description | text |
Table public.parts_translation Inherits translation,
Tracks per-customer pricing. Discounts can be offered for periods of time and for pricegroups as well as per customer
F-Key | Name | Type | Description |
---|---|---|---|
parts_id | integer | ||
public.entity_credit_account.id | credit_id | integer | |
public.pricegroup.id | pricegroup_id | integer | |
pricebreak | numeric | ||
sellprice | numeric | ||
validfrom | date | ||
validto | date | ||
curr | character(3) | ||
entry_id | serial | PRIMARY KEY |
Groups of parts for Point of Sale screen.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
partsgroup | text | ||
public.partsgroup.id | parent | integer |
Tables referencing this one via Foreign Key Constraints:
partsgroup_id_key idTranslation information for partsgroups.
F-Key | Name | Type | Description |
---|---|---|---|
public.partsgroup.id | trans_id | integer | PRIMARY KEY |
language_code | character varying(6) | PRIMARY KEY | |
description | text |
Table public.partsgroup_translation Inherits translation,
Mapping of parts to taxes.
F-Key | Name | Type | Description |
---|---|---|---|
public.parts.id | parts_id | integer | PRIMARY KEY |
public.account.id | chart_id | integer | PRIMARY KEY |
public.taxcategory.taxcategory_id | taxcategory_id | integer |
Tracks vendor's pricing, as well as vendor's part number, lead time required and currency.
F-Key | Name | Type | Description |
---|---|---|---|
public.entity_credit_account.id | credit_id | integer | NOT NULL |
parts_id | integer | ||
partnumber | text | ||
leadtime | smallint | ||
lastcost | numeric | ||
curr | character(3) | ||
entry_id | serial | PRIMARY KEY |
This table will store the main data on a payment, prepayment, overpayment, et
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
reference | text |
NOT NULL
This field will store the code for both receipts and payment order |
|
public.gl.id | gl_id | integer |
A payment should always be linked to a GL movement |
payment_class | integer | NOT NULL | |
payment_date | date | DEFAULT ('now'::text)::date | |
closed | boolean |
DEFAULT false
This will store the current state of a payment/receipt order |
|
public.entity_credit_account.id | entity_credit_id | integer | |
public.person.id | employee_id | integer | |
currency | character(3) | ||
notes | text |
Tables referencing this one via Foreign Key Constraints:
payment_id_idx idAn 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.
F-Key | Name | Type | Description |
---|---|---|---|
public.payment.id | payment_id | integer | |
public.acc_trans.entry_id | entry_id | integer | |
type | integer |
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.
F-Key | Name | Type | Description |
---|---|---|---|
public.journal_line.id | line_id | integer | PRIMARY KEY |
public.eca_invoice.journal_id | pays | integer | NOT NULL |
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | UNIQUE NOT NULL | |
label | text | PRIMARY KEY |
F-Key | Name | Type | Description |
---|---|---|---|
entry_id | serial | UNIQUE NOT NULL | |
public.entity.id | entity_id | integer | PRIMARY KEY |
public.payroll_deduction_type.id | type_id | integer | PRIMARY KEY |
rate | numeric | NOT NULL |
F-Key | Name | Type | Description |
---|---|---|---|
id | integer | UNIQUE#1 NOT NULL | |
public.country.id | country_id | integer | UNIQUE#1 PRIMARY KEY |
label | text | PRIMARY KEY |
Tables referencing this one via Foreign Key Constraints:
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | UNIQUE NOT NULL | |
public.account.id | account_id | integer | NOT NULL |
public.payroll_deduction_class.id#1 | pdc_id | integer | NOT NULL |
public.payroll_deduction_class.country_id#1 | country_id | integer | NOT NULL |
label | text | NOT NULL | |
unit | text | NOT NULL | |
default_amount | numeric | ||
calc_percent | boolean | NOT NULL |
Tables referencing this one via Foreign Key Constraints:
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | UNIQUE NOT NULL | |
label | text | PRIMARY KEY |
Tables referencing this one via Foreign Key Constraints:
F-Key | Name | Type | Description |
---|---|---|---|
public.payroll_employee_class.id | ec_id | integer | PRIMARY KEY |
public.payroll_income_type.id | it_id | integer | PRIMARY KEY |
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | UNIQUE NOT NULL | |
label | text |
F-Key | Name | Type | Description |
---|---|---|---|
id | integer | UNIQUE#1 NOT NULL | |
public.country.id | country_id | integer | UNIQUE#1 PRIMARY KEY |
label | text | PRIMARY KEY |
Tables referencing this one via Foreign Key Constraints:
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | UNIQUE NOT NULL | |
public.account.id | account_id | integer | NOT NULL |
public.payroll_income_class.id#1 | pic_id | integer | NOT NULL |
public.payroll_income_class.country_id#1 | country_id | integer | NOT NULL |
label | text | NOT NULL | |
unit | text | NOT NULL | |
default_amount | numeric |
Tables referencing this one via Foreign Key Constraints:
F-Key | Name | Type | Description |
---|---|---|---|
public.entity.id | employee_id | integer | NOT NULL |
public.payroll_pto_class.id | pto_class_id | integer | NOT NULL |
public.payroll_report.id | report_id | integer | NOT NULL |
amount | numeric | NOT NULL |
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | UNIQUE NOT NULL | |
label | text | PRIMARY KEY |
Tables referencing this one via Foreign Key Constraints:
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
public.payroll_employee_class.id | ec_id | integer | NOT NULL |
payment_date | date | NOT NULL | |
public.entity_employee.entity_id | created_by | integer | |
public.entity_employee.entity_id | approved_by | integer |
Tables referencing this one via Foreign Key Constraints:
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | UNIQUE NOT NULL | |
public.payroll_report.id | report_id | integer | PRIMARY KEY |
public.entity.id | employee_id | integer | PRIMARY KEY |
public.payroll_income_type.id | it_id | integer | PRIMARY KEY |
qty | numeric | NOT NULL | |
rate | numeric | NOT NULL | |
description | text |
F-Key | Name | Type | Description |
---|---|---|---|
entry_id | serial | UNIQUE NOT NULL | |
public.entity.id | entity_id | integer | PRIMARY KEY |
public.payroll_income_type.id | type_id | integer | PRIMARY KEY |
rate | numeric | NOT NULL |
F-Key | Name | Type | Description |
---|---|---|---|
id | text | ||
label | text | ||
date_to | date | ||
date_from | date |
SELECT'ytd'::text AS id ,'Year to Date'::text AS label , (now () )::date AS date_to , ( ( (date_part ('year'::text , now () ) )::text || '-01-01'::text ) )::date AS date_from UNIONSELECT'last_year'::text AS id ,'Last Year'::text AS label , ( ( ( (date_part ('YEAR'::text , now () ) - (1)::double precision ) )::text || '-12-31'::text ) )::date AS date_to , ( ( ( (date_part ('YEAR'::text , now () ) - (1)::double precision ) )::text || '-01-01'::text ) )::date AS date_from;
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.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
public.entity.id | entity_id | integer | UNIQUE NOT NULL |
public.salutation.id | salutation_id | integer | |
first_name | text | NOT NULL | |
middle_name | text | ||
last_name | text | NOT NULL | |
created | date | NOT NULL DEFAULT ('now'::text)::date |
Name | Constraint |
---|---|
person_first_name_check | CHECK ((first_name ~ '[[:alnum:]_]'::text)) |
person_last_name_check | CHECK ((last_name ~ '[[:alnum:]_]'::text)) |
Tables referencing this one via Foreign Key Constraints:
currently unused in the front-end, but can be used to map persons to companies.
F-Key | Name | Type | Description |
---|---|---|---|
public.location.id | location_id | integer | PRIMARY KEY |
public.person.id | person_id | integer | PRIMARY KEY |
public.company.id | company_id | integer | NOT NULL |
Pricegroups are groups of customers who are assigned prices and discounts together.
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
pricegroup | text |
Tables referencing this one via Foreign Key Constraints:
pricegroup_id_key id pricegroup_pricegroup_key pricegroupF-Key | Name | Type | Description |
---|---|---|---|
payee | text | ||
id | bigint | ||
report_id | integer | ||
scn | text | ||
their_balance | numeric | ||
our_balance | numeric | ||
errorcode | integer | ||
user | integer | ||
clear_time | date | ||
insert_time | timestamp with time zone | ||
trans_type | text | ||
post_date | date | ||
ledger_id | integer | ||
voucher_id | integer | ||
overlook | boolean | ||
cleared | boolean |
SELECT n.name AS payee , rr.id , rr.report_id , rr.scn , rr.their_balance , rr.our_balance , rr.errorcode , rr."user" , rr.clear_time , rr.insert_time , rr.trans_type , rr.post_date , rr.ledger_id , ac.voucher_id , rr.overlook , rr.cleared FROM ( ( (cr_report_line rr LEFT JOIN acc_trans ac ON ( (rr.ledger_id = ac.entry_id) ) ) LEFT JOIN gl ON ( (ac.trans_id = gl.id) ) ) LEFT JOIN ( ( SELECT ap.id , e.name FROM ( (ap JOIN entity_credit_account eca ON ( (ap.entity_credit_account = eca.id) ) ) JOIN entity e ON ( (eca.entity_id = e.id) ) ) UNIONSELECT ar.id , e.name FROM ( (ar JOIN entity_credit_account eca ON ( (ar.entity_credit_account = eca.id) ) ) JOIN entity e ON ( (eca.entity_id = e.id) ) ) ) UNIONSELECT gl.id , gl.description FROM gl ) n ON ( (n.id = ac.trans_id) ) );
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.
F-Key | Name | Type | Description |
---|---|---|---|
id | integer | PRIMARY KEY DEFAULT nextval('id'::regclass) | |
reference | text | ||
startdate | date | ||
nextdate | date | ||
enddate | date | ||
repeat | smallint | ||
unit | character varying(6) | ||
howmany | integer | ||
payment | boolean | DEFAULT false |
Email to be sent out when recurring transaction is posted.
F-Key | Name | Type | Description |
---|---|---|---|
id | integer | PRIMARY KEY | |
formname | text | PRIMARY KEY | |
format | text | ||
message | text |
Template, printer etc. to print to when recurring transaction posts.
F-Key | Name | Type | Description |
---|---|---|---|
id | integer | PRIMARY KEY | |
formname | text | PRIMARY KEY | |
format | text | ||
printer | text |
F-Key | Name | Type | Description |
---|---|---|---|
roleid | oid | ||
member | oid | ||
grantor | oid | ||
admin_option | boolean | ||
rolname | name | ||
rolsuper | boolean | ||
rolinherit | boolean | ||
rolcreaterole | boolean | ||
rolcreatedb | boolean | ||
rolcatupdate | boolean | ||
rolcanlogin | boolean | ||
rolreplication | boolean | ||
rolconnlimit | integer | ||
rolpassword | text | ||
rolvaliduntil | timestamp with time zone | ||
rolconfig | text[] | ||
oid | oid |
SELECT m.roleid , m.member , m.grantor , m.admin_option , a.rolname , a.rolsuper , a.rolinherit , a.rolcreaterole , a.rolcreatedb , a.rolcatupdate , a.rolcanlogin , a.rolreplication , a.rolconnlimit , a.rolpassword , a.rolvaliduntil , a.rolconfig , a.oid FROM (pg_auth_members m JOIN pg_roles a ON ( (m.roleid = a.oid) ) );
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | UNIQUE NOT NULL | |
salutation | text | PRIMARY KEY |
Tables referencing this one via Foreign Key Constraints:
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.
F-Key | Name | Type | Description |
---|---|---|---|
session_id | serial | PRIMARY KEY | |
token | character varying(32) | ||
last_used | timestamp without time zone | DEFAULT now() | |
ttl | integer | NOT NULL DEFAULT 3600 | |
public.users.id | users_id | integer | NOT NULL |
notify_pasword | interval | NOT NULL DEFAULT '7 days'::interval |
Name | Constraint |
---|---|
session_token_check | CHECK ((length((token)::text) = 32)) |
Tables referencing this one via Foreign Key Constraints:
This can be used SIC codes or any equivalent, such as ISIC, NAICS, etc.
F-Key | Name | Type | Description |
---|---|---|---|
code | character varying(6) | PRIMARY KEY | |
sictype | character(1) | ||
description | text |
Whether AR/AP transactions and invoices have been emailed and/or printed
F-Key | Name | Type | Description |
---|---|---|---|
trans_id | integer | PRIMARY KEY | |
formname | text | PRIMARY KEY | |
printed | boolean | DEFAULT false | |
emailed | boolean | DEFAULT false | |
spoolfile | text |
Information on tax rates.
F-Key | Name | Type | Description |
---|---|---|---|
public.account.id public.account.id | chart_id | integer | PRIMARY KEY |
rate | numeric | ||
minvalue | numeric | ||
maxvalue | numeric | ||
taxnumber | text | ||
validto | timestamp without time zone | PRIMARY KEY DEFAULT 'infinity'::timestamp without time zone | |
pass | integer |
NOT NULL
This is an integer indicating the pass of the tax. This is to support cumultative sales tax rules (for example, Quebec charging taxes on the federal taxes collected). |
|
public.taxmodule.taxmodule_id | taxmodule_id | integer | NOT NULL DEFAULT 1 |
This stores extended information for manual tax calculations.
F-Key | Name | Type | Description |
---|---|---|---|
tax_basis | numeric | ||
rate | numeric | ||
public.journal_line.id | entry_id | integer | PRIMARY KEY |
F-Key | Name | Type | Description |
---|---|---|---|
taxcategory_id | serial | PRIMARY KEY | |
taxcategoryname | text | NOT NULL | |
public.taxmodule.taxmodule_id | taxmodule_id | integer | NOT NULL |
Tables referencing this one via Foreign Key Constraints:
This is used to store information on tax modules. the module name is used to determine the Perl class for the taxes.
F-Key | Name | Type | Description |
---|---|---|---|
taxmodule_id | serial | PRIMARY KEY | |
taxmodulename | text | NOT NULL |
Tables referencing this one via Foreign Key Constraints:
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.
F-Key | Name | Type | Description |
---|---|---|---|
id | integer | PRIMARY KEY | |
table_name | text | ||
public.session.session_id | locked_by | integer |
This should only be used in pessimistic locking measures as required by large batch work flows. |
approved | boolean | ||
public.entity.id | approved_by | integer | |
approved_at | timestamp without time zone |
Tables referencing this one via Foreign Key Constraints:
transactions_locked_by_i locked_byabstract table for manual translation data. Should have zero rows.
F-Key | Name | Type | Description |
---|---|---|---|
trans_id | integer | PRIMARY KEY | |
language_code | character varying(6) | PRIMARY KEY | |
description | text |
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
date_from | date | ||
date_to | date | ||
description | text | NOT NULL | |
public.trial_balance__yearend_types.type | yearend | text | NOT NULL |
Tables referencing this one via Foreign Key Constraints:
F-Key | Name | Type | Description |
---|---|---|---|
public.trial_balance.id | report_id | integer | NOT NULL |
public.account.id | account_id | integer | NOT NULL |
F-Key | Name | Type | Description |
---|---|---|---|
public.trial_balance.id | report_id | integer | NOT NULL |
public.account_heading.id | heading_id | integer | NOT NULL |
F-Key | Name | Type | Description |
---|---|---|---|
type | text | PRIMARY KEY |
Tables referencing this one via Foreign Key Constraints:
This view provides join and approval information for transactions.
F-Key | Name | Type | Description |
---|---|---|---|
id | integer | ||
reference | text | ||
entity_credit_account | integer | ||
table | text | ||
approved | boolean |
( SELECT gl.id , gl.reference , NULL::integer AS entity_credit_account ,'gl'::text AS "table" , gl.approved FROM gl UNION ALLSELECT ap.id , ap.invnumber AS reference , ap.entity_credit_account ,'ap'::text AS "table" , ap.approved FROM ap ) UNION ALLSELECT ar.id , ar.invnumber AS reference , ar.entity_credit_account ,'ar'::text AS "table" , ar.approved FROM ar;
F-Key | Name | Type | Description |
---|---|---|---|
id | integer | ||
username | character varying(30) | ||
created | date |
SELECT u.id , u.username , e.created FROM (entity e JOIN users u ON ( (u.entity_id = e.id) ) );
This table sets the basic preferences for formats, languages, printers, and user-selected stylesheets.
F-Key | Name | Type | Description |
---|---|---|---|
public.users.id | id | integer | PRIMARY KEY |
public.language.code | language | character varying(6) | |
stylesheet | text | NOT NULL DEFAULT 'ledgersmb.css'::text | |
printer | text | ||
dateformat | text | NOT NULL DEFAULT 'yyyy-mm-dd'::text | |
numberformat | text | NOT NULL DEFAULT '1000.00'::text |
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | UNIQUE NOT NULL | |
username | character varying(30) | PRIMARY KEY | |
notify_password | interval | NOT NULL DEFAULT '7 days'::interval | |
public.entity.id | entity_id | integer | NOT NULL |
Tables referencing this one via Foreign Key Constraints:
Mapping transactions to batches for batch approval.
F-Key | Name | Type | Description |
---|---|---|---|
public.transactions.id | trans_id | integer | NOT NULL |
public.batch.id | batch_id | integer | NOT NULL |
id | serial |
PRIMARY KEY
This is simply a surrogate key for easy reference. |
|
public.batch_class.id | batch_class | integer |
NOT NULL
This is the authoritative class of the voucher. |
Tables referencing this one via Foreign Key Constraints:
F-Key | Name | Type | Description |
---|---|---|---|
id | serial | PRIMARY KEY | |
description | text |
Tables referencing this one via Foreign Key Constraints:
An extension to the journal_entry table to track transactionsactions which close out the books at yearend.
F-Key | Name | Type | Description |
---|---|---|---|
public.gl.id | trans_id | integer | PRIMARY KEY |
reversed | boolean | DEFAULT false | |
transdate | date |
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;
This deletes an account with the id specified. If the account has transactions associated with it, it will fail and raise a foreign key constraint.
BEGIN DELETE FROM account_link WHERE account_id = in_id; DELETE FROM account WHERE id = in_id; RETURN FOUND; END;
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);
Returns the account where the accno field matches (excatly) the in_accno provided.
select * from account where accno = $1;
Returns set of accounts where the tax attribute is true.
SELECT * FROM account WHERE tax is true ORDER BY accno;
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;
SELECT * FROM account ORDER BY heading;
Returns the account balance at a given point in time, calculating forward from most recent check point.
DECLARE balance numeric; BEGIN SELECT coalesce(sum(ac.amount) + cp.amount, sum(ac.amount)) INTO balance FROM acc_trans ac JOIN (select id, approved from ar union select id, approved from ap union select id, approved from gl) a ON (a.id = ac.trans_id) LEFT JOIN (select account_id, end_date, amount from account_checkpoint WHERE account_id = in_account_id AND end_date < in_transdate ORDER BY end_date desc limit 1 ) cp ON (cp.account_id = ac.chart_id) WHERE ac.chart_id = in_account_id AND ac.transdate > coalesce(cp.end_date, ac.transdate - '1 day'::interval) and ac.approved and a.approved and ac.transdate <= in_transdate GROUP BY cp.amount, ac.chart_id; RETURN balance; END;
This deletes existing account_link entries, where the account_link.description is not designated as a custom one in the account_link_description table. If no account heading is provided, the account heading which has an accno field closest to but prior (by collation order) is used. Then it saves the account information, and rebuilds the account_link records based on the in_link array.
DECLARE t_heading_id int; t_link record; t_id int; t_tax bool; BEGIN SELECT count(*) > 0 INTO t_tax FROM tax WHERE in_id = chart_id; t_tax := t_tax OR in_tax; -- check to ensure summary accounts are exclusive -- necessary for proper handling by legacy code FOR t_link IN SELECT description FROM account_link_description WHERE summary='t' LOOP IF t_link.description = ANY (in_link) and array_upper(in_link, 1) > 1 THEN RAISE EXCEPTION 'Invalid link settings: Summary'; END IF; END LOOP; -- heading settings IF in_heading IS NULL THEN SELECT id INTO t_heading_id FROM account_heading WHERE accno < in_accno order by accno desc limit 1; ELSE t_heading_id := in_heading; END IF; -- don't remove custom links. DELETE FROM account_link WHERE account_id = in_id and description in ( select description from account_link_description where custom = 'f'); UPDATE account SET accno = in_accno, description = in_description, category = in_category, gifi_accno = in_gifi_accno, heading = t_heading_id, contra = in_contra, obsolete = in_obsolete, tax = t_tax, is_temp = in_is_temp WHERE id = in_id; IF FOUND THEN t_id := in_id; ELSE -- can't obsolete on insert, but this can be changed if users -- request it --CT INSERT INTO account (accno, description, category, gifi_accno, heading, contra, tax, is_temp) VALUES (in_accno, in_description, in_category, in_gifi_accno, t_heading_id, in_contra, in_tax, in_is_temp); t_id := currval('account_id_seq'); END IF; FOR t_link IN select in_link[generate_series] AS val FROM generate_series(array_lower(in_link, 1), array_upper(in_link, 1)) LOOP INSERT INTO account_link (account_id, description) VALUES (t_id, t_link.val); END LOOP; RETURN t_id; END;
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;
Returns an entry from the chart view which matches the id requested, and which is an account, not a heading.
SELECT * from chart where id = $1 and charttype = 'A';
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;
Returns a list of all account headings, currently ordered by account number.
SELECT * FROM account_heading order by accno;
Returns an entry from the chart view which matches the id requested, and which is a heading, not an account.
DECLARE account chart%ROWTYPE; BEGIN SELECT * INTO account FROM chart WHERE id = in_id AND charttype = 'H'; RETURN account; END;
Lists all existing account headings.
SELECT * FROM account_heading order by accno;
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;
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;
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;
This function inserts the arguments into lsmb_group_grants for future reference and issues the db-level grant. It then returns true if there are no exceptions.
BEGIN PERFORM * FROM lsmb_group_grants WHERE group_name = in_group_name AND granted_role = in_role_name; IF NOT FOUND THEN INSERT INTO lsmb_group_grants(group_name, granted_role) VALUES (in_group_name, in_role_name); END IF; EXECUTE 'GRANT ' || quote_ident(in_role_name) || ' TO ' || quote_literal('lsmb_' || t_dbname || '__' || in_group_name); RETURN TRUE; END;
declare stmt TEXT; a_role name; a_user name; BEGIN -- Issue the grant select rolname into a_role from pg_roles where rolname = in_role; IF NOT FOUND THEN RAISE EXCEPTION 'Cannot grant permissions of a non-existant role.'; END IF; select rolname into a_user from pg_roles where rolname = in_username; IF NOT FOUND THEN RAISE EXCEPTION 'Cannot grant permissions to a non-existant user.'; END IF; stmt := 'GRANT '|| quote_ident(in_role) ||' to '|| quote_ident(in_username); EXECUTE stmt; insert into lsmb_roles (user_id, role) SELECT id, in_role from users where username = in_username; return 1; END;
DECLARE stmt text; t_dbname text; BEGIN t_dbname := current_database(); stmt := 'create role '|| quote_ident('lsmb_' || t_dbname || '__' || in_group_name); execute stmt; INSERT INTO lsmb_group (role_name) values (quote_literal('lsmb_' || t_dbname || '__' || in_group_name)); return 1; END;
Deletes the input group from the database. Not designed to be used to remove a login-capable user.
DECLARE stmt text; a_role role_view; t_dbname text; BEGIN t_dbname := current_database(); select * into a_role from role_view where rolname = in_group_name; if not found then return 'f'::bool; else stmt := 'drop role lsmb_' || quote_ident(t_dbname || '__' || in_group_name); execute stmt; return 't'::bool; end if; END;
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;
Drops the session identified, releasing all locks held.
BEGIN DELETE FROM "session" WHERE session_id = in_session_id; RETURN FOUND; END;
DECLARE v_rol record; t_dbname text; BEGIN t_dbname := current_database(); FOR v_rol in SELECT * from pg_roles where rolname ~ ('^lsmb_' || t_dbname || '__') and rolcanlogin is false order by rolname ASC LOOP RETURN NEXT v_rol; END LOOP; END;
Returns a set of roles that a user is a part of.
declare u_role record; a_user users; begin select * into a_user from admin__get_user(in_user_id); FOR u_role IN select r.rolname from pg_roles r, (select m.roleid from pg_auth_members m, pg_roles b where m.member = b.oid and b.rolname = a_user.username ) as ar where r.oid = ar.roleid LOOP RETURN NEXT u_role.rolname::text; END LOOP; RETURN; end;
Returns a set of (only one) user specified by the id.
DECLARE a_user users; BEGIN select * into a_user from users where entity_id = in_entity_id; return next a_user; return; END;
-- 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;
Returns true if user is set up in LedgerSMB. False otherwise.
BEGIN PERFORM * from users where username = in_user; RETURN found; END;
SELECT * FROM lsmb_group_grants WHERE group_name = $1 ORDER BY granted_role;
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;
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;
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;
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;
Creates a user and relevant records in LedgerSMB and PostgreSQL.
DECLARE a_user users; v_user_id int; p_id int; l_id int; stmt text; t_is_role bool; t_is_user bool; BEGIN -- WARNING TO PROGRAMMERS: This function runs as the definer and runs -- utility statements via EXECUTE. -- PLEASE BE VERY CAREFUL ABOUT SQL-INJECTION INSIDE THIS FUNCTION. PERFORM rolname FROM pg_roles WHERE rolname = in_username; t_is_role := found; t_is_user := admin__is_user(in_username); IF t_is_role is true and t_is_user is false and in_import is false THEN RAISE EXCEPTION 'Duplicate user'; END IF; if t_is_role and in_password is not null then execute 'ALTER USER ' || quote_ident( in_username ) || ' WITH ENCRYPTED PASSWORD ' || quote_literal (in_password) || $e$ valid until $e$ || quote_literal(now() + '1 day'::interval); elsif in_import is false AND t_is_user is false AND in_password IS NULL THEN RAISE EXCEPTION 'No password'; elsif t_is_role is false and in_import IS FALSE THEN -- create an actual user execute 'CREATE USER ' || quote_ident( in_username ) || ' WITH ENCRYPTED PASSWORD ' || quote_literal (in_password) || $e$ valid until $e$ || quote_literal(now() + '1 day'::interval); END IF; select * into a_user from users lu where lu.id = in_id; IF FOUND THEN return a_user.id; ELSE -- Insert cycle --- The entity is expected to already BE created. See admin.pm. v_user_id := nextval('users_id_seq'); insert into users (id, username, entity_id) VALUES ( v_user_id, in_username, in_entity_id ); insert into user_preference (id) values (v_user_id); IF NOT exists(SELECT * FROM entity_employee WHERE entity_id = in_entity_id) THEN INSERT into entity_employee (entity_id) values (in_entity_id); END IF; -- Finally, issue the create user statement return v_user_id ; END IF; END;
Returns a list of users matching search criteria. Nulls match all values. only username is not an exact match.
DECLARE t_return_row user_result; BEGIN FOR t_return_row IN SELECT u.id, u.username, p.first_name, p.last_name, e.ssn, e.dob FROM users u JOIN person p ON (u.entity_id = p.entity_id) JOIN entity_employee e ON (e.entity_id = p.entity_id) WHERE u.username LIKE '%' || coalesce(in_username,'') || '%' AND (p.first_name = in_first_name or in_first_name is null) AND (p.last_name = in_last_name or in_last_name is null) AND (in_ssn is NULL or in_ssn = e.ssn) AND (e.dob = in_dob::date or in_dob is NULL) LOOP RETURN NEXT t_return_row; END LOOP; END;
A basic array aggregate to take elements and return a one-dimensional array. Example: SELECT as_array(id) from entity_class;
aggregate_dummy
Retrieves a given asset either by id or tag. Both are complete matches. Note that the behavior is undefined if both id and tag are provided.
DECLARE ret_val asset_item; BEGIN SELECT * into ret_val from asset_item WHERE id = in_id OR in_tag = tag ORDER BY id desc limit 1; return ret_val; END;
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;
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;
Searches for assets. Nulls match all records. Asset class is exact, as is purchase date, purchase value, and salvage value. Tag and description are partial matches.
DECLARE out_val asset_item; BEGIN FOR out_val IN SELECT * FROM asset_item WHERE (in_asset_class is null or asset_class_id = in_asset_class) AND (in_description is null or description LIKE '%' || in_description || '%') and (in_tag is not null or tag like '%'||in_tag||'%') AND (in_purchase_date is null or purchase_date = in_purchase_date) AND (in_purchase_value is null or in_purchase_value = purchase_value) AND (in_usable_life is null or in_usable_life = usable_life) AND (in_salvage_value is null OR in_salvage_value = salvage_value) LOOP RETURN NEXT out_val; END LOOP; END;
returns the row from asset_class identified by in_id.
DECLARE ret_val asset_class; BEGIN SELECT * INTO ret_val FROM asset_class WHERE id = in_id; RETURN ret_val; END;
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;
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;
Returns the depreciation method associated with the asset class.
SELECT * from asset_dep_method WHERE id = (select method from asset_class where id = $1);
Returns a set of asset_dep_methods ordered by the method label.
SELECT * FROM asset_dep_method ORDER BY method;
Returns an alphabetical list of asset classes.
SELECT * FROM asset_class ORDER BY label;
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;
Returns a list of matching asset classes. The account id's are exact matches as is the method, but the label is a partial match. NULL's match all.
DECLARE out_var asset_class_result; BEGIN FOR out_var IN SELECT ac.id, ac.asset_account_id, aa.accno, aa.description, ad.accno, ad.description, m.method, ac.method, ac.label FROM asset_class ac JOIN account aa ON (aa.id = ac.asset_account_id) JOIN account ad ON (ad.id = ac.dep_account_id) JOIN asset_dep_method m ON (ac.method = m.id) WHERE (in_asset_account_id is null or in_asset_account_id = ac.asset_account_id) AND (in_dep_account_id is null OR in_dep_account_id = ac.dep_account_id) AND (in_method is null OR in_method = ac.method) AND (in_label IS NULL OR ac.label LIKE '%' || in_label || '%') ORDER BY label LOOP RETURN NEXT out_var; END LOOP; END;
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;
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;
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;
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;
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;
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;
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;
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;
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');
Returns a list of matching asset items. Nulls match all records. Tag and description allow for partial match. All other matches are exact.
DECLARE retval asset_item; BEGIN FOR retval IN SELECT * FROM asset_item WHERE (id = in_id or in_id is null) and (asset_class_id = in_asset_class or in_asset_class is null) and (description like '%'||in_description||'%' or in_description is null) and (tag like '%' || in_tag || '%' or in_tag is null) and (purchase_value = in_purchase_value or in_purchase_value is null) and (in_purchase_date = purchase_date or in_purchase_date is null) and (start_depreciation = in_start_depreciation or in_start_depreciation is null) and (in_warehouse_id = location_id OR in_warehouse_id is null) and (department_id = in_department_id or in_department_id is null) and (in_invoice_id = invoice_id OR in_invoice_id IS NULL) and (asset_account_id = in_asset_account_id or in_asset_account_id is null) and (dep_account_id = in_dep_account_id or in_dep_account_id is null) LOOP return next retval; end loop; END;
Returns the current net book value report.
SELECT ai.id, ai.tag, ai.description, coalesce(ai.start_depreciation, ai.purchase_date), adm.short_name, ai.usable_life - months_passed(coalesce(ai.start_depreciation, ai.purchase_date), coalesce(max(r.report_date), ai.start_depreciation, ai.purchase_date))/ 12, ai.purchase_value - ai.salvage_value, ai.salvage_value, max(r.report_date), sum(rl.amount), ai.purchase_value - sum(rl.amount) FROM asset_item ai JOIN asset_class ac ON (ai.asset_class_id = ac.id) JOIN asset_dep_method adm ON (adm.id = ac.method) LEFT JOIN asset_report_line rl ON (ai.id = rl.asset_id) LEFT JOIN asset_report r on (rl.report_id = r.id) WHERE r.id IS NULL OR r.approved_at IS NOT NULL GROUP BY ai.id, ai.tag, ai.description, ai.start_depreciation, ai.purchase_date, adm.short_name, ai.usable_life, ai.purchase_value, salvage_value HAVING (NOT 2 = ANY(as_array(r.report_class))) AND (NOT 4 = ANY(as_array(r.report_class))) OR max(r.report_class) IS NULL ORDER BY ai.id, ai.tag, ai.description;
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;
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;
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');
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;
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;
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;
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;
Returns the asset_report line identified by id.
select * from asset_report where id = $1;
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;
Returns a list of asset_disposal_method items ordered by label.
SELECT * FROM asset_disposal_method order by label;
Lists all asset expense reports.
SELECT * FROM account__get_by_link_desc('asset_expense');
Returns a list of gain accounts for asset depreciation and disposal reports.
SELECT * FROM account__get_by_link_desc('asset_gain');
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;
Returns a list of loss accounts for asset depreciation and disposal reports.
SELECT * FROM account__get_by_link_desc('asset_loss');
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;
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;
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;
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;
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;
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;
Returns a list of batches and amounts processed on the batch. Nulls match all values. in_date_from and in_date_to specify date ranges. in_description is a partial match. All other criteria are exact matches.
DECLARE out_value batch_list_item; BEGIN FOR out_value IN SELECT b.id, c.class, b.control_code, b.description, u.username, b.created_on, b.default_date, sum( CASE WHEN vc.id = 5 AND al.amount < 0 -- GL THEN al.amount WHEN vc.id = 1 THEN ap.amount WHEN vc.id = 2 THEN ap.amount ELSE 0 END) AS transaction_total, sum( CASE WHEN alc.link = 'AR' AND vc.id IN (6, 7) THEN al.amount WHEN alc.link = 'AP' AND vc.id IN (3, 4) THEN al.amount * -1 ELSE 0 END ) AS payment_total FROM batch b JOIN batch_class c ON (b.batch_class_id = c.id) LEFT JOIN users u ON (u.entity_id = b.created_by) LEFT JOIN voucher v ON (v.batch_id = b.id) LEFT JOIN batch_class vc ON (v.batch_class = vc.id) LEFT JOIN ar ON (vc.id = 2 AND v.trans_id = ar.id) LEFT JOIN ap ON (vc.id = 1 AND v.trans_id = ap.id) LEFT JOIN acc_trans al ON ((vc.id = 5 AND v.trans_id = al.trans_id) OR (vc.id IN (3, 4, 6, 7) AND al.voucher_id = v.id)) LEFT JOIN chart alc ON (al.chart_id = alc.id) WHERE (c.id = in_class_id OR in_class_id IS NULL) AND (b.description LIKE '%' || in_description || '%' OR in_description IS NULL) AND (in_created_by_eid = b.created_by OR in_created_by_eid IS NULL) AND ((in_approved = false OR in_approved IS NULL AND approved_on IS NULL) OR (in_approved = true AND approved_on IS NOT NULL) ) and (in_date_from IS NULL or b.default_date >= in_date_from) and (in_date_to IS NULL or b.default_date <= in_date_to) GROUP BY b.id, c.class, b.description, u.username, b.created_on, b.control_code, b.default_date HAVING (in_amount_gt IS NULL OR sum(coalesce(ar.amount - ar.paid, ap.amount - ap.paid, al.amount)) >= in_amount_gt) AND (in_amount_lt IS NULL OR sum(coalesce(ar.amount - ar.paid, ap.amount - ap.paid, al.amount)) <= in_amount_lt) ORDER BY b.control_code, b.description LOOP RETURN NEXT out_value; END LOOP; END;
Inserts the batch into the table.
BEGIN INSERT INTO batch (batch_class_id, default_date, description, control_code, created_by) VALUES ((SELECT id FROM batch_class WHERE class = in_batch_class), in_batch_date, in_description, in_batch_number, (select entity_id FROM users WHERE username = session_user)); return currval('batch_id_seq'); END;
If the batch is found and unapproved, deletes it and returns 1. Otherwise raises an exception.
DECLARE t_transaction_ids int[]; BEGIN -- Adjust AR/AP tables for payment and payment reversal vouchers -- voucher_id is only set in acc_trans on payment/receipt vouchers and -- their reversals. -CT perform * from batch where id = in_batch_id and approved_on IS NULL; IF NOT FOUND THEN RAISE EXCEPTION 'Batch not found'; END IF; update ar set paid = amount + (select sum(amount) from acc_trans join chart ON (acc_trans.chart_id = chart.id) where link = 'AR' AND trans_id = ar.id AND (voucher_id IS NULL OR voucher_id NOT IN (select id from voucher WHERE batch_id = in_batch_id))) where id in (select trans_id from acc_trans where voucher_id IN (select id from voucher where batch_id = in_batch_id)); update ap set paid = amount - (select sum(amount) from acc_trans join chart ON (acc_trans.chart_id = chart.id) where link = 'AP' AND trans_id = ap.id AND (voucher_id IS NULL OR voucher_id NOT IN (select id from voucher WHERE batch_id = in_batch_id))) where id in (select trans_id from acc_trans where voucher_id IN (select id from voucher where batch_id = in_batch_id)); DELETE FROM ac_tax_form WHERE entry_id IN (select entry_id from acc_trans where voucher_id in (select id from voucher where batch_id = in_batch_id) ); DELETE FROM acc_trans WHERE voucher_id IN (select id FROM voucher where batch_id = in_batch_id); -- The rest of this function involves the deletion of actual -- transactions, vouchers, and batches, and jobs which are in progress. -- -CT SELECT as_array(trans_id) INTO t_transaction_ids FROM voucher WHERE batch_id = in_batch_id AND batch_class IN (1, 2, 5); DELETE FROM ac_tax_form WHERE entry_id in (select entry_id from acc_trans where trans_id = any(t_transaction_ids)); DELETE FROM acc_trans WHERE trans_id = ANY(t_transaction_ids); DELETE FROM ap WHERE id = ANY(t_transaction_ids); DELETE FROM gl WHERE id = ANY(t_transaction_ids); DELETE FROM voucher WHERE batch_id = in_batch_id; DELETE FROM batch WHERE id = in_batch_id; DELETE FROM transactions WHERE id = ANY(t_transaction_ids); RETURN 1; END;
returns the batch class id associated with the in_type label provided.
SELECT id FROM batch_class WHERE class = $1;
Returns a sim[ple set of user objects. This should be renamed so that it is more obvious it is a general purpose function.
DECLARE out_record users%ROWTYPE; BEGIN FOR out_record IN SELECT * from users WHERE entity_id IN (select created_by from batch) LOOP RETURN NEXT out_record; END LOOP; END;
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;
Posts the specified batch to the books. Only posted batches should show up on standard financial reports.
BEGIN UPDATE ar SET approved = true WHERE id IN (select trans_id FROM voucher WHERE batch_id = in_batch_id AND batch_class = 2); UPDATE ap SET approved = true WHERE id IN (select trans_id FROM voucher WHERE batch_id = in_batch_id AND batch_class = 1); UPDATE gl SET approved = true WHERE id IN (select trans_id FROM voucher WHERE batch_id = in_batch_id AND batch_class = 5); UPDATE acc_trans SET approved = true WHERE voucher_id IN (select id FROM voucher WHERE batch_id = in_batch_id AND batch_class IN (3, 4, 6, 7)); UPDATE batch SET approved_on = now(), approved_by = (select entity_id FROM users WHERE username = SESSION_USER) WHERE id = in_batch_id; RETURN now()::date; END;
This is a full search for the batches, listing them by amount processed. in_amount_gt and in_amount_lt provide a range to search for. in_description is a partial match field. Other fields are exact matches. NULLs match all values.
DECLARE out_value batch_list_item; BEGIN FOR out_value IN SELECT b.id, c.class, b.control_code, b.description, u.username, b.created_on, b.default_date, 0, 0 FROM batch b JOIN batch_class c ON (b.batch_class_id = c.id) JOIN users u ON (u.entity_id = b.created_by) LEFT JOIN voucher v ON (v.batch_id = b.id) where v.id is null and(u.entity_id = in_created_by_eid or in_created_by_eid is null) and (in_description is null or b.description like '%' || in_description || '%') and (in_class_id is null or c.id = in_class_id) GROUP BY b.id, c.class, b.description, u.username, b.created_on, b.control_code, b.default_date ORDER BY b.control_code, b.description LOOP RETURN NEXT out_value; END LOOP; END;
This performs a simple search of open batches created by the entity_id in question. This is used to pull up batches that were currently used so that they can be picked up and more vouchers added. NULLs match all values. in_description is a partial match All other inouts are exact matches.
DECLARE out_value batch_list_item; BEGIN FOR out_value IN SELECT b.id, c.class, b.control_code, b.description, u.username, b.created_on, b.default_date, NULL FROM batch b JOIN batch_class c ON (b.batch_class_id = c.id) LEFT JOIN users u ON (u.entity_id = b.created_by) WHERE (c.id = in_class_id OR in_class_id IS NULL) AND (b.description LIKE '%' || in_description || '%' OR in_description IS NULL) AND (in_created_by_eid = b.created_by OR in_created_by_eid IS NULL) AND ((in_approved = false OR in_approved IS NULL AND approved_on IS NULL) OR (in_approved = true AND approved_on IS NOT NULL) ) GROUP BY b.id, c.class, b.description, u.username, b.created_on, b.control_code, b.default_date LOOP RETURN NEXT out_value; END LOOP; END;
UPDATE budget_info set approved_at = now(), approved_by = person__get_my_entity_id() WHERE id = $1; SELECT budget__get_info($1);
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;
This retrieves the budget lines associated with a budget.
SELECT * FROM budget_line where budget_id = $1;
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;
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;
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)
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;
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;
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;
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);
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;
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;
Returns a list of all business types. Ordered by description by default.
DECLARE out_row business%ROWTYPE; BEGIN FOR out_row IN SELECT * FROM business ORDER BY description LOOP RETURN NEXT out_row; END LOOP; END;
SELECT * FROM business_unit where id = $1;
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;
This function retUrns a list of all units (projects, departments, funds, etc) active on the in_active_on date, where in_credit_id matches the credit id of the customer or vendor requested, and where in_business_uni_class_id is the class id of the class of business units (1 for department, 2 for project, etc). With the exception of in_business_unit_class_id, the null matches all records.
BEGIN RETURN QUERY SELECT * FROM business_unit WHERE (in_active_on BETWEEN coalesce(start_date, in_active_on) AND coalesce(end_date, in_active_on) OR in_active_on IS NULL) AND (in_credit_id = credit_id OR (credit_id IS NULL and in_strict_credit IS NOT TRUE) OR (in_credit_id IS NULL)) AND class_id = in_business_unit_class_id ORDER BY control_code; END;
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;
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;
SELECT * FROM lsmb_module WHERE id IN (select module_id from bu_class_to_module where bu_class_id = $1) ORDER BY id;
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;
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;
SELECT * FROM business_unit WHERE id = $1;
This function returns the cash account acording with in_account_class which must be 1 or 2. If in_account_class is 1 then it returns a list of AP accounts, and if in_account_class is 2, then a list of AR accounts.
DECLARE out_row chart%ROWTYPE; BEGIN IF in_account_class NOT IN (1, 2) THEN RAISE EXCEPTION 'Bad Account Type'; END IF; FOR out_row IN SELECT * FROM chart WHERE link = CASE WHEN in_account_class = 1 THEN 'AP' WHEN in_account_class = 2 THEN 'AR' END ORDER BY accno LOOP RETURN NEXT out_row; END LOOP; END;
Generates a list of chart view entries.
DECLARE out_row chart%ROWTYPE; BEGIN FOR out_row IN SELECT * FROM chart ORDER BY accno LOOP RETURN next out_row; END LOOP; END;
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;
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;
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;
This returns a list of account entries where the description or account number begins with in_search. If in_link_desc is provided, the list is further filtered by which accounts are set to an account_link.description equal to that provided.
DECLARE out_row account%ROWTYPE; BEGIN FOR out_row IN SELECT * FROM account WHERE (accno ~* ('^'||in_search) OR description ~* ('^'||in_search)) AND (in_link_desc IS NULL or id in (select account_id from account_link where description = in_link_desc)) AND not obsolete ORDER BY accno LOOP RETURN next out_row; END LOOP; END;
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;
DECLARE t_alloc numeric := 0; t_cogs numeric := 0; t_inv invoice; t_cp account_checkpoint; t_ar ar; t_avail numeric; BEGIN IF in_qty > 0 THEN return cogs__reverse_ap(in_parts_id, in_qty * -1) * in_lastcost; END IF; SELECT * INTO t_cp FROM account_checkpoint ORDER BY end_date DESC LIMIT 1; FOR t_inv IN SELECT i.* FROM invoice i JOIN ar a ON a.id = i.trans_id WHERE qty + allocated > 0 and parts_id = in_parts_id ORDER BY a.transdate, a.id, i.id LOOP t_avail := t_inv.qty + t_inv.allocated; SELECT * INTO t_ar FROM ar WHERE id = t_inv.trans_id; IF t_alloc < in_qty THEN RAISE EXCEPTION 'TOO MANY ALLOCATED'; ELSIF t_alloc = in_qty THEN return t_alloc; ELSIF (in_qty + t_alloc) * -1 <= t_avail THEN UPDATE invoice SET allocated = allocated + (in_qty + t_alloc) WHERE id = t_inv.id; INSERT INTO acc_trans (chart_id, transdate, amount, invoice_id, approved, trans_id) SELECT expense_accno_id, CASE WHEN t_ar.transdate > t_cp.end_date THEN t_ar.transdate ELSE t_cp.end_date + '1 day'::interval END, (in_qty + t_alloc) * in_lastcost, t_inv.id, true, t_inv.trans_id FROM parts WHERE id = t_inv.parts_id AND inventory_accno_id IS NOT NULL AND expense_accno_id IS NOT NULL UNION SELECT income_accno_id, CASE WHEN t_ar.transdate > t_cp.end_date THEN t_ar.transdate ELSE t_cp.end_date + '1 day'::interval END, -1 * (in_qty + t_alloc) * in_lastcost, t_inv.id, true, t_inv.trans_id FROM parts WHERE id = t_inv.parts_id AND inventory_accno_id IS NOT NULL AND expense_accno_id IS NOT NULL; t_cogs := t_cogs + (in_qty + t_alloc) * in_lastcost; return in_qty * -1; ELSE UPDATE invoice SET allocated = qty * -1 WHERE id = t_inv.id; t_cogs := t_cogs + t_avail * in_lastcost; INSERT INTO acc_trans (chart_id, transdate, amount, invoice_id, approved, trans_id) SELECT expense_accno_id, CASE WHEN t_ar.transdate > t_cp.end_date THEN t_ar.transdate ELSE t_cp.end_date + '1 day'::interval END, -1 * t_avail * in_lastcost, t_inv.id, true, t_inv.trans_id FROM parts WHERE id = t_inv.parts_id AND inventory_accno_id IS NOT NULL AND expense_accno_id IS NOT NULL UNION SELECT income_accno_id, CASE WHEN t_ar.transdate > t_cp.end_date THEN t_ar.transdate ELSE t_cp.end_date + '1 day'::interval END, -t_avail * in_lastcost, t_inv.id, true, t_inv.trans_id FROM parts WHERE id = t_inv.parts_id AND inventory_accno_id IS NOT NULL AND expense_accno_id IS NOT NULL; t_alloc := t_alloc + t_avail; t_cogs := t_cogs + t_avail * in_lastcost; END IF; END LOOP; RETURN t_alloc; END;
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;
This function accepts a parts_id and a quantity, and iterates through AP records in order, calculating COGS on a FIFO basis and returning it to the application to attach to the current transaction. Return values are an array of {allocated, cogs}.
DECLARE t_alloc numeric := 0; t_cogs numeric := 0; t_inv invoice; t_avail numeric; BEGIN FOR t_inv IN SELECT i.* FROM invoice i JOIN (select id, approved, transdate from ap union select id, approved, transdate from gl) a ON a.id = i.trans_id WHERE qty + allocated < 0 AND i.parts_id = in_parts_id ORDER BY a.transdate asc, a.id asc, i.id asc LOOP t_avail := (t_inv.qty + t_inv.allocated) * -1; IF t_alloc > in_qty THEN RAISE EXCEPTION 'TOO MANY ALLOCATED'; ELSIF t_alloc = in_qty THEN return ARRAY[t_alloc, t_cogs]; ELSIF (in_qty + t_alloc) <= t_avail THEN UPDATE invoice SET allocated = allocated + (in_qty - t_alloc) WHERE id = t_inv.id; t_cogs := t_cogs + (in_qty - t_alloc) * t_inv.sellprice; t_alloc := in_qty; return ARRAY[t_alloc, t_cogs]; ELSE UPDATE invoice SET allocated = qty * -1 WHERE id = t_inv.id; t_cogs := t_cogs + (t_avail * t_inv.sellprice); t_alloc := t_alloc + t_avail; END IF; END LOOP; RETURN ARRAY[t_alloc, t_cogs]; END;
DECLARE t_cogs numeric[]; t_inv invoice; t_part parts; t_ar ar; t_transdate date; BEGIN SELECT * INTO t_inv FROM invoice WHERE id = in_invoice_id; SELECT * INTO t_part FROM parts WHERE id = t_inv.parts_id; SELECT * INTO t_ar FROM ar WHERE id = t_inv.trans_id; IF t_inv.qty + t_inv.allocated = 0 THEN return 0; END IF; IF t_inv.qty > 0 THEN t_cogs := cogs__add_for_ar(t_inv.parts_id, t_inv.qty + t_inv.allocated); ELSE t_cogs := cogs__reverse_ar(t_inv.parts_id, t_inv.qty + t_inv.allocated); END IF; UPDATE invoice set allocated = allocated - t_cogs[1] WHERE id = in_invoice_id; SELECT CASE WHEN t_ar.transdate > max(end_date) THEN t_ar.transdate ELSE max(end_date) + '1 day'::interval END INTO t_transdate from account_checkpoint td; INSERT INTO acc_trans (trans_id, chart_id, approved, amount, transdate, invoice_id) VALUES (t_inv.trans_id, CASE WHEN t_inv.qty < 0 AND t_ar.is_return THEN t_part.returns_accno_id ELSE t_part.expense_accno_id END, TRUE, t_cogs[2] * -1, t_transdate, t_inv.id), (t_inv.trans_id, t_part.inventory_accno_id, TRUE, t_cogs[2], t_transdate, t_inv.id); RETURN t_cogs[1]; END;
This function iterates through invoice rows attached to ap transactions and allocates them on a first-in first-out basis. The sort of pseudo-"COGS" value is returned to the application for further handling.
DECLARE t_alloc numeric :=0; t_inv invoice; t_cogs numeric :=0; retval numeric[]; BEGIN RAISE NOTICE 'reversing AP: parts_id %, qty %', in_parts_id, in_qty; FOR t_inv IN SELECT i.* FROM invoice i JOIN ap a ON a.id = i.trans_id WHERE qty + allocated < 0 AND parts_id = in_parts_id ORDER BY a.transdate, a.id, i.id LOOP RAISE NOTICE 'id %, avail %, allocated %, requesting %', t_inv.id, t_inv.qty + t_inv.allocated, t_alloc, in_qty - t_alloc; IF t_alloc > in_qty THEN RAISE EXCEPTION 'TOO MANY ALLOCATED'; ELSIF t_alloc = in_qty THEN return ARRAY[t_alloc, t_cogs]; ELSIF (in_qty - t_alloc) <= -1 * (t_inv.qty + t_inv.allocated) THEN raise notice 'partial reversal'; UPDATE invoice SET allocated = allocated + (in_qty - t_alloc) WHERE id = t_inv.id; return ARRAY[in_qty * -1, t_cogs + (in_qty - t_alloc) * t_inv.sellprice]; ELSE raise notice 'total reversal'; UPDATE invoice SET allocated = qty * -1 WHERE id = t_inv.id; t_alloc := t_alloc - (t_inv.qty + t_inv.allocated); t_cogs := t_cogs - (t_inv.qty + t_inv.allocated) * t_inv.sellprice; END IF; END LOOP; RETURN ARRAY[t_alloc, t_cogs]; RAISE EXCEPTION 'TOO FEW TO ALLOCATE'; END;
This function accepts a part id and quantity to reverse. It then iterates backwards over AP related records, calculating COGS. This does not save COGS but rather returns it to the application to save. Return values are an array of {allocated, cogs}.
DECLARE t_alloc numeric := 0; t_cogs numeric := 0; t_inv invoice; t_avail numeric; BEGIN FOR t_inv IN SELECT i.* FROM invoice i JOIN (select id, approved, transdate from ap union select id, approved, transdate from gl) a ON a.id = i.trans_id WHERE allocated > 0 and a.approved and parts_id = in_parts_id ORDER BY a.transdate DESC, a.id DESC, i.id DESC LOOP t_avail := t_inv.allocated; IF t_alloc < in_qty THEN RAISE EXCEPTION 'TOO MANY ALLOCATED'; ELSIF t_alloc = in_qty THEN RETURN ARRAY[t_alloc, t_cogs]; ELSIF (in_qty - t_alloc) * -1 <= t_inv.allocated THEN raise notice 'partial reversal'; UPDATE invoice SET allocated = allocated + (in_qty - t_alloc) WHERE id = t_inv.id; t_cogs := t_cogs + (in_qty - t_alloc) * t_inv.sellprice; return ARRAY[t_alloc + (in_qty - t_alloc), t_cogs]; ELSE raise notice 'full reversal'; UPDATE invoice SET allocated = 0 WHERE id = t_inv.id; t_alloc := t_alloc + t_inv.allocated * -1; t_cogs := t_cogs + -1 * (t_inv.allocated) * t_inv.sellprice; END IF; END LOOP; RAISE EXCEPTION 'TOO FEW TO REVERSE'; END;
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;
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;
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;
select nextval('company_id_seq');
Saves a company. Returns the id number of the record stored.
DECLARE t_entity_id INT; t_company_id INT; t_control_code TEXT; t_retval COMPANY; BEGIN t_company_id := in_id; IF in_control_code IS NULL THEN t_control_code := setting_increment('company_control'); ELSE t_control_code := in_control_code; END IF; UPDATE entity SET name = in_legal_name, entity_class = in_entity_class, control_code = in_control_code WHERE id = in_entity_id; IF FOUND THEN t_entity_id = in_entity_id; ELSE INSERT INTO entity (name, entity_class, control_code,country_id) VALUES (in_legal_name, in_entity_class, t_control_code,in_country_id); t_entity_id := currval('entity_id_seq'); END IF; UPDATE company SET legal_name = in_legal_name, tax_id = in_tax_id, sic_code = in_sic_code, sales_tax_id = in_sales_tax_id, license_number = in_license_number WHERE id = t_company_id; IF NOT FOUND THEN INSERT INTO company(entity_id, legal_name, tax_id, sic_code, sales_tax_id, license_number) VALUES (t_entity_id, in_legal_name, in_tax_id, in_sic_code, in_sales_tax_id, in_license_number); END IF; SELECT * INTO t_retval FROM company WHERE entity_id = t_entity_id; RETURN t_retval; END;
Returns billing information (billing name and address) for a given credit account.
DECLARE out_var company_billing_info; t_id INT; BEGIN select coalesce(eca.pay_to_name, c.legal_name), eca.meta_number, e.control_code, c.tax_id, a.line_one, a.line_two, a.line_three, a.city, a.state, a.mail_code, cc.name into out_var FROM (select legal_name, tax_id, entity_id FROM company UNION ALL SELECT last_name || ', ' || first_name, null, entity_id FROM person) c JOIN entity e ON (c.entity_id = e.id) JOIN entity_credit_account eca ON (eca.entity_id = e.id) LEFT JOIN eca_to_location cl ON (eca.id = cl.credit_id) LEFT JOIN location a ON (a.id = cl.location_id) LEFT JOIN country cc ON (cc.id = a.country_id) WHERE eca.id = in_id AND (location_class = 1 or location_class is null); RETURN out_var; END;
Returns an n dimensional array. Example: SELECT as_array(ARRAY[id::text, class]) from contact_class
aggregate_dummy
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
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;
DECLARE out_row contact_search_result; loop_count int; t_contact_info text[]; BEGIN t_contact_info = in_contact_info; FOR out_row IN SELECT e.id, e.control_code, ec.id, ec.meta_number, ec.description, ec.entity_class, c.legal_name, c.sic_code, b.description , ec.curr::text FROM (select * from entity where control_code like in_control_code || '%' union select * from entity where in_control_code is null) e JOIN (SELECT legal_name, sic_code, entity_id FROM company WHERE legal_name @@ plainto_tsquery(in_name_part) UNION ALL SELECT legal_name, sic_code, entity_id FROM company WHERE in_name_part IS NULL UNION ALL SELECT coalesce(first_name, '') || ' ' || coalesce(middle_name, '') || ' ' || coalesce(last_name, ''), null, entity_id FROM person WHERE coalesce(first_name, '') || ' ' || coalesce(middle_name, '') || ' ' || coalesce(last_name, '') @@ plainto_tsquery(in_name_part) UNION ALL SELECT coalesce(first_name, '') || ' ' || coalesce(middle_name, '') || ' ' || coalesce(last_name, ''), null, entity_id FROM person WHERE in_name_part IS NULL) c ON (e.id = c.entity_id) LEFT JOIN entity_credit_account ec ON (ec.entity_id = e.id) LEFT JOIN business b ON (ec.business_id = b.id) WHERE coalesce(ec.entity_class,e.entity_class) = in_entity_class AND (c.entity_id IN (select entity_id FROM entity_to_contact WHERE contact ILIKE ANY(t_contact_info)) OR '' ILIKE ALL(t_contact_info) OR t_contact_info IS NULL) AND ((in_address IS NULL AND in_city IS NULL AND in_state IS NULL AND in_country IS NULL) OR (c.entity_id IN (select entity_id FROM entity_to_location WHERE location_id IN (SELECT id FROM location WHERE (line_one @@ plainto_tsquery( in_address) OR line_two @@ plainto_tsquery( in_address) OR line_three @@ plainto_tsquery( in_address)) AND city ILIKE '%' || coalesce(in_city, '') || '%' AND state ILIKE '%' || coalesce(in_state, '') || '%' AND mail_code ILIKE '%' || coalesce(in_mail_code, '') || '%' AND country_id IN (SELECT id FROM country WHERE name ilike in_country OR short_name ilike in_country))))) AND (ec.business_id = coalesce(in_business_id, ec.business_id) OR (ec.business_id IS NULL AND in_business_id IS NULL)) AND (ec.startdate <= coalesce(in_active_date_to, ec.startdate) OR (ec.startdate IS NULL)) AND (ec.enddate >= coalesce(in_active_date_from, ec.enddate) OR (ec.enddate IS NULL)) AND (ec.meta_number like in_meta_number || '%' OR in_meta_number IS NULL) AND (in_notes IS NULL OR e.id in ( SELECT entity_id from entity_note WHERE note @@ plainto_tsquery(in_notes)) OR ec.id IN (select ref_key FROM eca_note WHERE note @@ plainto_tsquery(in_notes))) LOOP RETURN NEXT out_row; END LOOP; END;
Returns a list of contact classes ordered by ID.
DECLARE out_row RECORD; BEGIN FOR out_row IN SELECT * FROM contact_class ORDER BY id LOOP RETURN NEXT out_row; END LOOP; END;
Provides default rules for setting reconciliation labels. Currently saves a label of accno ||'--' || description.
DECLARE v_chart_id int; BEGIN -- Check for existence of the account already PERFORM * FROM cr_coa_to_account WHERE account = in_accno; IF NOT FOUND THEN -- This is a new account. Insert the relevant data. SELECT id INTO v_chart_id FROM chart WHERE accno = in_accno; INSERT INTO cr_coa_to_account (chart_id, account) VALUES (v_chart_id, in_accno||'--'||in_description); END IF; -- Already found, no need to do anything. =) END;
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;
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;
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;
SELECT (extract('YEAR' from now())|| '-12-01')::date;
SELECT ((extract('YEAR' from now())|| '-12-01')::date + '1 year'::interval)::date;
This function return each year inside transdate in transactions. Currently it uses a sparse index scan because the number of rows returned is very small and the table can be very large.
DECLARE next_record int; BEGIN SELECT MIN(EXTRACT ('YEAR' FROM transdate))::INT INTO next_record FROM acc_trans; LOOP EXIT WHEN next_record IS NULL; RETURN NEXT next_record; SELECT MIN(EXTRACT ('YEAR' FROM transdate))::INT AS YEAR INTO next_record FROM acc_trans WHERE EXTRACT ('YEAR' FROM transdate) > next_record; END LOOP; END;
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;
SELECT * FROM payroll_deduction WHERE entity_id = $1;
SELECT * FROM payroll_deduction_type where country_id = $1
BEGIN UPDATE payroll_deduction SET rate = in_rate WHERE entity_id = in_entity_id and in_type_id; IF NOT FOUND THEN INSERT INTO payroll_deduction (entity_id, type_id, rate) VALUES (in_entity_id, in_type_id, in_rate); END IF; RETURN QUERY SELECT * FROM payroll_deduction WHERE entity_id = in_entity_id and in_type_id; END;
This function return the default currency asigned by the program.
DECLARE defaultcurrency defaults.value%TYPE; BEGIN SELECT INTO defaultcurrency substr(value,1,3) FROM defaults WHERE setting_key = 'curr'; RETURN NEXT defaultcurrency; END;
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;
begin delete from yearend where trans_id = old.id; return NULL; end;
Searches for drafts. in_type may be any of 'ar', 'ap', or 'gl'.
DECLARE out_row RECORD; BEGIN FOR out_row IN SELECT trans.id, trans.transdate, trans.invoice, trans.reference, trans.description, sum(case when lower(in_type) = 'ap' AND chart.link = 'AP' THEN line.amount WHEN lower(in_type) = 'ar' AND chart.link = 'AR' THEN line.amount * -1 WHEN lower(in_type) = 'gl' AND line.amount > 0 THEN line.amount ELSE 0 END) as amount FROM ( SELECT id, transdate, reference, description, false as invoice, approved from gl WHERE lower(in_type) = 'gl' UNION SELECT id, transdate, invnumber as reference, (SELECT name FROM eca__get_entity(entity_credit_account)), invoice, approved from ap WHERE lower(in_type) = 'ap' UNION SELECT id, transdate, invnumber as reference, description, invoice, approved from ar WHERE lower(in_type) = 'ar' ) trans JOIN acc_trans line ON (trans.id = line.trans_id) JOIN chart ON (line.chart_id = chart.id and charttype = 'A') LEFT JOIN voucher v ON (v.trans_id = trans.id) WHERE (in_from_date IS NULL or trans.transdate >= in_from_date) AND (in_to_date IS NULL or trans.transdate <= in_to_date) AND trans.approved IS FALSE AND v.id IS NULL GROUP BY trans.id, trans.transdate, trans.description, trans.reference, trans.invoice HAVING (in_with_accno IS NULL or in_with_accno = ANY(as_array(chart.accno))) ORDER BY trans.reference LOOP RETURN NEXT out_row; END LOOP; END;
Deletes the draft from the book. Only will delete unapproved transactions. Otherwise an exception is raised and the transaction terminated.
declare t_table text; begin SELECT table_name into t_table FROM transactions where id = in_id; IF (t_table = 'ar') THEN PERFORM cogs__add_for_ar_line(id) FROM invoice WHERE trans_id = in_id; UPDATE ar set approved = true where id = in_id; ELSIF (t_table = 'ap') THEN PERFORM cogs__add_for_ap_line(id) FROM invoice WHERE trans_id = in_id; UPDATE ap set approved = true where id = in_id; ELSIF (t_table = 'gl') THEN UPDATE gl set approved = true where id = in_id; ELSE raise exception 'Invalid table % in draft_approve for transaction %', t_table, in_id; END IF; IF NOT FOUND THEN RETURN FALSE; END IF; UPDATE transactions SET approved_by = (select entity_id FROM users WHERE username = SESSION_USER), approved_at = now() WHERE id = in_id; RETURN TRUE; END;
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;
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;
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;
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;
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;
SELECT * FROM entity_credit_account WHERE entity_class = $2 AND meta_number = $1;
Returns a set of (only one) entity to which the entity credit account is attached.
declare v_row entity; BEGIN SELECT entity.* INTO v_row FROM entity_credit_account JOIN entity ON entity_credit_account.entity_id = entity.id WHERE entity_credit_account.id = in_credit_id; IF NOT FOUND THEN raise exception 'Could not find entity with ID %', in_credit_id; ELSE return next v_row; END IF; END;
This returns the pricematrix for the customer or vendor (entity_credit_account identified by in_id), orderd by partnumber, validfrom
SELECT pc.parts_id, p.partnumber, p.description, pc.credit_id, pc.pricebreak, pc.sellprice, NULL, NULL::int, NULL, pc.validfrom, pc.validto, pc.curr, pc.entry_id FROM partscustomer pc JOIN parts p on pc.parts_id = p.id JOIN entity_credit_account eca ON pc.credit_id = eca.id WHERE pc.credit_id = $1 AND eca.entity_class = 2 UNION SELECT pv.parts_id, p.partnumber, p.description, pv.credit_id, NULL, NULL, pv.lastcost, pv.leadtime::int, pv.partnumber, NULL, NULL, pv.curr, pv.entry_id FROM partsvendor pv JOIN parts p on pv.parts_id = p.id JOIN entity_credit_account eca ON pv.credit_id = eca.id WHERE pv.credit_id = $1 and eca.entity_class = 1 ORDER BY partnumber, validfrom
SELECT pc.parts_id, p.partnumber, p.description, pc.credit_id, pc.pricebreak, pc.sellprice, NULL::numeric, NULL::int, NULL::text, pc.validfrom, pc.validto, pc.curr, pc.entry_id FROM partscustomer pc JOIN parts p on pc.parts_id = p.id JOIN entity_credit_account eca ON pc.pricegroup_id = eca.pricegroup_id WHERE eca.id = $1 AND eca.entity_class = 2
Returns a set of taxable account id's.
select * from eca_tax where eca_id = $1;
This produces a history detail report, i.e. a list of all products purchased by a customer over a specific date range. meta_number is an exact match, as are in_open and inc_closed. All other fields allow for partial matches. NULL matches all values.
SELECT eca.id, e.name, eca.meta_number, a.id as invoice_id, a.invnumber, a.curr::text, p.id AS parts_id, p.partnumber, i.description, i.qty, i.unit::text, i.sellprice, i.discount, i.deliverydate, null::int as project_id, null::text as projectnumber, i.serialnumber, case when $16 = 1 then ex.buy else ex.sell end as exchange_rate, ee.id as salesperson_id, ep.last_name || ', ' || ep.first_name as salesperson_name FROM (select * from entity_credit_account where meta_number = $2 UNION select * from entity_credit_account WHERE $2 is null ) eca -- broken into unions for performance join entity e on eca.entity_id = e.id JOIN (select invnumber, curr, transdate, entity_credit_account, id, person_id, notes FROM ar where $16 = 2 and $13 = 'i' and (($17 and amount = paid) or ($18 and amount <> paid)) UNION select invnumber, curr, transdate, entity_credit_account, id, person_id, notes FROM ap where $16 = 1 and $13 = 'i' and (($17 and amount = paid) or ($18 and amount <> paid)) union select ordnumber, curr, transdate, entity_credit_account, id, person_id, notes from oe where ($16= 1 and oe.oe_class_id = 2 and $13 = 'o' and quotation is not true) and (($17 and not closed) or ($18 and closed)) union select ordnumber, curr, transdate, entity_credit_account, id, person_id, notes from oe where ($16= 2 and oe.oe_class_id = 1 and $13 = 'o' and quotation is not true) and (($17 and not closed) or ($18 and closed)) union select quonumber, curr, transdate, entity_credit_account, id, person_id, notes from oe where($16= 1 and oe.oe_class_id = 4 and $13 = 'q' and quotation is true) and (($17 and not closed) or ($18 and closed)) union select quonumber, curr, transdate, entity_credit_account, id, person_id, notes from oe where($16= 2 and oe.oe_class_id = 4 and $13 = 'q' and quotation is true) and (($17 and not closed) or ($18 and closed)) ) a ON (a.entity_credit_account = eca.id) -- broken into unions -- for performance JOIN ( select id, trans_id, parts_id, qty, description, unit, discount, deliverydate, serialnumber, sellprice FROM invoice where $13 = 'i' union select id, trans_id, parts_id, qty, description, unit, discount, reqdate, serialnumber, sellprice FROM orderitems where $13 <> 'i' ) i on i.trans_id = a.id JOIN parts p ON (p.id = i.parts_id) LEFT JOIN exchangerate ex ON (ex.transdate = a.transdate) LEFT JOIN entity ee ON (a.person_id = ee.id) LEFT JOIN person ep ON (ep.entity_id = ee.id) -- these filters don't perform as well on large databases WHERE (e.name ilike '%' || $1 || '%' or $1 is null) and ($3 is null or eca.id in (select credit_id from eca_to_contact where contact ilike '%' || $3 || '%')) -- and (($4 is null and $5 is null and $6 is null and $7 is null) -- or eca.id in -- (select credit_id from eca_to_location -- where location_id in -- (select id from location -- where ($4 is null or line_one ilike '%' || $4 || '%' -- or line_two ilike '%' || $4 || '%') -- and ($5 is null or city -- ilike '%' || $5 || '%') -- and ($6 is null or state -- ilike '%' || $6 || '%') -- and ($7 is null or mail_code -- ilike '%' || $7 || '%') -- and ($10 is null or country_id = $10)) -- ) -- ) -- and (a.transdate >= $11 or $11 is null) -- and (a.transdate <= $12 or $12 is null) -- and (eca.startdate >= $14 or $14 is null) -- and (eca.startdate <= $15 or $15 is null) -- and (a.notes @@ plainto_tsquery($9) or $9 is null) ORDER BY eca.meta_number;
Creates a summary account (no quantities, just parts group by invoice). meta_number must match exactly or be NULL. inc_open and inc_closed are exact matches too. All other values specify ranges or may match partially.
SELECT id, name, meta_number, null::int, null::text, curr, parts_id, partnumber, description, sum(qty), unit, null::numeric, null::numeric, null::date, null::int, null::text, null::text, null::numeric, null::int, null::text FROM eca__history($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18) group by id, name, meta_number, curr, parts_id, partnumber, description, unit order by meta_number;
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;
Returns a list of locations attached to the credit account.
DECLARE out_row RECORD; BEGIN FOR out_row IN SELECT l.id, l.line_one, l.line_two, l.line_three, l.city, l.state, l.mail_code, c.id, c.name, lc.id, lc.class FROM location l JOIN eca_to_location ctl ON (ctl.location_id = l.id) JOIN location_class lc ON (ctl.location_class = lc.id) JOIN country c ON (c.id = l.country_id) WHERE ctl.credit_id = in_credit_id ORDER BY lc.id, l.id, c.name LOOP RETURN NEXT out_row; END LOOP; END;
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;
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;
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;
Saves the contact record at the entity credit account level. Returns 1.
DECLARE out_id int; BEGIN PERFORM * FROM eca_to_contact WHERE credit_id = in_credit_id AND contact_class_id = in_old_contact_class AND contact = in_old_contact; IF FOUND THEN UPDATE eca_to_contact SET contact = in_contact, description = in_description, contact_class_id = in_class_id WHERE credit_id = in_credit_id AND contact_class_id = in_old_contact_class AND contact = in_old_contact; ELSE INSERT INTO eca_to_contact(credit_id, contact_class_id, description, contact) VALUES (in_credit_id, in_class_id, in_description, in_contact); END IF; RETURN 1; END;
Saves an entity credit account-level note. Such a note is valid for only one credit account. Returns the id of the note.
DECLARE out_id int; BEGIN -- TODO, change this to create vector too INSERT INTO eca_note (ref_key, note_class, note, vector, subject) VALUES (in_credit_id, 3, in_note, '', in_subject); SELECT currval('note_id_seq') INTO out_id; RETURN out_id; END;
DECLARE retval eca__pricematrix; t_insert bool; BEGIN t_insert := false; PERFORM * FROM entity_credit_account WHERE id = in_credit_id AND entity_class = 1; IF FOUND THEN -- VENDOR UPDATE partsvendor SET lastcost = in_price, leadtime = in_lead_time, partnumber = in_partnumber, curr = in_curr WHERE credit_id = in_credit_id AND entry_id = in_entry_id; IF NOT FOUND THEN INSERT INTO partsvendor (parts_id, credit_id, lastcost, leadtime, partnumber, curr) VALUES (in_parts_id, in_credit_id, in_price, in_leadtime::int2, in_partnumber, in_curr); END IF; SELECT pv.parts_id, p.partnumber, p.description, pv.credit_id, NULL, NULL, pv.lastcost, pv.leadtime::int, pv.partnumber, NULL, NULL, pv.curr, pv.entry_id INTO retval FROM partsvendor pv JOIN parts p ON p.id = pv.parts_id WHERE parts_id = in_parts_id and credit_id = in_credit_id; RETURN retval; END IF; PERFORM * FROM entity_credit_account WHERE id = in_credit_id AND entity_class = 2; IF FOUND THEN -- CUSTOMER UPDATE partscustomer SET pricebreak = in_pricebreak, sellprice = in_price, validfrom = in_validfrom, validto = in_validto, curr = in_curr WHERE entry_id = in_entry_id and credit_id = in_credit_id; IF NOT FOUND THEN INSERT INTO partscustomer (parts_id, credit_id, sellprice, validfrom, validto, curr) VALUES (in_parts_id, in_credit_id, in_price, in_validfrom, in_validto, in_curr); t_insert := true; END IF; SELECT pc.parts_id, p.partnumber, p.description, pc.credit_id, pc.pricebreak, pc.sellprice, NULL, NULL, NULL, pc.validfrom, pc.validto, pc.curr, pc.entry_id INTO retval FROM partscustomer pc JOIN parts p on pc.parts_id = p.id WHERE entry_id = CASE WHEN t_insert THEN currval('partscustomer_entry_id_seq') ELSE in_entry_id END; RETURN retval; END IF; RAISE EXCEPTION 'No valid entity credit account found'; END;
Sets the tax values for the customer or vendor. The entity credit account must exist before calling this function, and must have a type of either 1 or 2.
DECLARE eca entity_credit_account; iter int; BEGIN IF in_tax_ids = '{}' THEN RETURN NULL; END IF; SELECT * FROM entity_credit_account into eca WHERE id = in_id; DELETE FROM eca_tax WHERE eca_id = in_id; FOR iter in array_lower(in_tax_ids, 1) .. array_upper(in_tax_ids, 1) LOOP INSERT INTO eca_tax (eca_id, chart_id) values (in_id, in_tax_ids[iter]); END LOOP; RETURN TRUE; end;
BEGIN IF TG_OP = 'INSERT' THEN INSERT INTO business_unit(class_id, description, credit_id) VALUES (7 - NEW.entity_class, NEW.meta_number, NEW.id); ELSIF TG_OP = 'UPDATE' THEN IF new.meta_number <> old.meta_number THEN UPDATE business_unit SET description = new.meta_number WHERE class_id = 7 - NEW.entity_class AND credit_id = new.id; END IF; ELSIF TG_OP = 'DELETE'THEN DELETE FROM business_unit WHERE class_id = 7 - NEW.entity_class AND credit_id = old_id; RETURN OLD; END IF; RETURN NEW; END;
SELECT p.entity_id, e.control_code, p.id, s.salutation, s.id, p.first_name, p.middle_name, p.last_name, ee.startdate, ee.enddate, ee.role, ee.ssn, ee.sales, ee.manager_id, mp.first_name, mp.last_name, ee.employeenumber, ee.dob, e.country_id FROM person p JOIN entity_employee ee on (ee.entity_id = p.entity_id) JOIN entity e ON (p.entity_id = e.id) LEFT JOIN salutation s on (p.salutation_id = s.id) LEFT JOIN person mp ON ee.manager_id = p.entity_id WHERE ee.role = 'manager' ORDER BY ee.employeenumber;
SELECT p.entity_id, e.control_code, p.id, s.salutation, s.id, p.first_name, p.middle_name, p.last_name, ee.startdate, ee.enddate, ee.role, ee.ssn, ee.sales, ee.manager_id, mp.first_name, mp.last_name, ee.employeenumber, ee.dob, e.country_id FROM person p JOIN entity_employee ee on (ee.entity_id = p.entity_id) JOIN entity e ON (p.entity_id = e.id) LEFT JOIN salutation s on (p.salutation_id = s.id) LEFT JOIN person mp ON ee.manager_id = p.entity_id WHERE ee.sales ORDER BY ee.employeenumber;
Returns an employee_result tuple with information specified by the entity_id.
SELECT p.entity_id, e.control_code, p.id, s.salutation, s.id, p.first_name, p.middle_name, p.last_name, ee.startdate, ee.enddate, ee.role, ee.ssn, ee.sales, ee.manager_id, mp.first_name, mp.last_name, ee.employeenumber, ee.dob, e.country_id FROM person p JOIN entity_employee ee on (ee.entity_id = p.entity_id) JOIN entity e ON (p.entity_id = e.id) LEFT JOIN salutation s on (p.salutation_id = s.id) LEFT JOIN person mp ON ee.manager_id = p.entity_id WHERE p.entity_id = $1;
Returns username, user_id, etc. information if the employee is a user.
SELECT * FROM users WHERE entity_id = $1;
Returns a list of managers, that is employees with the 'manager' role set.
DECLARE emp employees%ROWTYPE; BEGIN FOR emp IN SELECT e.salutation, e.first_name, e.last_name, ee.* FROM entity_employee ee JOIN entity e on e.id = ee.entity_id WHERE ee.sales = 't'::bool AND ee.role='manager' AND ee.entity_id <> coalesce(in_id, -1) ORDER BY name LOOP RETURN NEXT emp; END LOOP; END;
Saves an employeerecord with the specified information.
DECLARE out_id INT; BEGIN UPDATE entity_employee SET startdate = coalesce(in_start_date, now()::date), enddate = in_end_date, dob = in_dob, role = in_role, ssn = in_ssn, manager_id = in_manager_id, employeenumber = in_employeenumber WHERE entity_id = in_entity_id; out_id = in_entity_id; IF NOT FOUND THEN INSERT INTO entity_employee (startdate, enddate, dob, role, ssn, manager_id, employeenumber, entity_id) VALUES (coalesce(in_start_date, now()::date), in_end_date, in_dob, in_role, in_ssn, in_manager_id, in_employeenumber, in_entity_id); RETURN in_entity_id; END IF; RETURN out_id; END;
Returns a list of employee_result records matching the search criteria. employeenumber is an exact match. stardate_from and startdate_to specify the start dates for employee searches All others are partial matches. NULLs match all values.
SELECT p.entity_id, e.control_code, p.id, s.salutation, s.id, p.first_name, p.middle_name, p.last_name, ee.startdate, ee.enddate, ee.role, ee.ssn, ee.sales, ee.manager_id, mp.first_name, mp.last_name, ee.employeenumber, ee.dob, e.country_id FROM person p JOIN entity e ON p.entity_id = e.id JOIN entity_employee ee on (ee.entity_id = p.entity_id) LEFT JOIN salutation s on (p.salutation_id = s.id) LEFT JOIN person mp ON ee.manager_id = p.entity_id WHERE ($7 is null or p.entity_id in (select ref_key from entity_note WHERE note ilike '%' || $7 || '%')) and ($1 is null or $1 = ee.employeenumber) and ($2 is null or $2 <= ee.startdate) and ($3 is null or $3 >= ee.startdate) and ($4 is null or p.first_name ilike '%' || $4 || '%') and ($5 is null or p.middle_name ilike '%' || $5 || '%') and ($6 is null or p.last_name ilike '%' || $6 || '%');
DECLARE emp employee_search%ROWTYPE; BEGIN FOR emp IN SELECT * FROM employee_search WHERE coalesce(startdate, 'infinity'::timestamp) >= coalesce(in_startdateto, '-infinity'::timestamp) AND coalesce(startdate, '-infinity'::timestamp) <= coalesce(in_startdatefrom, 'infinity'::timestamp) AND coalesce(enddate, '-infinity'::timestamp) <= coalesce(in_enddateto, 'infinity'::timestamp) AND coalesce(enddate, 'infinity'::timestamp) >= coalesce(in_enddatefrom, '-infinity'::timestamp) AND (name % in_name OR note % in_notes) AND (sales = 't' OR coalesce(in_sales, 'f') = 'f') LOOP RETURN NEXT emp; END LOOP; return; END;
INSERT INTO entity_to_location (entity_id,location_id) SELECT entity_id, $2 FROM person WHERE id = $1;
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;
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;
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;
Returns a set of (only one) entity record with the entity id.
declare v_row entity; BEGIN -- Removing the exception when not found handling. Applications are -- perfectly capable of handling whether an entity was not found. No need -- for a database-level exception here. Moreover such results may be useful -- --CT SELECT * INTO v_row FROM entity WHERE id = in_entity_id; return next v_row; END;
Lists all bank accounts for the entity.
DECLARE out_row entity_bank_account%ROWTYPE; BEGIN FOR out_row IN SELECT * from entity_bank_account where entity_id = in_entity_id LOOP RETURN NEXT out_row; END LOOP; END;
Returns a list of entity classes, ordered by assigned ids
DECLARE out_row entity_class; BEGIN FOR out_row IN SELECT * FROM entity_class LEFT JOIN defaults ON setting_key = 'roll_prefix' WHERE active and pg_has_role(SESSION_USER, coalesce(defaults.value, 'lsmb_' || current_database() || '__') || 'contact_class_' || lower(regexp_replace(class, ' ', '_')), 'USAGE') ORDER BY id LOOP RETURN NEXT out_row; END LOOP; END;
Lists all contact info for the entity.
DECLARE out_row contact_list; BEGIN FOR out_row IN SELECT cl.class, cl.id, c.description, c.contact FROM entity_to_contact c JOIN contact_class cl ON (c.contact_class_id = cl.id) WHERE c.entity_id = in_entity_id LOOP return next out_row; END LOOP; END;
Returns a list of entity credit account entries for the entity and of the entity class.
DECLARE out_row entity_credit_retrieve; BEGIN FOR out_row IN SELECT ec.id, e.id, ec.entity_class, ec.discount, ec.discount_terms, ec.taxincluded, ec.creditlimit, ec.terms, ec.meta_number, ec.description, ec.business_id, ec.language_code, ec.pricegroup_id, ec.curr, ec.startdate, ec.enddate, ec.ar_ap_account_id, ec.cash_account_id, ec.discount_account_id, ec.threshold, e.control_code, ec.id, ec.pay_to_name, ec.taxform_id FROM entity e JOIN entity_credit_account ec ON (e.id = ec.entity_id) WHERE e.id = in_entity_id AND ec.entity_class = CASE WHEN in_entity_class = 3 THEN 2 WHEN in_entity_class IS NULL THEN ec.entity_class ELSE in_entity_class END LOOP RETURN NEXT out_row; END LOOP; END;
Lists all locations for an entity.
DECLARE out_row RECORD; BEGIN FOR out_row IN SELECT l.id, l.line_one, l.line_two, l.line_three, l.city, l.state, l.mail_code, c.id, c.name, lc.id, lc.class FROM location l JOIN entity_to_location ctl ON (ctl.location_id = l.id) JOIN location_class lc ON (ctl.location_class = lc.id) JOIN country c ON (c.id = l.country_id) WHERE ctl.entity_id = in_entity_id ORDER BY lc.id, l.id, c.name LOOP RETURN NEXT out_row; END LOOP; END;
Returns a set of notes (including content) attached to the entity.
DECLARE out_row record; BEGIN FOR out_row IN SELECT * FROM entity_note WHERE ref_key = in_entity_id ORDER BY created LOOP RETURN NEXT out_row; END LOOP; END;
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;
Saves bank account to the credit account.
DECLARE out_id int; BEGIN UPDATE entity_bank_account SET bic = in_bic, iban = in_iban WHERE id = in_bank_account_id; IF FOUND THEN out_id = in_bank_account_id; ELSE INSERT INTO entity_bank_account(entity_id, bic, iban) VALUES(in_entity_id, in_bic, in_iban); SELECT CURRVAL('entity_bank_account_id_seq') INTO out_id ; END IF; IF in_credit_id IS NOT NULL THEN UPDATE entity_credit_account SET bank_account = out_id WHERE id = in_credit_id; END IF; RETURN out_id; END;
Saves company contact information. The return value is meaningless.
DECLARE out_id int; BEGIN DELETE FROM entity_to_contact WHERE entity_id = in_entity_id AND contact = in_old_contact AND contact_class_id = in_old_class_id; INSERT INTO entity_to_contact (entity_id, contact_class_id, description, contact) VALUES (in_entity_id, in_class_id, in_description, in_contact); RETURN 1; END;
Saves an entity-level note. Such a note is valid for all credit accounts attached to that entity. Returns the id of the note.
DECLARE out_id int; BEGIN -- TODO, change this to create vector too INSERT INTO entity_note (ref_key, note_class, entity_id, note, vector, subject) VALUES (in_entity_id, 1, in_entity_id, in_note, '', in_subject); SELECT currval('note_id_seq') INTO out_id; RETURN out_id; END;
Returns the entity credit account info.
SELECT * FROM entity_credit_account WHERE id = $1;
Returns an entity credit id, based on entity_id, entity_class, and meta_number. This is the preferred way to locate an account if all three of these are known
DECLARE out_var int; BEGIN SELECT id INTO out_var FROM entity_credit_account WHERE entity_id = in_entity_id AND in_entity_class = entity_class AND in_meta_number = meta_number; RETURN out_var; END;
Returns the credit id from the meta_number and entity_class.
DECLARE out_credit_id int; BEGIN SELECT id INTO out_credit_id FROM entity_credit_account WHERE meta_number = in_meta_number AND entity_class = in_account_class; RETURN out_credit_id; END;
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;
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;
Creates checkpoints for each account at a specific date. Books are considered closed when they occur before the latest checkpoint timewise. This means that balances (and credit/debit amounts) can be calculated starting at a checkpoint and moving forward (thus providing a mechanism for expunging old data while keeping balances correct at some future point).
DECLARE ret_val int; approval_check int; cp_date date; BEGIN IF in_end_date > now()::date THEN RAISE EXCEPTION 'Invalid date: Must be earlier than present'; END IF; SELECT count(*) into approval_check FROM acc_trans ac JOIN ( select id, approved, transdate FROM ar UNION SELECT id, approved, transdate FROM gl UNION SELECT id, approved, transdate FROM ap ) gl ON (gl.id = ac.trans_id) WHERE (ac.approved IS NOT TRUE AND ac.transdate <= in_end_date) OR (gl.approved IS NOT TRUE AND gl.transdate <= in_end_date); if approval_check > 0 THEN RAISE EXCEPTION 'Unapproved transactions in closed period'; END IF; SELECT max(end_date) INTO cp_date FROM account_checkpoint WHERE end_date < in_end_date; INSERT INTO account_checkpoint (end_date, account_id, amount, debits, credits) SELECT in_end_date, COALESCE(a.chart_id, cp.account_id), COALESCE(SUM (a.amount),0) + coalesce(MAX (cp.amount), 0), COALESCE(SUM (CASE WHEN (a.amount < 0) THEN a.amount ELSE 0 END), 0) + COALESCE( MIN (cp.debits), 0), COALESCE(SUM (CASE WHEN (a.amount > 0) THEN a.amount ELSE 0 END), 0) + COALESCE( MAX (cp.credits), 0) FROM (SELECT * FROM acc_trans WHERE transdate <= in_end_date AND transdate > COALESCE(cp_date, '1200-01-01')) a FULL OUTER JOIN ( select account_id, end_date, amount, debits, credits from account_checkpoint WHERE end_date = cp_date ) cp on (a.chart_id = cp.account_id) group by COALESCE(a.chart_id, cp.account_id); SELECT count(*) INTO ret_val FROM account_checkpoint where end_date = in_end_date; return ret_val; END;
Lists equity accounts for the retained earnings dropdown.
SELECT * FROM account WHERE category = 'Q' ORDER BY accno;
Removes checkpoints and reverses yearend transactions on in_end_date
BEGIN PERFORM count(*) FROM account_checkpoint WHERE end_date = in_end_date; IF NOT FOUND THEN RETURN FALSE; END IF; DELETE FROM account_checkpoint WHERE end_date = in_end_date; PERFORM count(*) FROM yearend WHERE transdate = in_end_date and reversed is not true; IF FOUND THEN INSERT INTO gl (reference, description, approved) SELECT 'Reversing ' || reference, 'Reversing ' || description, true FROM gl WHERE id = (select trans_id from yearend where transdate = in_end_date and reversed is not true); INSERT INTO acc_trans (chart_id, amount, transdate, trans_id, approved) SELECT chart_id, amount * -1, currval('id'), true FROM acc_trans where trans_id = (select trans_id from yearend where transdate = in_end_date and reversed is not true); UPDATE yearend SET reversed = true where transdate = in_end_date and reversed is not true; END IF; DELETE FROM account_checkpoint WHERE end_date = in_end_date; RETURN TRUE; END;
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;
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;
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;
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;
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;
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;
Retrieves the file information specified including content.
SELECT * FROM file_base where id = $1 and file_class = $2;
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)
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);
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;
This function retrieves a list of file attachments on a specified object.
select * from file_links where ref_key = $1 and dest_class = $2;
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;
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;
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;
This opens a form, and returns the id of the form opened.
DECLARE usertest bool; BEGIN SELECT count(*) = 1 INTO usertest FROM session WHERE session_id = in_session_id AND users_id IN (select id from users WHERE username = SESSION_USER); IF usertest is not true THEN RAISE EXCEPTION 'Invalid session'; END IF; INSERT INTO open_forms (session_id) VALUES (in_session_id); RETURN currval('open_forms_id_seq'); END;
SELECT coalesce((select description FROM language WHERE code = (SELECT substring(value, 1, 2) FROM defaults WHERE setting_key = 'default_language')), 'english');
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;
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;
Gets a set of all valid account_link descriptions.
SELECT * FROM account_link_description;
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;
SELECT CASE WHEN count(*) > 0 THEN true ELSE false END FROM unnest($2) r WHERE t @> array[$1];
SELECT bool_and(in_tree(e, $2)) FROM unnest($1) e;
SELECT l.parts_id, p.partnumber, p.description, l.counted, l.expected, l.counted - l.expected FROM inventory_report_line l JOIN parts p ON l.parts_id = p.id WHERE l.report_id = $1;
SELECT r.id, r.transdate, r.source, r.ar_trans_id, r.ap_trans_id, ar.invnumber, ap.invnumber FROM inventory_report r JOIN inventory_report_line l ON l.report_id = r.id LEFT JOIN ar ON ar.id = r.ar_trans_id LEFT JOIN ap ON ap.id = r.ap_trans_id WHERE r.id = $1;
SELECT r.id, r.transdate, r.source, r.ar_trans_id, r.ap_trans_id, ar.invnumber, ap.invnumber FROM inventory_report r JOIN inventory_report_line l ON l.report_id = r.id JOIN parts p ON l.parts_id = p.id LEFT JOIN ar ON ar.id = r.ar_trans_id LEFT JOIN ap ON ap.id = r.ap_trans_id WHERE ($1 is null or $1 <= r.transdate) AND ($2 is null OR $2 >= r.transdate) AND ($3 IS NULL OR plainto_tsquery($3) @@ tsvector(p.partnumber)) AND ($4 IS NULL OR source LIKE $4 || '%');
DECLARE retval ap; BEGIN SELECT * INTO retval FROM ap WHERE entity_credit_id = (select id from entity_credit_account where entity_class = 1 AND meta_number = in_meta_number) AND invnumber = in_invoice_number; RETURN retval; END;
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;
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);
Returns true if the two dates are in the same year, false otherwise.
SELECT extract ('YEAR' from $1) = extract ('YEAR' from $2);
SELECT value FROM menu_attribute where node_id = 74 and attribute = 'rowcount';
BEGIN UPDATE menu_attribute set value = $1 where node_id = 74 and attribute='rowcount'; IF NOT FOUND THEN INSERT INTO menu_attribute (node_id, attribute, value) values (74, 'rowcount', $1); END IF; RETURN $1; END;
DECLARE retval journal_entry; BEGIN INSERT INTO journal_entry (source, description, entry_type, transaction_date, approved, is_template) VALUES (in_source, in_description, in_entry_type, in_transaction_date, coalesce(in_approved, false), coalesce(in_is_template, false)); SELECT * INTO retval FROM journal_entry WHERE id = currval('journal_id_seq'); RETURN retval; END;
DECLARE retval journal_line; BEGIN INSERT INTO journal_line(account_id, journal_id, amount, cleared, memo) VALUES (in_account_id, in_journal_id, in_amount, coalesce(in_cleared, false), in_memo); INSERT INTO business_unit_jl(entry_id, bu_class, bu_id) SELECT currval('journal_line_line_id_seq'), business_unit_class, bu FROM business_unit WHERE id = any(in_business_units); SELECT * INTO retval FROM journal_line where line_id = currval('journal_line_line_id_seq'); return retval; END;
SELECT * FROM journal_entry where id = $1;
SELECT * FROM eca_invoice where journal_id = $1;
select * from journal_line where journal_id = $1;
DECLARE retval eca_invoice; BEGIN INSERT INTO eca_invoice (order_id, journal_id, on_hold, reverse, credit_id, language_code) VALUES (in_order_id, in_journal_id, coalesce(in_on_hold, false), in_reverse, in_credit_id, in_language_code); SELECT * INTO retval FROM eca_invoice WHERE journal_id = in_journal_id; RETURN retval; END;
DECLARE retval journal_search_result; BEGIN FOR retval IN SELECT j.id, j.source, j.description, j.entry_type, j.transaction_date, j.approved, j.is_template, eca.meta_number, e.name, ec.class FROM journal_entry j LEFT JOIN eca_invoice i ON (i.journal_id = j.id) LEFT JOIN entity_credit_account eca ON (eca.id = credit_id) LEFT JOIN entity e ON (eca.entity_id = e.id) LEFT JOIN entity_class ec ON (eca.entity_class = ec.id) WHERE (in_source IS NULL OR in_source = j.source) AND (in_description IS NULL or in_description = j.description) AND (in_entry_type is null or in_entry_type = j.entry_type) and (in_transaction_date is null or in_transaction_date = j.transaction_date) and j.approved = coalesce(in_approved, true) and j.is_template = coalesce(in_is_template, false) and (in_department_id is null or j.department_id = in_department_id) and (in_meta_number is null or eca.meta_number = in_meta_number) and (in_entity_class is null or eca.entity_class = in_entity_class) LOOP RETURN NEXT retval; END LOOP; END;
SELECT sum(amount) = 0 FROM journal_line WHERE journal_id = $1;
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;
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);
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;
UPDATE location set active = false, inactive_date = now() WHERE id = $1; SELECT * FROM location WHERE id = 1;
Returns the location specified by in_id.
DECLARE out_location location%ROWTYPE; BEGIN SELECT * INTO out_location FROM location WHERE id = in_id; RETURN out_location; END;
DELETES the location specified by in_id. Does not return a value.
BEGIN DELETE FROM location WHERE id = in_id; END;
Returns all locations, ordered by country, state, and city.
DECLARE out_location location%ROWTYPE; BEGIN FOR out_location IN SELECT * FROM location ORDER BY country, state, city LOOP RETURN NEXT out_location; END LOOP; END;
Lists location classes, by default in order entered.
DECLARE out_row RECORD; BEGIN FOR out_row IN SELECT * FROM location_class ORDER BY id LOOP RETURN NEXT out_row; END LOOP; END;
Lists countries, by default in alphabetical order.
DECLARE out_row RECORD; BEGIN FOR out_row IN SELECT * FROM country ORDER BY name LOOP RETURN NEXT out_row; END LOOP; END;
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;
Returns matching locations. All matches may be partial.
DECLARE out_location location%ROWTYPE; BEGIN FOR out_location IN SELECT * FROM location WHERE address1 ilike '%' || in_address1 || '%' AND address2 ilike '%' || in_address2 || '%' AND in_city ilike '%' || in_city || '%' AND in_state ilike '%' || in_state || '%' AND in_zipcode ilike '%' || in_zipcode || '%' AND in_country ilike '%' || in_country || '%' LOOP RETURN NEXT out_location; END LOOP; END;
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;
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;
Retrieves a single module's info by id.
SELECT * FROM lsmb_module where id = $1;
Returns a list of all defined modules, ordered by id.
SELECT * FROM lsmb_module ORDER BY id
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;
This function returns the complete menu tree. It is used to generate nested menus for the web interface.
DECLARE item menu_item; arg menu_attribute%ROWTYPE; BEGIN FOR item IN WITH RECURSIVE tree (path, id, parent, level, positions) AS (select id::text as path, id, parent, 0 as level, position::text FROM menu_node where parent is null UNION select path || ',' || n.id::text, n.id, n.parent, t.level + 1, t.positions || ',' || n.position FROM menu_node n JOIN tree t ON t.id = n.parent) SELECT n.position, n.id, c.level, n.label, c.path, n.parent, to_args(array[ma.attribute, ma.value]) FROM tree c JOIN menu_node n USING(id) JOIN menu_attribute ma ON (n.id = ma.node_id) WHERE n.id IN (select node_id FROM menu_acl acl LEFT JOIN pg_roles pr on pr.rolname = acl.role_name WHERE CASE WHEN role_name ilike 'public' THEN true WHEN rolname IS NULL THEN FALSE ELSE pg_has_role(rolname, 'USAGE') END GROUP BY node_id HAVING bool_and(CASE WHEN acl_type ilike 'DENY' THEN FALSE WHEN acl_type ilike 'ALLOW' THEN TRUE END)) or exists (select cn.id, cc.path FROM tree cc JOIN menu_node cn USING(id) WHERE cn.id IN (select node_id FROM menu_acl acl LEFT JOIN pg_roles pr on pr.rolname = acl.role_name WHERE CASE WHEN rolname ilike 'public' THEN true WHEN rolname IS NULL THEN FALSE ELSE pg_has_role(rolname, 'USAGE') END GROUP BY node_id HAVING bool_and(CASE WHEN acl_type ilike 'DENY' THEN false WHEN acl_type ilike 'ALLOW' THEN TRUE END)) and cc.path::text like c.path::text || ',%') GROUP BY n.position, n.id, c.level, n.label, c.path, c.positions, n.parent ORDER BY string_to_array(c.positions, ',')::int[] LOOP RETURN NEXT item; END LOOP; END;
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;
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 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;
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;
DECLARE retval order_search_line; BEGIN FOR retval IN SELECT o.id, CASE WHEN oe_class_id IN (1, 2) THEN o.ordnumber WHEN oe_class_id IN (3, 4) THEN o.quonumber ELSE NULL END as ordnumber, o.transdate, o.reqdate, o.amount, c.legal_name AS name, o.netamount, o.entity_credit_account, o.closed, o.quonumber, o.shippingpoint, CASE WHEN ct.entity_class = 2 THEN ex.buy ELSE ex.sell END AS exchangerate, o.shipvia, pe.first_name || ' ' || pe.last_name AS employee, pm.first_name || ' ' || pm.last_name AS manager, o.curr, o.ponumber, ct.meta_number, c.entity_id FROM oe o JOIN entity_credit_account ct ON (o.entity_credit_account = ct.id) JOIN company c ON (c.entity_id = ct.entity_id) LEFT JOIN person pe ON (o.person_id = pe.id) LEFT JOIN entity_employee e ON (pe.entity_id = e.entity_id) LEFT JOIN person pm ON (e.manager_id = pm.id) LEFT JOIN entity_employee m ON (pm.entity_id = m.entity_id) LEFT JOIN exchangerate ex ON (ex.curr = o.curr AND ex.transdate = o.transdate) WHERE o.oe_class_id = in_oe_class_id AND (in_meta_number IS NULL or ct.meta_number ILIKE in_meta_number || '%') AND (in_legal_name IS NULL OR c.legal_name @@ plainto_tsquery(in_legal_name)) AND (in_ponumber IS NULL OR o.ponumber ILIKE in_ponumber || '%') AND (in_ordnumber IS NULL OR o.ordnumber ILIKE in_ordnumber || '%') AND (in_open is true or o.closed is false) AND (in_closed is true or o.closed is true) AND (in_shipvia IS NULL OR o.shipvia @@ plainto_tsquery(in_shipvia)) AND (in_description IS NULL AND in_shippable IS NULL OR EXISTS (SELECT 1 FROM orderitems oi JOIN parts p ON p.id = oi.parts_id WHERE trans_id = o.id AND (in_description IS NULL OR oi.description @@ plainto_tsquery(in_description)) AND (in_shippable IS NULL OR p.assembly OR p.inventory_accno_id IS NOT NULL)) ) AND (in_date_from IS NULL OR o.transdate >= in_date_from) AND (in_date_to IS NULL OR o.transdate <= in_date_to) LOOP RETURN NEXT retval; END LOOP; END;
Simple way to cast a Perl string to a date format of known type.
select $1;
SELECT * FROM parts WHERE id = $1;
SELECT * FROM PARTS WHERE partnumber = $1 and obsolete is not true;
SELECT * FROM parts WHERE ($1 IS NULL OR (partnumber like $1 || '%')) AND ($2 IS NULL OR (description @@ plainto_tsquery(get_default_lang()::regconfig, $2))) AND not obsolete ORDER BY partnumber;
Reverses a payment. All fields are mandatory except batch_id and voucher_id because they determine the identity of the payment to be reversed.
DECLARE pay_row record; t_voucher_id int; t_voucher_inserted bool; t_currs text[]; t_rev_fx numeric; t_fxgain_id int; t_fxloss_id int; t_paid_fx numeric; BEGIN SELECT * INTO t_rev_fx FROM currency_get_exchangerate( in_currency, in_date_reversed, in_account_class); SELECT * INTO t_paid_fx FROM currency_get_exchangerate( in_currency, in_date_paid, in_account_class); select value::int INTO t_fxgain_id FROM setting_get('fxgain_accno_id'); select value::int INTO t_fxloss_id FROM setting_get('fxloss_accno_id'); SELECT string_to_array(value, ':') into t_currs from defaults where setting_key = 'curr'; IF in_currency IS NULL OR in_currency = t_currs[1] THEN t_rev_fx := 1; t_paid_fx := 1; ELSIF t_rev_fx IS NULL THEN t_rev_fx := in_exchangerate; PERFORM payments_set_exchangerate(in_account_class, in_exchangerate, in_currency, in_date_reversed); ELSIF t_rev_fx <> in_exchangerate THEN RAISE EXCEPTION 'Exchange rate different than on file'; END IF; IF t_rev_fx IS NULL THEN RAISE EXCEPTION 'No exchangerate provided and not default currency'; END IF; IF in_batch_id IS NOT NULL THEN t_voucher_id := nextval('voucher_id_seq'); t_voucher_inserted := FALSE; END IF; FOR pay_row IN SELECT a.*, c.ar_ap_account_id, arap.curr, arap.fxrate FROM acc_trans a JOIN (select id, curr, entity_credit_account, CASE WHEN curr = t_currs[1] THEN 1 ELSE buy END as fxrate FROM ar LEFT JOIN exchangerate USING (transdate, curr) WHERE in_account_class = 2 UNION SELECT id, curr, entity_credit_account, CASE WHEN curr = t_currs[1] THEN 1 ELSE sell END as fxrate FROM ap LEFT JOIN exchangerate USING (transdate, curr) WHERE in_account_class = 1 ) arap ON (a.trans_id = arap.id) JOIN entity_credit_account c ON (arap.entity_credit_account = c.id) JOIN account ch ON (a.chart_id = ch.id) WHERE a.source IS NOT DISTINCT FROM in_source AND a.transdate = in_date_paid AND in_credit_id = arap.entity_credit_account AND in_cash_accno = ch.accno and in_voucher_id IS NOT DISTINCT FROM voucher_id LOOP IF pay_row.curr = t_currs[1] THEN pay_row.fxrate = 1; END IF; IF in_batch_id IS NOT NULL AND t_voucher_inserted IS NOT TRUE THEN INSERT INTO voucher (id, trans_id, batch_id, batch_class) VALUES (t_voucher_id, pay_row.trans_id, in_batch_id, CASE WHEN in_account_class = 1 THEN 4 WHEN in_account_class = 2 THEN 7 END); t_voucher_inserted := TRUE; END IF; INSERT INTO acc_trans (trans_id, chart_id, amount, transdate, source, memo, approved, voucher_id) VALUES (pay_row.trans_id, pay_row.chart_id, pay_row.amount / t_paid_fx * -1 * t_rev_fx, in_date_reversed, in_source, 'Reversing ' || COALESCE(in_source, ''), case when in_batch_id is not null then false else true end, t_voucher_id), (pay_row.trans_id, pay_row.ar_ap_account_id, pay_row.amount / t_paid_fx * pay_row.fxrate, in_date_reversed, in_source, 'Reversing ' || COALESCE(in_source, ''), case when in_batch_id is not null then false else true end, t_voucher_id), (pay_row.trans_id, case when pay_row.fxrate > t_rev_fx THEN t_fxloss_id ELSE t_fxgain_id END, pay_row.amount / t_paid_fx * (t_rev_fx - pay_row.fxrate), in_date_reversed, in_source, 'Reversing ' || COALESCE(in_source, ''), case when in_batch_id is not null then false else true end, t_voucher_id); END LOOP; RETURN 1; END;
This searches for payments. in_date_to and _date_from specify the acceptable date range. All other matches are exact except that null matches all values. Currently (and to support earlier data) we define a payment as a collection of acc_trans records against the same credit account and cash account, on the same day with the same source number, and optionally the same voucher id.
DECLARE out_row payment_record; BEGIN FOR out_row IN select sum(CASE WHEN c.entity_class = 1 then a.amount ELSE a.amount * -1 END), c.meta_number, c.id, co.legal_name, compound_array(ARRAY[ARRAY[ch.id::text, ch.accno, ch.description]]), a.source, b.control_code, b.description, a.voucher_id, a.transdate FROM entity_credit_account c JOIN ( select entity_credit_account, id, curr FROM ar WHERE in_entity_class = 2 UNION SELECT entity_credit_account, id, curr FROM ap WHERE in_entity_class = 1 ) arap ON (arap.entity_credit_account = c.id) JOIN acc_trans a ON (arap.id = a.trans_id) JOIN chart ch ON (ch.id = a.chart_id) JOIN company co ON (c.entity_id = co.entity_id) LEFT JOIN voucher v ON (v.id = a.voucher_id) LEFT JOIN batch b ON (b.id = v.batch_id) WHERE (ch.accno = in_cash_accno) AND (in_currency IS NULL OR in_currency = arap.curr) AND (c.id = in_credit_id OR in_credit_id IS NULL) AND (a.transdate >= in_date_from OR in_date_from IS NULL) AND (a.transdate <= in_date_to OR in_date_to IS NULL) AND (source = in_source OR in_source IS NULL) GROUP BY c.meta_number, c.id, co.legal_name, a.transdate, a.source, a.memo, b.id, b.control_code, b.description, voucher_id ORDER BY a.transdate, c.meta_number, a.source LOOP RETURN NEXT out_row; END LOOP; END;
This posts the payments for large batch workflows. Note that in_transactions is a two-dimensional numeric array. Of each sub-array, the first element is the (integer) transaction id, and the second is the amount for that transaction.
DECLARE out_count int; t_voucher_id int; t_trans_id int; t_amount numeric; t_ar_ap_id int; t_cash_id int; t_currs text[]; t_exchangerate numeric; t_cash_sign int; BEGIN SELECT * INTO t_exchangerate FROM currency_get_exchangerate( in_currency, in_payment_date, in_account_class); IF in_batch_id IS NULL THEN -- t_voucher_id := NULL; RAISE EXCEPTION 'Bulk Post Must be from Batch!'; ELSE INSERT INTO voucher (batch_id, batch_class, trans_id) values (in_batch_id, (SELECT batch_class_id FROM batch WHERE id = in_batch_id), in_transactions[1][1]); t_voucher_id := currval('voucher_id_seq'); END IF; SELECT string_to_array(value, ':') into t_currs from defaults where setting_key = 'curr'; IF (in_currency IS NULL OR in_currency = t_currs[1]) THEN t_exchangerate := 1; ELSIF t_exchangerate IS NULL THEN t_exchangerate := in_exchangerate; PERFORM payments_set_exchangerate(in_account_class, in_exchangerate, in_currency, in_payment_date); ELSIF t_exchangerate <> in_exchangerate THEN RAISE EXCEPTION 'Exchange rate different than on file'; END IF; IF t_exchangerate IS NULL THEN RAISE EXCEPTION 'No exchangerate provided and not default currency'; END IF; CREATE TEMPORARY TABLE bulk_payments_in (id int, amount numeric, fxrate numeric, gain_loss_accno int); select id into t_ar_ap_id from chart where accno = in_ar_ap_accno; select id into t_cash_id from chart where accno = in_cash_accno; FOR out_count IN array_lower(in_transactions, 1) .. array_upper(in_transactions, 1) LOOP -- Fill the bulk payments table INSERT INTO bulk_payments_in(id, amount) VALUES (in_transactions[out_count][1], in_transactions[out_count][2]); END LOOP; IF in_account_class = 1 THEN t_cash_sign := 1; ELSE t_cash_sign := -1; END IF; IF (in_currency IS NULL OR in_currency = t_currs[1]) THEN UPDATE bulk_payments_in SET fxrate = 1; ELSE UPDATE bulk_payments_in SET fxrate = (SELECT CASE WHEN in_account_class = 1 THEN sell ELSE buy END FROM exchangerate e JOIN (SELECT transdate, id, curr FROM ar UNION SELECT transdate, id, curr FROM ap) a ON (e.transdate = a.transdate AND e.curr = a.curr) WHERE a.id = bulk_payments_in.id); UPDATE bulk_payments_in SET gain_loss_accno = (SELECT value::int FROM defaults WHERE setting_key = 'fxgain_accno_id') WHERE ((t_exchangerate - bulk_payments_in.fxrate) * t_cash_sign) < 0; UPDATE bulk_payments_in SET gain_loss_accno = (SELECT value::int FROM defaults WHERE setting_key = 'fxloss_accno_id') WHERE ((t_exchangerate - bulk_payments_in.fxrate) * t_cash_sign) > 0; -- explicitly leave zero gain/loss accno_id entries at NULL -- so we have an easy check for which END IF; -- Insert cash side INSERT INTO acc_trans (trans_id, chart_id, amount, approved, voucher_id, transdate, source) SELECT id, t_cash_id, amount * t_cash_sign * t_exchangerate/fxrate, CASE WHEN t_voucher_id IS NULL THEN true ELSE false END, t_voucher_id, in_payment_date, in_source FROM bulk_payments_in where amount <> 0; -- early payment discounts INSERT INTO acc_trans (trans_id, chart_id, amount, approved, voucher_id, transdate, source) SELECT bpi.id, eca.discount_account_id, amount * t_cash_sign * t_exchangerate/fxrate / (1 - discount::numeric/100) * (discount::numeric/100), CASE WHEN t_voucher_id IS NULL THEN true ELSE false END, t_voucher_id, in_payment_date, in_source FROM bulk_payments_in bpi JOIN (select entity_credit_account, id, transdate FROM ar WHERE in_account_class = 2 UNION SELECT entity_credit_account, id, transdate FROM ap WHERE in_account_class = 1) gl ON gl.id = bpi.id JOIN entity_credit_account eca ON gl.entity_credit_account = eca.id WHERE bpi.amount <> 0 AND extract('days' from age(gl.transdate)) < eca.discount_terms; INSERT INTO acc_trans (trans_id, chart_id, amount, approved, voucher_id, transdate, source) SELECT bpi.id, t_ar_ap_id, amount * t_cash_sign * -1 * t_exchangerate/fxrate / (1 - discount::numeric/100) * (discount::numeric/100), CASE WHEN t_voucher_id IS NULL THEN true ELSE false END, t_voucher_id, in_payment_date, in_source FROM bulk_payments_in bpi JOIN (select entity_credit_account, id, transdate FROM ar WHERE in_account_class = 2 UNION SELECT entity_credit_account, id, transdate FROM ap WHERE in_account_class = 1) gl ON gl.id = bpi.id JOIN entity_credit_account eca ON gl.entity_credit_account = eca.id WHERE bpi.amount <> 0 AND extract('days' from age(gl.transdate)) < eca.discount_terms; -- Insert ar/ap side INSERT INTO acc_trans (trans_id, chart_id, amount, approved, voucher_id, transdate, source) SELECT id, t_ar_ap_id, amount * -1 * t_cash_sign, CASE WHEN t_voucher_id IS NULL THEN true ELSE false END, t_voucher_id, in_payment_date, in_source FROM bulk_payments_in where amount <> 0; -- Insert fx gain/loss effects, if applicable INSERT INTO acc_trans (trans_id, chart_id, amount, approved, voucher_id, transdate, source) SELECT id, gain_loss_accno, amount * t_cash_sign * (1 - t_exchangerate/fxrate), CASE WHEN t_voucher_id IS NULL THEN true ELSE false END, t_voucher_id, in_payment_date, in_source FROM bulk_payments_in WHERE amount <> 0 AND gain_loss_accno IS NOT NULL; DROP TABLE bulk_payments_in; perform unlock_all(); return out_count; END;
This function finds a payment based on the id and retrieves the record, it is usefull for printing payments :)
DECLARE out_payment payment_header_item; BEGIN FOR out_payment IN SELECT p.id as payment_id, p.reference as payment_reference, p.payment_date, c.legal_name as legal_name, am.amount as amount, em.first_name, em.last_name, p.currency, p.notes FROM payment p JOIN entity_employee ent_em ON (ent_em.entity_id = p.employee_id) JOIN person em ON (ent_em.entity_id = em.entity_id) JOIN entity_credit_account eca ON (eca.id = p.entity_credit_id) JOIN company c ON (c.entity_id = eca.entity_id) JOIN payment_links pl ON (p.id = pl.payment_id) LEFT JOIN ( SELECT sum(a.amount) as amount FROM acc_trans a JOIN account acc ON (a.chart_id = acc.id) JOIN account_link al ON (acc.id =al.account_id) JOIN payment_links pl ON (pl.entry_id=a.entry_id) WHERE al.description in ('AP_paid', 'AP_discount', 'AR_paid', 'AR_discount') and ((in_account_class = 1 AND al.description like 'AP%') or (in_account_class = 2 AND al.description like 'AR%')) ) am ON (true) WHERE p.id = in_payment_id LOOP RETURN NEXT out_payment; END LOOP; END;
This function finds a payment based on the id and retrieves all the line records, it is usefull for printing payments and build reports :)
DECLARE out_payment_line payment_line_item; BEGIN FOR out_payment_line IN SELECT pl.payment_id, ac.entry_id, pl.type as link_type, ac.trans_id, a.invnumber as invoice_number, ac.chart_id, ch.accno as chart_accno, ch.description as chart_description, ch.link as chart_link, ac.amount, ac.transdate as trans_date, ac.source, ac.cleared_on, ac.fx_transaction, ac.project_id, ac.memo, ac.invoice_id, ac.approved, ac.cleared_on, ac.reconciled_on FROM acc_trans ac JOIN payment_links pl ON (pl.entry_id = ac.entry_id ) JOIN chart ch ON (ch.id = ac.chart_id) LEFT JOIN (SELECT id,invnumber FROM ar WHERE in_account_class = 2 UNION SELECT id,invnumber FROM ap WHERE in_account_class = 1 ) a ON (ac.trans_id = a.id) WHERE pl.payment_id = in_payment_id LOOP RETURN NEXT out_payment_line; END LOOP; END;
This function takes a single argument (1 for vendor, 2 for customer as always) and returns all entities with accounts of the appropriate type.
DECLARE out_entity entity%ROWTYPE; BEGIN FOR out_entity IN SELECT ec.id, e.name, e.entity_class, e.created FROM entity e JOIN entity_credit_account ec ON (ec.entity_id = e.id) WHERE e.entity_class = in_account_class LOOP RETURN NEXT out_entity; END LOOP; END;
This function takes the following arguments (all prefaced with in_ in the db): account_class: 1 for vendor, 2 for customer business_type: integer of business.id. currency: char(3) of currency (for example 'USD') date_from, date_to: These dates are inclusive. batch_id: For payment batches, where fees are concerned. ar_ap_accno: The AR/AP account number. This then returns a set of contact information with a 2 dimensional array cnsisting of outstanding invoices. Note that the payment selection logic is that this returns all invoices which are either approved or in the batch_id specified. It also locks the invoices using the LedgerSMB discretionary locking framework, and if not possible, returns the username of the individual who has the lock.
DECLARE payment_item payment_contact_invoice; BEGIN FOR payment_item IN SELECT c.id AS contact_id, e.control_code as econtrol_code, c.description as eca_description, e.name AS contact_name, c.meta_number AS account_number, sum( case when u.username IS NULL or u.username = SESSION_USER THEN coalesce(p.due::numeric, 0) - CASE WHEN c.discount_terms > extract('days' FROM age(a.transdate)) THEN 0 ELSE (coalesce(p.due::numeric, 0)) * coalesce(c.discount::numeric, 0) / 100 END ELSE 0::numeric END) AS total_due, compound_array(ARRAY[[ a.id::text, a.invnumber, a.transdate::text, a.amount::text, (a.amount - p.due)::text, (CASE WHEN c.discount_terms < extract('days' FROM age(a.transdate)) THEN 0 ELSE (coalesce(p.due, 0) * coalesce(c.discount, 0) / 100) END)::text, (coalesce(p.due, 0) - (CASE WHEN c.discount_terms < extract('days' FROM age(a.transdate)) THEN 0 ELSE (coalesce(p.due, 0)) * coalesce(c.discount, 0) / 100 END))::text, case when u.username IS NOT NULL and u.username <> SESSION_USER THEN 0::text ELSE 1::text END, COALESCE(u.username, 0::text) ]]), sum(case when a.batch_id = in_batch_id then 1 else 0 END), bool_and(lock_record(a.id, (select max(session_id) FROM "session" where users_id = ( select id from users WHERE username = SESSION_USER)))) FROM entity e JOIN entity_credit_account c ON (e.id = c.entity_id) JOIN (SELECT ap.id, invnumber, transdate, amount, entity_id, curr, 1 as invoice_class, entity_credit_account, on_hold, v.batch_id, approved, paid FROM ap LEFT JOIN (select * from voucher where batch_class = 1) v ON (ap.id = v.trans_id) WHERE in_account_class = 1 AND (v.batch_class = 1 or v.batch_id IS NULL) UNION SELECT ar.id, invnumber, transdate, amount, entity_id, curr, 2 as invoice_class, entity_credit_account, on_hold, v.batch_id, approved, paid FROM ar LEFT JOIN (select * from voucher where batch_class = 2) v ON (ar.id = v.trans_id) WHERE in_account_class = 2 AND (v.batch_class = 2 or v.batch_id IS NULL) ORDER BY transdate ) a ON (a.entity_credit_account = c.id) JOIN transactions t ON (a.id = t.id) JOIN (SELECT acc_trans.trans_id, sum(CASE WHEN in_account_class = 1 THEN amount WHEN in_account_class = 2 THEN amount * -1 END) AS due FROM acc_trans JOIN account coa ON (coa.id = acc_trans.chart_id) JOIN account_link al ON (al.account_id = coa.id) LEFT JOIN voucher v ON (acc_trans.voucher_id = v.id) WHERE ((al.description = 'AP' AND in_account_class = 1) OR (al.description = 'AR' AND in_account_class = 2)) AND (approved IS TRUE or v.batch_class IN (3, 6)) GROUP BY acc_trans.trans_id) p ON (a.id = p.trans_id) LEFT JOIN "session" s ON (s."session_id" = t.locked_by) LEFT JOIN users u ON (u.id = s.users_id) WHERE (a.batch_id = in_batch_id OR (a.invoice_class = in_account_class AND a.approved AND due <> 0 AND NOT a.on_hold AND a.curr = in_currency AND EXISTS (select trans_id FROM acc_trans WHERE trans_id = a.id AND chart_id = (SELECT id from account WHERE accno = in_ar_ap_accno) ))) AND (in_meta_number IS NULL OR in_meta_number = c.meta_number) GROUP BY c.id, e.name, c.meta_number, c.threshold, e.control_code, c.description HAVING (sum(p.due) >= c.threshold OR sum(case when a.batch_id = in_batch_id then 1 else 0 END) > 0) ORDER BY c.meta_number ASC LOOP RETURN NEXT payment_item; END LOOP; END;
DECLARE out_overpayment payment_overpayments_available_amount; BEGIN FOR out_overpayment IN SELECT chart_id, accno, chart_description, abs(sum(available)) FROM overpayments WHERE payment_class = in_account_class AND entity_credit_id = in_entity_credit_id AND available <> 0 GROUP BY chart_id, accno, chart_description LOOP RETURN NEXT out_overpayment; END LOOP; END;
Returns payment information on the entity credit account as required to for discount calculations and payment processing.
SELECT ec.id, cp.legal_name || coalesce(':' || ec.description,'') as name, e.entity_class, ec.discount_account_id, ec.meta_number FROM entity_credit_account ec JOIN entity e ON (ec.entity_id = e.id) JOIN company cp ON (cp.entity_id = e.id) WHERE ec.id = $1;
Returns a minimal set of information about customer or vendor accounts as needed for discount calculations and the like.
DECLARE out_entity payment_vc_info; BEGIN FOR out_entity IN SELECT ec.id, cp.legal_name || coalesce(':' || ec.description,'') as name, e.entity_class, ec.discount_account_id, ec.meta_number FROM entity_credit_account ec JOIN entity e ON (ec.entity_id = e.id) JOIN company cp ON (cp.entity_id = e.id) WHERE ec.entity_class = in_account_class AND (cp.legal_name ilike coalesce('%'||in_vc_name||'%','%%') OR cp.tax_id = in_vc_idn) LOOP RETURN NEXT out_entity; END LOOP; END;
This function takes a single argument (1 for vendor, 2 for customer as always) and returns all entities with open accounts of the appropriate type.
DECLARE out_entity entity%ROWTYPE; BEGIN FOR out_entity IN SELECT ec.id, cp.legal_name as name, e.entity_class, e.created FROM entity e JOIN entity_credit_account ec ON (ec.entity_id = e.id) JOIN company cp ON (cp.entity_id = e.id) WHERE ec.entity_class = in_account_class AND CASE WHEN in_account_class = 1 THEN ec.id IN (SELECT entity_credit_account FROM acc_trans JOIN chart ON (acc_trans.chart_id = chart.id) JOIN ap ON (acc_trans.trans_id = ap.id) WHERE link = 'AP' GROUP BY chart_id, trans_id, entity_credit_account HAVING SUM(acc_trans.amount) <> 0) WHEN in_account_class = 2 THEN ec.id IN (SELECT entity_credit_account FROM acc_trans JOIN chart ON (acc_trans.chart_id = chart.id) JOIN ar ON (acc_trans.trans_id = ar.id) WHERE link = 'AR' GROUP BY chart_id, trans_id, entity_credit_account HAVING SUM(acc_trans.amount) <> 0) END LOOP RETURN NEXT out_entity; END LOOP; END;
This function is based on payment_get_open_invoices and returns only one invoice if the in_invnumber is set. if no in_invnumber is passed this function behaves the same as payment_get_open_invoices
DECLARE payment_inv payment_invoice; BEGIN FOR payment_inv IN SELECT * from payment_get_open_invoices(in_account_class, in_entity_credit_id, in_curr, in_datefrom, in_dateto, in_amountfrom, in_amountto, in_department_id) WHERE (invnumber like in_invnumber OR in_invnumber IS NULL) LOOP RETURN NEXT payment_inv; END LOOP; END;
This function is the base for get_open_invoice and returns all open invoices for the entity_credit_id it has a lot of options to enable filtering and use the same logic for entity_class_id and currency.
DECLARE payment_inv payment_invoice; BEGIN FOR payment_inv IN SELECT a.id AS invoice_id, a.invnumber AS invnumber,a.invoice AS invoice, a.transdate AS invoice_date, a.amount AS amount, a.amount/ (CASE WHEN a.curr = (SELECT * from defaults_get_defaultcurrency()) THEN 1 ELSE (CASE WHEN in_account_class = 2 THEN ex.buy ELSE ex.sell END) END) as amount_fx, (CASE WHEN c.discount_terms < extract('days' FROM age(a.transdate)) THEN 0 ELSE (coalesce(ac.due, a.amount)) * coalesce(c.discount, 0) / 100 END) AS discount, (CASE WHEN c.discount_terms < extract('days' FROM age(a.transdate)) THEN 0 ELSE (coalesce(ac.due, a.amount)) * coalesce(c.discount, 0) / 100 END)/ (CASE WHEN a.curr = (SELECT * from defaults_get_defaultcurrency()) THEN 1 ELSE (CASE WHEN in_account_class = 2 THEN ex.buy ELSE ex.sell END) END) as discount_fx, ac.due - (CASE WHEN c.discount_terms < extract('days' FROM age(a.transdate)) THEN 0 ELSE (coalesce(ac.due, a.amount)) * coalesce(c.discount, 0) / 100 END) AS due, (ac.due - (CASE WHEN c.discount_terms < extract('days' FROM age(a.transdate)) THEN 0 ELSE (coalesce(ac.due, a.amount)) * coalesce(c.discount, 0) / 100 END))/ (CASE WHEN a.curr = (SELECT * from defaults_get_defaultcurrency()) THEN 1 ELSE (CASE WHEN in_account_class = 2 THEN ex.buy ELSE ex.sell END) END) AS due_fx, (CASE WHEN a.curr = (SELECT * from defaults_get_defaultcurrency()) THEN 1 ELSE (CASE WHEN in_account_class = 2 THEN ex.buy ELSE ex.sell END) END) AS exchangerate --TODO HV prepare drop entity_id from ap,ar --FROM (SELECT id, invnumber, transdate, amount, entity_id, FROM (SELECT id, invnumber, invoice, transdate, amount, 1 as invoice_class, curr, entity_credit_account, department_id, approved FROM ap UNION --SELECT id, invnumber, transdate, amount, entity_id, SELECT id, invnumber, invoice, transdate, amount, 2 AS invoice_class, curr, entity_credit_account, department_id, approved FROM ar ) a JOIN (SELECT trans_id, chart_id, sum(CASE WHEN in_account_class = 1 THEN amount WHEN in_account_class = 2 THEN amount * -1 END) as due FROM acc_trans GROUP BY trans_id, chart_id) ac ON (ac.trans_id = a.id) JOIN chart ON (chart.id = ac.chart_id) LEFT JOIN exchangerate ex ON ( ex.transdate = a.transdate AND ex.curr = a.curr ) JOIN entity_credit_account c ON (c.id = a.entity_credit_account) -- OR (a.entity_credit_account IS NULL and a.entity_id = c.entity_id)) WHERE ((chart.link = 'AP' AND in_account_class = 1) OR (chart.link = 'AR' AND in_account_class = 2)) AND a.invoice_class = in_account_class AND c.entity_class = in_account_class AND c.id = in_entity_credit_id --### short term: ignore fractional cent differences AND a.curr = in_curr AND (a.transdate >= in_datefrom OR in_datefrom IS NULL) AND (a.transdate <= in_dateto OR in_dateto IS NULL) AND (a.amount >= in_amountfrom OR in_amountfrom IS NULL) AND (a.amount <= in_amountto OR in_amountto IS NULL) AND (a.department_id = in_department_id OR in_department_id IS NULL) AND due <> 0 AND a.approved = true GROUP BY a.invnumber, a.transdate, a.amount, amount_fx, discount, discount_fx, ac.due, a.id, c.discount_terms, ex.buy, ex.sell, a.curr, a.invoice LOOP RETURN NEXT payment_inv; END LOOP; END;
DECLARE out_entity payment_vc_info; BEGIN FOR out_entity IN SELECT DISTINCT entity_credit_id, legal_name, e.entity_class, discount, o.meta_number FROM overpayments o JOIN entity e ON (e.id=o.entity_id) WHERE available <> 0 AND in_account_class = payment_class LOOP RETURN NEXT out_entity; END LOOP; END;
Returns a list of available overpayments
DECLARE out_overpayment overpayments%ROWTYPE; BEGIN FOR out_overpayment IN SELECT DISTINCT * FROM overpayments WHERE payment_class = in_account_class AND entity_credit_id = in_entity_credit_id AND available <> 0 AND (in_chart_id IS NULL OR chart_id = in_chart_id ) ORDER BY payment_date LOOP RETURN NEXT out_overpayment; END LOOP; END;
This function returns vendor or customer info
DECLARE out_row payment_location_result; BEGIN FOR out_row IN SELECT l.id, l.line_one, l.line_two, l.line_three, l.city, l.state, l.mail_code, c.name, lc.class FROM location l JOIN entity_to_location ctl ON (ctl.location_id = l.id) JOIN entity cp ON (ctl.entity_id = cp.id) JOIN location_class lc ON (ctl.location_class = lc.id) JOIN country c ON (c.id = l.country_id) JOIN entity_credit_account ec ON (ec.entity_id = cp.entity_id) WHERE ec.id = in_entity_credit_id AND lc.id = in_location_class_id ORDER BY lc.id, l.id, c.name LOOP RETURN NEXT out_row; END LOOP; END;
Posts a payment. in_op_* arrays are cross-indexed with eachother. Other arrays are cross-indexed with eachother. This API will probably change in 1.4 as we start looking at using more custom complex types and arrays of those (requires Pg 8.4 or higher).
DECLARE var_payment_id int; DECLARE var_gl_id int; DECLARE var_entry record; DECLARE var_entry_id int[]; DECLARE out_count int; DECLARE coa_id record; DECLARE var_employee int; DECLARE var_account_id int; DECLARE default_currency char(3); DECLARE current_exchangerate numeric; DECLARE old_exchangerate numeric; DECLARE fx_gain_loss_amount numeric; BEGIN IF array_upper(in_amount, 1) <> array_upper(in_cash_account_id, 1) THEN RAISE EXCEPTION 'Wrong number of accounts'; END IF; SELECT * INTO default_currency FROM defaults_get_defaultcurrency(); SELECT * INTO current_exchangerate FROM currency_get_exchangerate(in_curr, in_datepaid, in_account_class); SELECT INTO var_employee p.id FROM users u JOIN person p ON (u.entity_id=p.entity_id) WHERE username = SESSION_USER LIMIT 1; -- -- WE HAVE TO INSERT THE PAYMENT, USING THE GL INFORMATION -- THE ID IS GENERATED BY payment_id_seq -- INSERT INTO payment (reference, payment_class, payment_date, employee_id, currency, notes, department_id, entity_credit_id) VALUES ((CASE WHEN in_account_class = 1 THEN setting_increment('rcptnumber') -- I FOUND THIS ON sql/modules/Settings.sql ELSE -- and it is very usefull setting_increment('paynumber') END), in_account_class, in_datepaid, var_employee, in_curr, in_notes, in_department_id, in_entity_credit_id); SELECT currval('payment_id_seq') INTO var_payment_id; -- WE'LL NEED THIS VALUE TO USE payment_link table -- WE'LL NEED THIS VALUE TO JOIN WITH PAYMENT -- NOW COMES THE HEAVY PART, STORING ALL THE POSSIBLE TRANSACTIONS... -- -- FIRST WE SHOULD INSERT THE CASH ACCOUNTS -- -- WE SHOULD HAVE THE DATA STORED AS (ACCNO, AMOUNT), SO IF (array_upper(in_cash_account_id, 1) > 0) THEN FOR out_count IN array_lower(in_cash_account_id, 1) .. array_upper(in_cash_account_id, 1) LOOP INSERT INTO acc_trans (chart_id, amount, trans_id, transdate, approved, source, memo) VALUES (in_cash_account_id[out_count], CASE WHEN in_account_class = 1 THEN in_amount[out_count]*current_exchangerate ELSE (in_amount[out_count]*current_exchangerate)* - 1 END, in_transaction_id[out_count], in_datepaid, coalesce(in_approved, true), in_source[out_count], in_memo[out_count]); INSERT INTO payment_links VALUES (var_payment_id, currval('acc_trans_entry_id_seq'), 1); IF (in_ovp_payment_id IS NOT NULL AND in_ovp_payment_id[out_count] IS NOT NULL) THEN INSERT INTO payment_links VALUES (in_ovp_payment_id[out_count], currval('acc_trans_entry_id_seq'), 0); END IF; END LOOP; -- NOW LETS HANDLE THE AR/AP ACCOUNTS -- WE RECEIVED THE TRANSACTIONS_ID AND WE CAN OBTAIN THE ACCOUNT FROM THERE FOR out_count IN array_lower(in_transaction_id, 1) .. array_upper(in_transaction_id, 1) LOOP SELECT INTO var_account_id chart_id FROM acc_trans as ac JOIN chart as c ON (c.id = ac.chart_id) WHERE trans_id = in_transaction_id[out_count] AND ( c.link = 'AP' OR c.link = 'AR' ); -- We need to know the exchangerate of this transaction -- ### BUG: we don't have a guarantee that the transaction is -- the same currency as in_curr, so, we can't use -- current_exchangerate as the basis for fx gain/loss -- calculations IF (in_curr = default_currency) THEN old_exchangerate := 1; ELSIF (in_account_class = 2) THEN SELECT buy INTO old_exchangerate FROM exchangerate e JOIN ar a ON (a.transdate = e.transdate) AND (a.curr = e.curr) WHERE a.id = in_transaction_id[out_count]; ELSE SELECT sell INTO old_exchangerate FROM exchangerate e JOIN ap a ON (a.transdate = e.transdate) AND (a.curr = e.curr) WHERE a.id = in_transaction_id[out_count]; END IF; -- Now we post the AP/AR transaction INSERT INTO acc_trans (chart_id, amount, trans_id, transdate, approved, source, memo) VALUES (var_account_id, CASE WHEN in_account_class = 1 THEN (in_amount[out_count]*old_exchangerate) * -1 ELSE in_amount[out_count]*old_exchangerate END, in_transaction_id[out_count], in_datepaid, coalesce(in_approved, true), in_source[out_count], in_memo[out_count]); -- Lets set the gain/loss, if fx_gain_loss_amount equals zero then we dont need to post -- any transaction fx_gain_loss_amount := in_amount[out_count]*current_exchangerate - in_amount[out_count]*old_exchangerate; IF (in_account_class = 1) THEN -- in case of vendor invoices, the invoice amounts have been negated, do the same with the diff fx_gain_loss_amount := fx_gain_loss_amount * -1; END IF; IF (fx_gain_loss_amount < 0) THEN INSERT INTO acc_trans (chart_id, amount, trans_id, transdate, approved, source) VALUES ((select value::int from defaults WHERE setting_key = 'fxgain_accno_id'), fx_gain_loss_amount, in_transaction_id[out_count], in_datepaid, coalesce(in_approved, true), in_source[out_count]); ELSIF (fx_gain_loss_amount > 0) THEN INSERT INTO acc_trans (chart_id, amount, trans_id, transdate, approved, source) VALUES ((select value::int from defaults WHERE setting_key = 'fxloss_accno_id'), fx_gain_loss_amount, in_transaction_id[out_count], in_datepaid, coalesce(in_approved, true), in_source[out_count]); END IF; -- Now we set the links INSERT INTO payment_links VALUES (var_payment_id, currval('acc_trans_entry_id_seq'), 1); END LOOP; END IF; -- END IF -- -- WE NEED TO HANDLE THE OVERPAYMENTS NOW -- -- -- FIRST WE HAVE TO MAKE THE GL TO HOLD THE OVERPAYMENT TRANSACTIONS -- THE ID IS GENERATED BY gl_id_seq -- IF (array_upper(in_op_cash_account_id, 1) > 0) THEN INSERT INTO gl (reference, description, transdate, person_id, notes, approved, department_id) VALUES (setting_increment('glnumber'), in_gl_description, in_datepaid, var_employee, in_notes, in_approved, in_department_id); SELECT currval('id') INTO var_gl_id; -- -- WE NEED TO SET THE GL_ID FIELD ON PAYMENT'S TABLE -- UPDATE payment SET gl_id = var_gl_id WHERE id = var_payment_id; -- NOW COMES THE HEAVY PART, STORING ALL THE POSSIBLE TRANSACTIONS... -- -- FIRST WE SHOULD INSERT THE OVERPAYMENT CASH ACCOUNTS -- FOR out_count IN array_lower(in_op_cash_account_id, 1) .. array_upper(in_op_cash_account_id, 1) LOOP INSERT INTO acc_trans (chart_id, amount, trans_id, transdate, approved, source, memo) VALUES (in_op_cash_account_id[out_count], CASE WHEN in_account_class = 1 THEN in_op_amount[out_count] ELSE in_op_amount[out_count] * - 1 END, var_gl_id, in_datepaid, coalesce(in_approved, true), in_op_source[out_count], in_op_memo[out_count]); INSERT INTO payment_links VALUES (var_payment_id, currval('acc_trans_entry_id_seq'), 2); END LOOP; -- NOW LETS HANDLE THE OVERPAYMENT ACCOUNTS FOR out_count IN array_lower(in_op_account_id, 1) .. array_upper(in_op_account_id, 1) LOOP INSERT INTO acc_trans (chart_id, amount, trans_id, transdate, approved, source, memo) VALUES (in_op_account_id[out_count], CASE WHEN in_account_class = 1 THEN in_op_amount[out_count] * -1 ELSE in_op_amount[out_count] END, var_gl_id, in_datepaid, coalesce(in_approved, true), in_op_source[out_count], in_op_memo[out_count]); INSERT INTO payment_links VALUES (var_payment_id, currval('acc_trans_entry_id_seq'), 2); END LOOP; END IF; return var_payment_id; END;
Returns all information on a payment type by the id. This should be renamed to account for its behavior in future versions.
DECLARE out_row payment_type%ROWTYPE; BEGIN FOR out_row IN SELECT * FROM payment_type where id=in_payment_type_id LOOP RETURN NEXT out_row; END LOOP; END;
DECLARE out_row payment_type%ROWTYPE; BEGIN FOR out_row IN SELECT * FROM payment_type LOOP RETURN NEXT out_row; END LOOP; END;
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;
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;
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);
SELECT * FROM payroll_income_category order by id;
SELECT * FROM payroll_income_class where country_id = $1 ORDER BY label;
SELECT * FROM payroll_income_type WHERE id = $1;
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;
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);
Returns dates for year to date, and last year.
SELECT * FROM periods ORDER BY id
Deletes a contact record specified for the person. Returns true if a record was found and deleted, false if not.
BEGIN DELETE FROM entity_to_contact WHERE person_id = (SELECT entity_id FROM person WHERE id = in_person_id) and contact_class_id = in_contact_class_id and contact= in_contact; RETURN FOUND; END;
Deletes a location mapping to a person. Returns true if found, false if no data deleted.
BEGIN DELETE FROM entity_to_location WHERE person_id = (select entity_id from person where id = in_person_id) AND location_id = in_location_id AND location_class = in_location_class; RETURN FOUND; END;
SELECT e.id, e.control_code, e.name, e.country_id, c.name, p.first_name, p.middle_name, p.last_name FROM entity e JOIN country c ON c.id = e.country_id JOIN person p ON p.entity_id = e.id WHERE e.id = $1;
SELECT e.id, e.control_code, e.name, e.country_id, c.name, p.first_name, p.middle_name, p.last_name FROM entity e JOIN country c ON c.id = e.country_id JOIN person p ON p.entity_id = e.id WHERE e.control_code = $1;
Returns the entity_id of the current, logged in user.
SELECT entity_id from users where username = SESSION_USER;
Lists bank accounts for a person
DECLARE out_row entity_bank_account%ROWTYPE; BEGIN FOR out_row IN SELECT * from entity_bank_account where entity_id = in_entity_id LOOP RETURN NEXT out_row; END LOOP; END;
Returns a list of contacts attached to the function.
DECLARE out_row RECORD; BEGIN FOR out_row IN SELECT cc.class, cc.id, c.description, c.contact FROM entity_to_contact c JOIN contact_class cc ON (c.contact_class_id = cc.id) JOIN person p ON (c.person_id = p.entity_id) WHERE p.entity_id = in_entity_id LOOP RETURN NEXT out_row; END LOOP; END;
Returns a list of languages ordered by code
SELECT * FROM language ORDER BY code ASC
Returns a list of locations specified attached to the person.
DECLARE out_row RECORD; BEGIN FOR out_row IN SELECT l.id, l.line_one, l.line_two, l.line_three, l.city, l.state, l.mail_code, c.id, c.name, lc.id, lc.class FROM location l JOIN entity_to_location ctl ON (ctl.location_id = l.id) JOIN person p ON (ctl.person_id = p.entity_id) JOIN location_class lc ON (ctl.location_class = lc.id) JOIN country c ON (c.id = l.country_id) WHERE p.entity_id = in_entity_id ORDER BY lc.id, l.id, c.name LOOP RETURN NEXT out_row; END LOOP; END;
Returns a list of notes attached to a person.
DECLARE out_row record; BEGIN FOR out_row IN SELECT * FROM entity_note WHERE ref_key = in_entity_id ORDER BY created LOOP RETURN NEXT out_row; END LOOP; END;
Returns a list of salutations ordered by id.
SELECT * FROM salutation ORDER BY id ASC
Saves the person with the information specified. Returns the entity_id of the record saved.
DECLARE e_id int; e entity; loc location; l_id int; p_id int; BEGIN select * into e from entity where id = in_entity_id and entity_class = 3; e_id := in_entity_id; IF FOUND THEN UPDATE entity SET name = in_first_name || ' ' || in_last_name, country_id = in_country_id WHERE id = in_entity_id; ELSE INSERT INTO entity (name, entity_class, country_id) values (in_first_name || ' ' || in_last_name, 3, in_country_id); e_id := currval('entity_id_seq'); END IF; UPDATE person SET salutation_id = in_salutation_id, first_name = in_first_name, last_name = in_last_name, middle_name = in_middle_name WHERE entity_id = in_entity_id; IF FOUND THEN RETURN in_entity_id; ELSE -- Do an insert INSERT INTO person (salutation_id, first_name, last_name, entity_id) VALUES (in_salutation_id, in_first_name, in_last_name, e_id); RETURN e_id; END IF; END;
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;
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;
WITH gl (id) AS ( SELECT id FROM ap WHERE approved is true AND entity_credit_account = $1 UNION ALL SELECT id FROM ar WHERE approved is true AND entity_credit_account = $1 ) SELECT a.id, a.accno, a.description, a.category, ah.id, ah.accno, ah.description, CASE WHEN a.category = 'E' THEN -1 ELSE 1 END * sum(ac.amount) FROM account a JOIN account_heading ah on a.heading = ah.id JOIN acc_trans ac ON a.id = ac.chart_id JOIN gl ON ac.trans_id = gl.id WHERE ac.approved is true AND ($2 IS NULL OR ac.transdate >= $2) AND ($3 IS NULL OR ac.transdate <= $3) AND a.category IN ('I', 'E') GROUP BY a.id, a.accno, a.description, a.category, ah.id, ah.accno, ah.description ORDER BY a.category DESC, a.accno ASC;
WITH RECURSIVE bu_tree (id, parent, path) AS ( SELECT id, null, row(array[id])::tree_record FROM business_unit WHERE id = any($4) UNION ALL SELECT bu.id, parent, row((path).t || bu.id)::tree_record FROM business_unit bu JOIN bu_tree ON bu.parent_id = bu_tree.id ) SELECT a.id, a.accno, a.description, a.category, ah.id, ah.accno, ah.description, CASE WHEN a.category = 'E' THEN -1 ELSE 1 END * sum(ac.amount) FROM account a JOIN account_heading ah on a.heading = ah.id JOIN acc_trans ac ON a.id = ac.chart_id AND ac.approved JOIN tx_report gl ON ac.trans_id = gl.id LEFT JOIN (select array_agg(path) as bu_ids, entry_id FROM business_unit_ac buac JOIN bu_tree ON bu_tree.id = buac.bu_id GROUP BY buac.entry_id) bu ON (ac.entry_id = bu.entry_id) WHERE ac.approved is true AND ($1 IS NULL OR ac.transdate >= $1) AND ($2 IS NULL OR ac.transdate <= $2) AND ($4 = '{}' OR $4 is null or in_tree($4, bu_ids)) AND a.category IN ('I', 'E') AND ($3 = 'none' OR ($3 = 'all' AND NOT EXISTS (SELECT * FROM yearend WHERE trans_id = gl.id )) OR ($3 = 'last' AND NOT EXISTS (SELECT 1 FROM yearend HAVING max(trans_id) = gl.id)) ) GROUP BY a.id, a.accno, a.description, a.category, ah.id, ah.accno, ah.description ORDER BY a.category DESC, a.accno ASC;
WITH RECURSIVE bu_tree (id, parent, path) AS ( SELECT id, null, row(array[id])::tree_record FROM business_unit WHERE id = any($4) UNION ALL SELECT bu.id, parent, row((path).t || bu.id)::tree_record FROM business_unit bu JOIN bu_tree ON bu.parent_id = bu_tree.id ) SELECT a.id, a.accno, a.description, a.category, ah.id, ah.accno, ah.description, CASE WHEN a.category = 'E' THEN -1 ELSE 1 END * sum(ac.amount * ca.portion) FROM account a JOIN account_heading ah on a.heading = ah.id JOIN acc_trans ac ON a.id = ac.chart_id AND ac.approved JOIN tx_report gl ON ac.trans_id = gl.id JOIN (SELECT id, sum(portion) as portion FROM cash_impact ca WHERE ($1 IS NULL OR ca.transdate >= $1) AND ($2 IS NULL OR ca.transdate <= $2) GROUP BY id ) ca ON gl.id = ca.id LEFT JOIN (select array_agg(path) as bu_ids, entry_id FROM business_unit_ac buac JOIN bu_tree ON bu_tree.id = buac.bu_id GROUP BY entry_id) bu ON (ac.entry_id = bu.entry_id) WHERE ac.approved is true AND ($4 = '{}' OR $4 is null or in_tree($4, bu_ids)) AND a.category IN ('I', 'E') AND ($3 = 'none' OR ($3 = 'all' AND NOT EXISTS (SELECT * FROM yearend WHERE trans_id = gl.id )) OR ($3 = 'last' AND NOT EXISTS (SELECT 1 FROM yearend HAVING max(trans_id) = gl.id)) ) GROUP BY a.id, a.accno, a.description, a.category, ah.id, ah.accno, ah.description ORDER BY a.category DESC, a.accno ASC;
SELECT a.id, a.accno, a.description, a.category, ah.id, ah.accno, ah.description, CASE WHEN a.category = 'E' THEN -1 ELSE 1 END * sum(ac.amount) FROM account a JOIN account_heading ah on a.heading = ah.id JOIN acc_trans ac ON a.id = ac.chart_id WHERE ac.approved AND ac.trans_id = $1 AND a.category IN ('I', 'E') GROUP BY a.id, a.accno, a.description, a.category, ah.id, ah.accno, ah.description ORDER BY a.category DESC, a.accno ASC;
WITH RECURSIVE bu_tree (id, parent, path) AS ( SELECT id, null, row(array[id])::tree_record FROM business_unit WHERE id = any($4) UNION ALL SELECT bu.id, parent, row((path).t || bu.id)::tree_record FROM business_unit bu JOIN bu_tree ON bu.parent_id = bu_tree.id ) SELECT a.id, a.accno, a.description, a.category, ah.id, ah.accno, ah.description, sum(ac.amount) * -1 FROM account a JOIN account_heading ah on a.heading = ah.id JOIN acc_trans ac ON ac.chart_id = a.id JOIN invoice i ON i.id = ac.invoice_id JOIN account_link l ON l.account_id = a.id JOIN ar ON ar.id = ac.trans_id LEFT JOIN (select as_array(bu.path) as bu_ids, entry_id from business_unit_inv bui JOIN bu_tree bu ON bui.bu_id = bu.id GROUP BY entry_id) bui ON bui.entry_id = i.id WHERE i.parts_id = $3 AND (ac.transdate >= $1 OR $1 IS NULL) AND (ac.transdate <= $2 OR $2 IS NULL) AND ar.approved AND l.description = 'IC_expense' AND ($4 is null or $4 = '{}' OR in_tree($4, bu_ids)) GROUP BY a.id, a.accno, a.description, a.category, ah.id, ah.accno, ah.description UNION SELECT a.id, a.accno, a.description, a.category, ah.id, ah.accno, ah.description, sum(i.sellprice * i.qty * (1 - coalesce(i.discount, 0))) FROM parts p JOIN invoice i ON i.id = p.id JOIN acc_trans ac ON ac.invoice_id = i.id JOIN account a ON p.income_accno_id = a.id JOIN ar ON ar.id = ac.trans_id JOIN account_heading ah on a.heading = ah.id LEFT JOIN (select as_array(bu.path) as bu_ids, entry_id from business_unit_inv bui JOIN bu_tree bu ON bui.bu_id = bu.id GROUP BY entry_id) bui ON bui.entry_id = i.id WHERE i.parts_id = $3 AND (ac.transdate >= $1 OR $1 IS NULL) AND (ac.transdate <= $2 OR $2 IS NULL) AND ar.approved AND ($4 is null or $4 = '{}' OR in_tree($4, bu_ids)) GROUP BY a.id, a.accno, a.description, a.category, ah.id, ah.accno, ah.description
Returns an alphabetically ordered pricegroup list.
SELECT * FROM pricegroup ORDER BY pricegroup;
SELECT * FROM pricegroup;
delete from partscustomer where entry_id = $1 and credit_id = $2; delete from partsvendor where entry_id = $1 and credit_id = $2; select true;
DECLARE retval eca__pricematrix; t_insert bool; t_entity_class int; BEGIN t_insert := false; SELECT entity_class INTO t_entity_class FROM entity_credit_account WHERE id = in_credit_id; IF t_entity_class = 1 THEN -- VENDOR UPDATE partsvendor SET lastcost = in_price, leadtime = in_lead_time, partnumber = in_partnumber, curr = in_curr WHERE credit_id = in_credit_id AND entry_id = in_entry_id; IF NOT FOUND THEN INSERT INTO partsvendor (parts_id, credit_id, lastcost, leadtime, partnumber, curr) VALUES (in_parts_id, in_credit_id, in_price, in_leadtime::int2, in_partnumber, in_curr); END IF; SELECT pv.parts_id, p.partnumber, p.description, pv.credit_id, NULL, NULL, pv.lastcost, pv.leadtime::int, pv.partnumber, NULL, NULL, pv.curr, pv.entry_id INTO retval FROM partsvendor pv JOIN parts p ON p.id = pv.parts_id WHERE parts_id = in_parts_id and credit_id = in_credit_id; RETURN retval; ELSIF t_entity_class = 2 THEN -- CUSTOMER UPDATE partscustomer SET pricebreak = in_pricebreak, sellprice = in_price, validfrom = in_validfrom, validto = in_validto, curr = in_curr WHERE entry_id = in_entry_id and credit_id = in_credit_id; IF NOT FOUND THEN INSERT INTO partscustomer (parts_id, credit_id, sellprice, validfrom, validto, curr) VALUES (in_parts_id, in_credit_id, in_price, in_validfrom, in_validto, in_curr); t_insert := true; END IF; SELECT pc.parts_id, p.partnumber, p.description, pc.credit_id, pc.pricebreak, pc.sellprice, NULL, NULL, NULL, pc.validfrom, pc.validto, pc.curr, pc.entry_id INTO retval FROM partscustomer pc JOIN parts p on pc.parts_id = p.id WHERE entry_id = CASE WHEN t_insert THEN currval('partscustomer_entry_id_seq') ELSE in_entry_id END; RETURN retval; ELSE RAISE EXCEPTION 'No valid entity credit account found'; END IF; END;
returns set of accounts set up for reconciliation. Currently we pull the account number and description from the account table.
SELECT coa.accno || ' ' || coa.description as name, coa.accno, coa.id as id FROM account coa JOIN cr_coa_to_account cta ON cta.chart_id = coa.id ORDER BY coa.accno;
This function is used for automatically matching entries from an external source like a bank-produced csv file. This function is very sensitive to ordering of inputs. NULL or empty in_scn values MUST be submitted after meaningful scns. It is also highly recommended that within each category, one submits in order of amount. We should therefore wrap it in another function which can operate on a set, perhaps in 1.4....
DECLARE in_account int; la RECORD; t_errorcode INT; our_value NUMERIC; lid INT; in_count int; t_scn TEXT; t_uid int; t_prefix text; t_amount numeric; BEGIN SELECT CASE WHEN a.category in ('A', 'E') THEN in_amount * -1 ELSE in_amount END into t_amount FROM cr_report r JOIN account a ON r.chart_id = a.id WHERE r.id = in_report_id; SELECT value into t_prefix FROM defaults WHERE setting_key = 'check_prefix'; t_uid := person__get_my_entity_id(); IF in_scn = '' THEN t_scn := NULL; ELSE t_scn := t_prefix || in_scn; END IF; IF t_scn IS NOT NULL THEN -- could this be changed to update, if not found insert? SELECT count(*) INTO in_count FROM cr_report_line WHERE scn ilike t_scn AND report_id = in_report_id AND their_balance = 0; IF in_count = 0 THEN INSERT INTO cr_report_line (report_id, scn, their_balance, our_balance, clear_time, "user", trans_type) VALUES (in_report_id, t_scn, t_amount, 0, in_date, t_uid, in_type); ELSIF in_count = 1 THEN UPDATE cr_report_line SET their_balance = t_amount, clear_time = in_date, cleared = true WHERE t_scn = scn AND report_id = in_report_id AND their_balance = 0; ELSE SELECT count(*) INTO in_count FROM cr_report_line WHERE t_scn ilike scn AND report_id = in_report_id AND our_value = t_amount and their_balance = 0; IF in_count = 0 THEN -- no match among many of values SELECT id INTO lid FROM cr_report_line WHERE t_scn ilike scn AND report_id = in_report_id ORDER BY our_balance ASC limit 1; UPDATE cr_report_line SET their_balance = t_amount, clear_time = in_date, trans_type = in_type, cleared = true WHERE id = lid; ELSIF in_count = 1 THEN -- EXECT MATCH UPDATE cr_report_line SET their_balance = t_amount, trans_type = in_type, clear_time = in_date, cleared = true WHERE t_scn = scn AND report_id = in_report_id AND our_value = t_amount AND their_balance = 0; ELSE -- More than one match SELECT id INTO lid FROM cr_report_line WHERE t_scn ilike scn AND report_id = in_report_id AND our_value = t_amount ORDER BY id ASC limit 1; UPDATE cr_report_line SET their_balance = t_amount, trans_type = in_type, cleared = true, clear_time = in_date WHERE id = lid; END IF; END IF; ELSE -- scn IS NULL, check on amount instead SELECT count(*) INTO in_count FROM cr_report_line WHERE report_id = in_report_id AND our_balance = t_amount AND their_balance = 0 and post_date = in_date and scn NOT LIKE t_prefix || '%'; IF in_count = 0 THEN -- no match INSERT INTO cr_report_line (report_id, scn, their_balance, our_balance, clear_time, "user", trans_type) VALUES (in_report_id, t_scn, t_amount, 0, in_date, t_uid, in_type); ELSIF in_count = 1 THEN -- perfect match UPDATE cr_report_line SET their_balance = t_amount, trans_type = in_type, clear_time = in_date, cleared = true WHERE report_id = in_report_id AND our_balance = t_amount AND their_balance = 0 and in_scn NOT LIKE t_prefix || '%'; ELSE -- more than one match SELECT min(id) INTO lid FROM cr_report_line WHERE report_id = in_report_id AND our_balance = t_amount AND their_balance = 0 and post_date = in_date AND scn NOT LIKE t_prefix || '%' LIMIT 1; UPDATE cr_report_line SET their_balance = t_amount, trans_type = in_type, clear_time = in_date, cleared = true WHERE id = lid; END IF; END IF; return 1; END;
This function allows a user to delete his or her own unsubmitted, unapproved reconciliation reports only. This is designed to allow a user to back out of the reconciliation process without cluttering up the search results for others.
BEGIN DELETE FROM cr_report_line WHERE report_id = in_report_id AND report_id IN (SELECT id FROM cr_report WHERE entered_username = SESSION_USER AND submitted IS NOT TRUE and approved IS NOT TRUE); DELETE FROM cr_report WHERE id = in_report_id AND entered_username = SESSION_USER AND submitted IS NOT TRUE AND approved IS NOT TRUE; RETURN FOUND; END;
This function deletes any specified unapproved transaction.
BEGIN DELETE FROM cr_report_line WHERE report_id = in_report_id AND report_id IN (SELECT id FROM cr_report WHERE approved IS NOT TRUE); DELETE FROM cr_report WHERE id = in_report_id AND approved IS NOT TRUE; RETURN FOUND; END;
Gets the cleared balance of the account specified by chart_id. This is specified in normal format (i.e. positive numbers for debits for asset and espense accounts, and positive numbers for credits in other accounts Note that currently contra accounts will show negative balances.
select CASE WHEN c.category in('A', 'E') THEN sum(ac.amount) * -1 ELSE sum(ac.amount) END FROM account c JOIN acc_trans ac ON (ac.chart_id = c.id) JOIN (select id from ar where approved union select id from ap where approved union select id from gl where approved) g on (g.id = ac.trans_id) WHERE c.id = $1 AND ac.cleared is true and ac.approved is true GROUP BY c.id, c.category;
Gets the current balance of all approved transactions against a specific account. For asset and expense accounts this is the debit balance, for others this is the credit balance.
DECLARE outval NUMERIC; BEGIN SELECT CASE WHEN (select category FROM account WHERE id = in_account_id) IN ('A', 'E') THEN sum(a.amount) * -1 ELSE sum(a.amount) END FROM acc_trans a JOIN ( SELECT id FROM ar WHERE approved is true UNION SELECT id FROM ap WHERE approved is true UNION SELECT id FROM gl WHERE approved is true ) gl ON a.trans_id = gl.id WHERE a.approved IS TRUE AND a.chart_id = in_account_id AND a.transdate <= in_date; RETURN outval; END;
Retrieves all header info from the reconciliation report.
DECLARE row cr_report; BEGIN SELECT * INTO row FROM cr_report where id = in_report_id AND scn = -1; IF NOT FOUND THEN -- I think this is a fairly major error condition RAISE EXCEPTION 'Bad report id.'; ELSE return next row; END IF; END;
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;
Ensures that the list of pending transactions in the report is up to date.
DECLARE gl_row RECORD; t_recon_fx BOOL; BEGIN SELECT recon_fx INTO t_recon_fx FROM cr_report WHERE id = in_report_id; INSERT INTO cr_report_line (report_id, scn, their_balance, our_balance, "user", voucher_id, ledger_id, post_date) SELECT in_report_id, CASE WHEN ac.source IS NULL OR ac.source = '' THEN gl.ref ELSE ac.source END, 0, sum(amount / CASE WHEN t_recon_fx IS NOT TRUE OR gl.table = 'gl' THEN 1 WHEN t_recon_fx and gl.table = 'ap' THEN ex.sell WHEN t_recon_fx and gl.table = 'ar' THEN ex.buy END) AS amount, (select entity_id from users where username = CURRENT_USER), ac.voucher_id, min(ac.entry_id), ac.transdate FROM acc_trans ac JOIN transactions t on (ac.trans_id = t.id) JOIN (select id, entity_credit_account::text as ref, curr, transdate, 'ar' as table FROM ar where approved UNION select id, entity_credit_account::text, curr, transdate, 'ap' as table FROM ap WHERE approved UNION select id, reference, '', transdate, 'gl' as table FROM gl WHERE approved) gl ON (gl.table = t.table_name AND gl.id = t.id) LEFT JOIN cr_report_line rl ON (rl.report_id = in_report_id AND ((rl.ledger_id = ac.entry_id AND ac.voucher_id IS NULL) OR (rl.voucher_id = ac.voucher_id))) LEFT JOIN exchangerate ex ON gl.transdate = ex.transdate WHERE ac.cleared IS FALSE AND ac.approved IS TRUE AND ac.chart_id = in_chart_id AND ac.transdate <= in_end_date AND ((t_recon_fx is not true and ac.fx_transaction is not true) OR (t_recon_fx is true AND (gl.table <> 'gl' OR ac.fx_transaction IS TRUE))) GROUP BY gl.ref, ac.source, ac.transdate, ac.memo, ac.voucher_id, gl.table, case when gl.table = 'gl' then gl.id else 1 end HAVING count(rl.id) = 0; UPDATE cr_report set updated = now(), their_total = coalesce(in_their_total, their_total) where id = in_report_id; RETURN in_report_id; END;
Marks the report approved and marks all cleared transactions in it cleared.
-- Does some basic checks before allowing the approval to go through; -- moves the approval to "cr_report_line", I guess, or some other "final" table. -- -- Pending may just be a single flag in the database to mark that it is -- not finalized. Will need to discuss with Chris. DECLARE current_row RECORD; completed cr_report_line; total_errors INT; in_user TEXT; ac_entries int[]; BEGIN in_user := current_user; -- so far, so good. Different user, and no errors remain. Therefore, -- we can move it to completed reports. -- -- User may not be necessary - I would think it better to use the -- in_user, to note who approved the report, than the user who -- filed it. This may require clunkier syntax.. -- ac_entries := '{}'; update cr_report set approved = 't', approved_by = person__get_my_entity_id(), approved_username = SESSION_USER where id = in_report_id; FOR current_row IN SELECT compound_array(entries) AS entries FROM ( select as_array(ac.entry_id) as entries FROM acc_trans ac JOIN transactions t on (ac.trans_id = t.id) JOIN (select id, entity_credit_account::text as ref, 'ar' as table FROM ar UNION select id, entity_credit_account::text, 'ap' as table FROM ap UNION select id, reference, 'gl' as table FROM gl) gl ON (gl.table = t.table_name AND gl.id = t.id) LEFT JOIN cr_report_line rl ON (rl.report_id = in_report_id AND ((rl.ledger_id = ac.entry_id AND ac.voucher_id IS NULL) OR (rl.voucher_id = ac.voucher_id)) and rl.cleared is true) WHERE ac.cleared IS FALSE AND ac.chart_id = (select chart_id from cr_report where id = in_report_id) GROUP BY gl.ref, ac.source, ac.transdate, ac.memo, ac.voucher_id, gl.table HAVING count(rl.report_id) > 0) a LOOP ac_entries := ac_entries || current_row.entries; END LOOP; UPDATE acc_trans SET cleared = TRUE where entry_id = any(ac_entries); return 1; END;
Returns the details of the report.
DECLARE row cr_report_line; BEGIN FOR row IN select * from cr_report_line where report_id = in_report_id order by scn, post_date LOOP RETURN NEXT row; END LOOP; END;
Pulls the payee information for the reconciliation report.
DECLARE row recon_payee; BEGIN FOR row IN select * from recon_payee where report_id = in_report_id order by scn, post_date LOOP RETURN NEXT row; END LOOP; END;
DECLARE row cr_report; BEGIN select * into row from cr_report where id = in_report_id; RETURN row; END;
Sets which lines of the report are cleared.
BEGIN UPDATE cr_report_line SET cleared = false WHERE report_id = in_report_id; UPDATE cr_report_line SET cleared = true WHERE report_id = in_report_id AND id = ANY(in_line_ids); RETURN found; END;
Searches for reconciliation reports. NULLs match all values. in_date_to and in_date_from give a range of reports. All other inputs are exact matches.
DECLARE report cr_report; BEGIN FOR report IN SELECT r.* FROM cr_report r JOIN account c ON (r.chart_id = c.id) WHERE (in_date_from IS NULL OR in_date_from <= end_date) and (in_date_to IS NULL OR in_date_to >= end_date) AND (in_balance_from IS NULL or in_balance_from <= their_total ) AND (in_balance_to IS NULL OR in_balance_to >= their_total) AND (in_chart_id IS NULL OR in_chart_id = chart_id) AND (in_submitted IS NULL or in_submitted = submitted) AND (in_approved IS NULL OR in_approved = approved) AND (r.deleted IS FALSE) ORDER BY c.accno, end_date, their_total LOOP RETURN NEXT report; END LOOP; END;
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;
SELECT null::int as id, null::bool as invoice, entity_id, meta_number, entity_name, null::date as transdate, count(*)::text as invnumber, sum(amount) as amount, sum(netamount) as netamount, sum(tax) as tax, sum(paid) as paid, sum(due) as due, max(last_payment) as last_payment, null::date as duedate, null::text as notes, null::text as till, null::text as salesperson, null::text as manager, null::text as shipping_point, null::text as ship_via, null::text[] as business_units FROM report__aa_outstanding_details($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) GROUP BY meta_number, entity_name, entity_id;
DECLARE retval aa_transactions_line; BEGIN FOR retval IN SELECT a.id, a.invoice, eeca.id, eca.meta_number, eeca.name, a.transdate, a.invnumber, a.amount, a.netamount, a.netamount - a.amount as tax, a.amount - p.due as paid, p.due, p.last_payment, a.duedate, a.notes, a.till, ee.name, me.name, a.shippingpoint, a.shipvia, '{}' as business_units -- TODO FROM (select id, transdate, invnumber, amount, netamount, duedate, notes, till, person_id, entity_credit_account, invoice, shippingpoint, shipvia, ordnumber, ponumber, description, on_hold FROM ar WHERE in_entity_class = 2 and approved UNION SELECT id, transdate, invnumber, amount, netamount, duedate, notes, null, person_id, entity_credit_account, invoice, shippingpoint, shipvia, ordnumber, ponumber, description, on_hold FROM ap WHERE in_entity_class = 1 and approved) a JOIN (SELECT trans_id, sum(amount) * CASE WHEN in_entity_class = 1 THEN 1 ELSE -1 END AS due, max(transdate) as last_payment FROM acc_trans ac JOIN account_link al ON ac.chart_id = al.account_id WHERE approved AND al.description IN ('AR', 'AP') AND (in_date_to is null or transdate <= in_date_to) GROUP BY trans_id) p ON p.trans_id = a.id JOIN entity_credit_account eca ON a.entity_credit_account = eca.id JOIN entity eeca ON eca.entity_id = eeca.id JOIN entity_employee ON entity_employee.entity_id = a.person_id JOIN entity ee ON entity_employee.entity_id = ee.id LEFT JOIN entity me ON entity_employee.manager_id = me.id WHERE (in_account_id IS NULL OR EXISTS (select 1 FROM acc_trans WHERE trans_id = a.id and chart_id = in_account_id)) AND (in_entity_name IS NULL OR eeca.name @@ plainto_tsquery(in_entity_name)) AND (in_meta_number IS NULL OR eca.meta_number ilike in_meta_number || '%') AND (in_employee_id IS NULL OR ee.id = in_employee_id) AND (in_ship_via IS NULL OR a.shipvia @@ plainto_tsquery(in_ship_via)) AND (in_on_hold IS NULL OR in_on_hold = a.on_hold) AND (in_date_from IS NULL OR a.transdate >= in_date_from) AND (in_date_to IS NULL OR a.transdate <= in_date_to) AND p.due::numeric(100,2) <> 0 LOOP RETURN NEXT retval; END LOOP; END;
DECLARE retval aa_transactions_line; BEGIN FOR retval IN SELECT a.id, a.invoice, eeca.id, eca.meta_number, eeca.name, a.transdate, a.invnumber, a.amount, a.netamount, a.amount - a.netamount as tax, a.amount - p.due, p.due, p.last_payment, a.duedate, a.notes, a.till, eee.name as employee, mee.name as manager, a.shippingpoint, a.shipvia, '{}' FROM (select id, transdate, invnumber, amount, netamount, duedate, notes, till, person_id, entity_credit_account, invoice, shippingpoint, shipvia, ordnumber, ponumber, description, on_hold FROM ar WHERE in_entity_class = 2 and approved UNION SELECT id, transdate, invnumber, amount, netamount, duedate, notes, null, person_id, entity_credit_account, invoice, shippingpoint, shipvia, ordnumber, ponumber, description, on_hold FROM ap WHERE in_entity_class = 1 and approved) a JOIN (select sum(amount) * case when in_entity_class = 1 THEN 1 ELSE -1 END as due, trans_id, max(transdate) as last_payment FROM acc_trans ac JOIN account_link l ON ac.chart_id = l.account_id WHERE l.description IN ('AR', 'AP') GROUP BY ac.trans_id ) p ON p.trans_id = a.id JOIN entity_employee ee ON ee.entity_id = a.person_id JOIN entity eee ON eee.id = ee.entity_id JOIN entity_credit_account eca ON a.entity_credit_account = eca.id JOIN entity eeca ON eca.entity_id = eeca.id LEFT JOIN entity mee ON ee.manager_id = mee.id WHERE (in_account_id IS NULL OR EXISTS (select * from acc_trans where trans_id = a.id AND chart_id = in_account_id)) AND (in_entity_name IS NULL OR eeca.name ilike in_entity_name || '%') AND (in_meta_number IS NULL OR eca.meta_number ilike in_meta_number) AND (in_employee_id = ee.entity_id OR in_employee_id IS NULL) AND (in_manager_id = mee.id OR in_manager_id IS NULL) AND (a.invnumber ilike in_invnumber || '%' OR in_invnumber IS NULL) AND (a.ordnumber ilike in_ordnumber || '%' OR in_ordnumber IS NULL) AND (a.ponumber ilike in_ponumber || '%' OR in_ponumber IS NULL) AND (in_source IS NULL OR EXISTS ( SELECT * from acc_trans where trans_id = a.id AND source ilike in_source || '%' )) AND (in_description IS NULL OR a.description @@ plainto_tsquery(in_description)) AND (in_notes IS NULL OR a.notes @@ plainto_tsquery(in_notes)) AND (in_shipvia IS NULL OR a.shipvia @@ plainto_tsquery(in_shipvia)) AND (in_date_from IS NULL OR a.transdate >= in_date_from) AND (in_date_to IS NULL OR a.transdate <= in_date_to) AND (in_on_hold IS NULL OR in_on_hold = a.on_hold) AND (in_taxable IS NULL OR (in_taxable AND (in_tax_account_id IS NULL OR EXISTS (SELECT 1 FROM acc_trans WHERE trans_id = a.id AND chart_id = in_tax_account_id) )) OR (NOT in_taxable AND NOT EXISTS (SELECT 1 FROM acc_trans ac JOIN account_link al ON al.account_id = ac.chart_id WHERE ac.trans_id = a.id AND al.description ilike '%tax')) ) LOOP RETURN NEXT retval; END LOOP; END;
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;
WITH ac (chart_id, amount) AS ( SELECT chart_id, CASE WHEN acc_trans.approved and gl.approved THEN amount ELSE 0 END FROM acc_trans JOIN (select id, approved from ar union all select id, approved from ap union all select id, approved from gl) gl ON gl.id = acc_trans.trans_id ), l(account_id, link) AS ( SELECT account_id, array_to_string(array_agg(description), ':') FROM account_link GROUP BY account_id ) SELECT a.id, a.is_heading, a.accno, a.description, a.gifi_accno, CASE WHEN sum(ac.amount) < 0 THEN sum(amount) * -1 ELSE null::numeric END, CASE WHEN sum(ac.amount) > 0 THEN sum(amount) ELSE null::numeric END, count(ac.*), l.link FROM (SELECT id,false as is_heading, accno, description, gifi_accno FROM account UNION SELECT id, true, accno, description, null::text FROM account_heading) a LEFT JOIN ac ON ac.chart_id = a.id AND not a.is_heading LEFT JOIN l ON l.account_id = a.id AND NOT a.is_heading GROUP BY a.id, a.is_heading, a.accno, a.description, a.gifi_accno, l.link ORDER BY a.accno;
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;
DECLARE retval gl_report_item; t_balance numeric; t_chart_id int; BEGIN IF in_from_date IS NULL THEN t_balance := 0; ELSIF in_accno IS NOT NULL THEN SELECT id INTO t_chart_id FROM account WHERE accno = in_accno; t_balance := account__obtain_balance(in_from_date , (select id from account where accno = in_accno)); ELSE t_balance := null; END IF; FOR retval IN WITH RECURSIVE bu_tree (id, path) AS ( SELECT id, id::text AS path FROM business_unit WHERE parent_id is null UNION SELECT bu.id, bu_tree.path || ',' || bu.id FROM business_unit bu JOIN bu_tree ON bu_tree.id = bu.parent_id ) SELECT g.id, g.type, g.invoice, g.reference, g.description, ac.transdate, ac.source, ac.amount, c.accno, c.gifi_accno, g.till, ac.cleared, ac.memo, c.description AS accname, ac.chart_id, ac.entry_id, sum(ac.amount) over (rows unbounded preceding) + t_balance as running_balance, compound_array(ARRAY[ARRAY[bac.class_id, bac.bu_id]]) FROM (select id, 'gl' as type, false as invoice, reference, description, approved, null::text as till FROM gl UNION SELECT ar.id, 'ar', invoice, invnumber, e.name, approved, till FROM ar JOIN entity_credit_account eca ON ar.entity_credit_account = eca.id JOIN entity e ON e.id = eca.entity_id UNION SELECT ap.id, 'ap', invoice, invnumber, e.name, approved, null as till FROM ap JOIN entity_credit_account eca ON ap.entity_credit_account = eca.id JOIN entity e ON e.id = eca.entity_id) g JOIN acc_trans ac ON ac.trans_id = g.id JOIN account c ON ac.chart_id = c.id LEFT JOIN business_unit_ac bac ON ac.entry_id = bac.entry_id LEFT JOIN bu_tree ON bac.bu_id = bu_tree.id WHERE (g.reference ilike in_reference || '%' or in_reference is null) AND (c.accno = in_accno OR in_accno IS NULL) AND (ac.source ilike '%' || in_source || '%' OR in_source is null) AND (ac.memo ilike '%' || in_memo || '%' OR in_memo is null) AND (in_description IS NULL OR g.description @@ plainto_tsquery(get_default_lang()::regconfig, in_description)) AND (transdate BETWEEN in_from_date AND in_to_date OR (transdate >= in_from_date AND in_to_date IS NULL) OR (transdate <= in_to_date AND in_from_date IS NULL) OR (in_to_date IS NULL AND in_from_date IS NULL)) AND (in_approved is false OR (g.approved AND ac.approved)) AND (in_from_amount IS NULL OR ac.amount >= in_from_amount) AND (in_to_amount IS NULL OR ac.amount <= in_to_amount) AND (in_category = c.category OR in_category IS NULL) GROUP BY g.id, g.type, g.invoice, g.reference, g.description, ac.transdate, ac.source, ac.amount, c.accno, c.gifi_accno, g.till, ac.cleared, ac.memo, c.description, ac.chart_id, ac.entry_id, ac.trans_id HAVING in_business_units is null or in_business_units <@ compound_array(string_to_array(bu_tree.path, ',')::int[]) ORDER BY ac.transdate, ac.trans_id, c.accno LOOP RETURN NEXT retval; END LOOP; END;
DECLARE item report_aging_item; BEGIN FOR item IN WITH RECURSIVE bu_tree (id, path) AS ( SELECT id, id::text AS path FROM business_unit WHERE id = any(in_business_units) UNION SELECT bu.id, bu_tree.path || ',' || bu.id FROM business_unit bu JOIN bu_tree ON bu_tree.id = bu.parent_id ) SELECT c.entity_id, c.meta_number, e.name, e.name as contact_name, a.invnumber, a.transdate, a.till, a.ordnumber, a.ponumber, a.notes, CASE WHEN a.age/30 = 0 THEN (a.sign * sum(ac.amount)) ELSE 0 END as c0, CASE WHEN a.age/30 = 1 THEN (a.sign * sum(ac.amount)) ELSE 0 END as c30, CASE WHEN a.age/30 = 2 THEN (a.sign * sum(ac.amount)) ELSE 0 END as c60, CASE WHEN a.age/30 > 2 THEN (a.sign * sum(ac.amount)) ELSE 0 END as c90, a.duedate, a.id, a.curr, COALESCE((SELECT sell FROM exchangerate ex WHERE a.curr = ex.curr AND ex.transdate = a.transdate), 1) AS exchangerate, (SELECT compound_array(ARRAY[[p.partnumber, i.description, i.qty::text]]) FROM parts p JOIN invoice i ON (i.parts_id = p.id) WHERE i.trans_id = a.id) AS line_items, (coalesce(in_to_date, now())::date - a.transdate) as age FROM (select id, invnumber, till, ordnumber, amount, duedate, curr, ponumber, notes, entity_credit_account, -1 AS sign, transdate, CASE WHEN in_use_duedate THEN coalesce(in_to_date, now())::date - duedate ELSE coalesce(in_to_date, now())::date - transdate END as age FROM ar WHERE in_entity_class = 2 UNION SELECT id, invnumber, null, ordnumber, amount, duedate, curr, ponumber, notes, entity_credit_account, 1 as sign, transdate, CASE WHEN in_use_duedate THEN coalesce(in_to_date, now())::date - duedate ELSE coalesce(in_to_date, now())::date - transdate END as age FROM ap WHERE in_entity_class = 1) a JOIN acc_trans ac ON ac.trans_id = a.id JOIN account acc ON ac.chart_id = acc.id JOIN account_link acl ON acl.account_id = acc.id AND ((in_entity_class = 1 AND acl.description = 'AP') OR (in_entity_class = 2 AND acl.description = 'AR')) JOIN entity_credit_account c ON a.entity_credit_account = c.id JOIN entity e ON (e.id = c.entity_id) LEFT JOIN business_unit_ac buac ON ac.entry_id = buac.entry_id LEFT JOIN bu_tree ON buac.bu_id = bu_tree.id LEFT JOIN entity_to_location e2l ON e.id = e2l.entity_id AND e2l.location_class = 3 LEFT JOIN location l ON l.id = e2l.location_id LEFT JOIN country ON (country.id = l.country_id) WHERE (e.id = in_entity_id OR in_entity_id IS NULL) AND (in_accno IS NULL or acc.accno = in_accno) GROUP BY c.entity_id, c.meta_number, e.name, l.line_one, l.line_two, l.line_three, l.city, l.state, l.mail_code, country.name, a.invnumber, a.transdate, a.till, a.ordnumber, a.ponumber, a.notes, a.amount, a.sign, a.duedate, a.id, a.curr, a.age HAVING in_business_units is null or in_business_units <@ compound_array(string_to_array(bu_tree.path, ',')::int[]) ORDER BY entity_id, curr, transdate, invnumber LOOP return next item; END LOOP; END;
SELECT entity_id, account_number, name, contact_name, null::text, null::date, null::text, null::text, null::text, null::text, sum(c0), sum(c30), sum(c60), sum(c90), null::date, null::int, curr, null::numeric, null::text[], null::int FROM report__invoice_aging_detail($1, $2, $3, $4, $5, $6) GROUP BY entity_id, account_number, name, contact_name, curr ORDER BY account_number
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;
Saves tax form information. Returns true or raises exception.
BEGIN INSERT INTO country_tax_form(country_id, form_name) values (in_country_code, in_taxform_name); RETURN true; END;
Returns a session row. If no session exists, it returns null
DECLARE out_row session%ROWTYPE; BEGIN DELETE FROM session WHERE last_used < now() - coalesce((SELECT value FROM defaults WHERE setting_key = 'session_timeout')::interval, '90 minutes'::interval); UPDATE session SET last_used = now() WHERE session_id = in_session_id AND token = in_token AND users_id = (select id from users where username = SESSION_USER); IF FOUND THEN SELECT * INTO out_row FROM session WHERE session_id = in_session_id; ELSE DELETE FROM SESSION WHERE users_id IN (select id from users where username = SESSION_USER); -- the above query also releases all discretionary locks by the -- session PERFORM * FROM defaults WHERE setting_key = 'auto_logout' and value = '1'; IF FOUND THEN RAISE NOTICE 'auto logout'; RETURN NULL; ELSE INSERT INTO session (users_id, token) SELECT id, md5(random()::text) FROM users WHERE username = SESSION_USER; SELECT * INTO out_row FROM SESSION WHERE users_id = (select id from users where username = SESSION_USER); RETURN out_row; END IF; END IF; RETURN out_row; END;
Returns an array of currencies from the defaults table.
SELECT string_to_array(value, ':') from defaults where setting_key = 'curr';
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;
Returns the value of the setting in the defaults table.
SELECT * FROM defaults WHERE setting_key = $1;
Returns a set of settings for default accounts.
DECLARE account defaults%ROWTYPE; BEGIN FOR account IN SELECT * FROM defaults WHERE setting_key like '%accno_id' ORDER BY setting_key LOOP RETURN NEXT account; END LOOP; END;
This function takes a value for a sequence in the defaults table and increments it. Leading zeroes and spaces are preserved as placeholders. Currently <?lsmb parsing is not supported in this routine though it may be added at a later date.
DECLARE base_value VARCHAR; raw_value VARCHAR; increment INTEGER; inc_length INTEGER; new_value VARCHAR; BEGIN SELECT value INTO raw_value FROM defaults WHERE setting_key = in_key FOR UPDATE; SELECT substring(raw_value from '(' || E'\\' || 'd*)(' || E'\\' || 'D*|<' || E'\\' || '?lsmb [^<>] ' || E'\\' || '?>)*$') INTO base_value; IF base_value like '0%' THEN increment := base_value::integer + 1; SELECT char_length(increment::text) INTO inc_length; SELECT overlay(base_value placing increment::varchar from (select char_length(base_value) - inc_length + 1) for inc_length) INTO new_value; ELSE new_value := base_value::integer + 1; END IF; SELECT regexp_replace(raw_value, base_value, new_value) INTO new_value; UPDATE defaults SET value = new_value WHERE setting_key = in_key; return new_value; END;
Retrieves specified tax form information from the database.
SELECT * FROM country_tax_form where id = $1;
Returns a set of all tax forms, ordered by country_id and id
SELECT * FROM country_tax_form ORDER BY country_id, id;
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;
Saves tax form information to the database.
BEGIN UPDATE country_tax_form SET country_id = in_country_id, form_name =in_form_name, default_reportable = coalesce(in_default_reportable,false), is_accrual = coalesce(in_is_accrual, false) WHERE id = in_id; IF FOUND THEN RETURN in_id; END IF; insert into country_tax_form(country_id,form_name, default_reportable) values (in_country_id, in_form_name, coalesce(in_default_reportable, false), coalesce(in_is_accrual, false)); RETURN currval('country_tax_form_id_seq'); END;
This provides a list of invoices and transactions that a report hits. This is intended to allow an organization to adjust what is reported on the 1099 before printing them.
DECLARE out_row tax_form_report_detail_item; BEGIN FOR out_row IN SELECT entity_credit_account.id, company.legal_name, company.entity_id, entity_credit_account.entity_class, entity.control_code, entity_credit_account.meta_number, sum(CASE WHEN gl.amount = 0 then 0 when relation = 'acc_trans' THEN ac.reportable_amount * pmt.amount / gl.amount ELSE 0 END * CASE WHEN gl.class = 'ap' THEN -1 else 1 end), sum(CASE WHEN gl.amount = 0 then 0 WHEN relation = 'invoice' THEN ac.reportable_amount * pmt.amount / gl.amount ELSE 0 END * CASE WHEN gl.class = 'ar' THEN -1 else 1 end), SUM(CASE WHEN gl.amount = 0 THEN 0 ELSE ac.reportable_amount * pmt.amount / gl.amount END * CASE WHEN gl.class = 'ap' THEN -1 else 1 end * CASE WHEN relation = 'invoice' THEN -1 ELSE 1 END), gl.invnumber, gl.duedate::text, gl.id FROM (select id, entity_credit_account, invnumber, duedate, amount, transdate, 'ar' as class FROM ar UNION select id, entity_credit_account, invnumber, duedate, amount, transdate, 'ap' as class FROM ap ) gl JOIN (select trans_id, 'acc_trans' as relation, sum(amount) as amount, sum(case when atf.reportable then amount else 0 end) as reportable_amount FROM acc_trans LEFT JOIN ac_tax_form atf ON (acc_trans.entry_id = atf.entry_id) GROUP BY trans_id UNION select trans_id, 'invoice' as relation, sum(sellprice * qty) as amount, sum(case when itf.reportable then sellprice * qty else 0 end) as reportable_amount FROM invoice LEFT JOIN invoice_tax_form itf ON (invoice.id = itf.invoice_id) GROUP BY trans_id ) ac ON (ac.trans_id = gl.id) JOIN entity_credit_account ON (gl.entity_credit_account = entity_credit_account.id) JOIN entity ON (entity.id = entity_credit_account.entity_id) JOIN company ON (entity.id = company.entity_id) JOIN country_tax_form ON (entity_credit_account.taxform_id = country_tax_form.id) JOIN (SELECT ac.trans_id, sum(ac.amount) as amount, as_array(entry_id) as entry_ids, as_array(chart_id) as chart_ids, count(*) as num FROM acc_trans ac where chart_id in (select account_id from account_link where description like '%paid') AND transdate BETWEEN in_begin AND in_end group by ac.trans_id ) pmt ON (pmt.trans_id = gl.id) WHERE country_tax_form.id = in_tax_form_id AND meta_number = in_meta_number GROUP BY legal_name, meta_number, company.entity_id, entity_credit_account.entity_class, entity.control_code, gl.invnumber, gl.duedate, gl.id, entity_credit_account.id LOOP RETURN NEXT out_row; END LOOP; END;
This provides a list of invoices and transactions that a report hits. This is intended to allow an organization to adjust what is reported on the 1099 before printing them.
DECLARE out_row tax_form_report_detail_item; BEGIN FOR out_row IN SELECT entity_credit_account.id, company.legal_name, company.entity_id, entity_credit_account.entity_class, entity.control_code, entity_credit_account.meta_number, sum(CASE WHEN gl.amount = 0 then 0 when relation = 'acc_trans' THEN ac.reportable_amount ELSE 0 END * CASE WHEN gl.class = 'ap' THEN -1 else 1 end), sum(CASE WHEN gl.amount = 0 then 0 WHEN relation = 'invoice' THEN ac.reportable_amount ELSE 0 END * CASE WHEN gl.class = 'ar' THEN -1 else 1 end), SUM(CASE WHEN gl.amount = 0 THEN 0 ELSE ac.reportable_amount END * CASE WHEN gl.class = 'ap' THEN -1 else 1 end * CASE WHEN relation = 'invoice' THEN -1 ELSE 1 END), gl.invnumber, gl.duedate::text, gl.id FROM (select id, entity_credit_account, invnumber, duedate, amount, transdate, 'ar' as class FROM ar WHERE transdate BETWEEN in_begin AND in_end UNION select id, entity_credit_account, invnumber, duedate, amount, transdate, 'ap' as class FROM ap WHERE transdate BETWEEN in_begin AND in_end ) gl JOIN (select trans_id, 'acc_trans' as relation, sum(amount) as amount, sum(case when atf.reportable then amount else 0 end) as reportable_amount FROM acc_trans LEFT JOIN ac_tax_form atf ON (acc_trans.entry_id = atf.entry_id) GROUP BY trans_id UNION select trans_id, 'invoice' as relation, sum(sellprice * qty) as amount, sum(case when itf.reportable then sellprice * qty else 0 end) as reportable_amount FROM invoice LEFT JOIN invoice_tax_form itf ON (invoice.id = itf.invoice_id) GROUP BY trans_id ) ac ON (ac.trans_id = gl.id) JOIN entity_credit_account ON (gl.entity_credit_account = entity_credit_account.id) JOIN entity ON (entity.id = entity_credit_account.entity_id) JOIN company ON (entity.id = company.entity_id) JOIN country_tax_form ON (entity_credit_account.taxform_id = country_tax_form.id) WHERE country_tax_form.id = in_tax_form_id AND meta_number = in_meta_number GROUP BY legal_name, meta_number, company.entity_id, entity_credit_account.entity_class, entity.control_code, gl.invnumber, gl.duedate, gl.id, entity_credit_account.id LOOP RETURN NEXT out_row; END LOOP; END;
This provides the total reportable value per vendor. As per 1099 forms, these are cash-basis documents and show amounts paid.
DECLARE out_row tax_form_report_item; BEGIN FOR out_row IN SELECT entity_credit_account.id, company.legal_name, company.entity_id, entity_credit_account.entity_class, entity.control_code, entity_credit_account.meta_number, sum(CASE WHEN gl.amount = 0 THEN 0 WHEN relation = 'acc_trans' THEN ac.reportable_amount * pmt.amount / gl.amount ELSE 0 END * CASE WHEN gl.class = 'ap' THEN -1 else 1 end), sum(CASE WHEN gl.amount = 0 THEN 0 WHEN relation = 'invoice' THEN ac.reportable_amount * pmt.amount / gl.amount ELSE 0 END * CASE WHEN gl.class = 'ar' THEN -1 else 1 end), sum(CASE WHEN gl.amount = 0 THEN 0 ELSE ac.reportable_amount * pmt.amount / gl.amount END * CASE WHEN gl.class = 'ap' THEN -1 else 1 end * CASE WHEN ac.relation = 'invoice' then -1 else 1 end) FROM (select id, transdate, entity_credit_account, invoice, amount, 'ar' as class FROM ar UNION select id, transdate, entity_credit_account, invoice, amount, 'ap' as class from ap ) gl JOIN (select trans_id, 'acc_trans' as relation, sum(amount) as amount, sum(case when atf.reportable then amount else 0 end) as reportable_amount FROM acc_trans LEFT JOIN ac_tax_form atf ON (acc_trans.entry_id = atf.entry_id) GROUP BY trans_id UNION select trans_id, 'invoice' as relation, sum(sellprice * qty) as amount, sum(case when itf.reportable then sellprice * qty else 0 end) as reportable_amount FROM invoice LEFT JOIN invoice_tax_form itf ON (invoice.id = itf.invoice_id) GROUP BY trans_id ) ac ON (ac.trans_id = gl.id AND ((gl.invoice is true and ac.relation='invoice') OR (gl.invoice is false and ac.relation='acc_trans'))) JOIN (SELECT ac.trans_id, sum(ac.amount) as amount, as_array(entry_id) as entry_ids, as_array(chart_id) as chart_ids, count(*) as num FROM acc_trans ac where chart_id in (select account_id from account_link where description like '%paid') AND transdate BETWEEN in_begin AND in_end group by ac.trans_id ) pmt ON (pmt.trans_id = gl.id) JOIN entity_credit_account ON (gl.entity_credit_account = entity_credit_account.id) JOIN entity ON (entity.id = entity_credit_account.entity_id) JOIN company ON (entity.id = company.entity_id) JOIN country_tax_form ON (entity_credit_account.taxform_id = country_tax_form.id) WHERE country_tax_form.id = in_tax_form_id GROUP BY legal_name, meta_number, company.entity_id, entity_credit_account.entity_class, entity.control_code, entity_credit_account.id LOOP RETURN NEXT out_row; END LOOP; END;
This provides the total reportable value per vendor. As per 1099 forms, these are cash-basis documents and show amounts paid.
DECLARE out_row tax_form_report_item; BEGIN FOR out_row IN SELECT entity_credit_account.id, company.legal_name, company.entity_id, entity_credit_account.entity_class, entity.control_code, entity_credit_account.meta_number, sum(CASE WHEN gl.amount = 0 THEN 0 WHEN relation = 'acc_trans' THEN ac.reportable_amount ELSE 0 END * CASE WHEN gl.class = 'ap' THEN -1 else 1 end), sum(CASE WHEN gl.amount = 0 THEN 0 WHEN relation = 'invoice' THEN ac.reportable_amount ELSE 0 END * CASE WHEN gl.class = 'ar' THEN -1 else 1 end), sum(CASE WHEN gl.amount = 0 THEN 0 ELSE ac.reportable_amount END * CASE WHEN gl.class = 'ap' THEN -1 else 1 end * CASE WHEN ac.relation = 'invoice' then -1 else 1 end) FROM (select id, transdate, entity_credit_account, invoice, amount, 'ar' as class FROM ar WHERE transdate BETWEEN in_begin AND in_end UNION select id, transdate, entity_credit_account, invoice, amount, 'ap' as class from ap WHERE transdate BETWEEN in_begin AND in_end ) gl JOIN (select trans_id, 'acc_trans' as relation, sum(amount) as amount, sum(case when atf.reportable then amount else 0 end) as reportable_amount FROM acc_trans LEFT JOIN ac_tax_form atf ON (acc_trans.entry_id = atf.entry_id) GROUP BY trans_id UNION select trans_id, 'invoice' as relation, sum(sellprice * qty) as amount, sum(case when itf.reportable then sellprice * qty else 0 end) as reportable_amount FROM invoice LEFT JOIN invoice_tax_form itf ON (invoice.id = itf.invoice_id) GROUP BY trans_id ) ac ON (ac.trans_id = gl.id AND ((gl.invoice is true and ac.relation='invoice') OR (gl.invoice is false and ac.relation='acc_trans'))) JOIN entity_credit_account ON (gl.entity_credit_account = entity_credit_account.id) JOIN entity ON (entity.id = entity_credit_account.entity_id) JOIN company ON (entity.id = company.entity_id) JOIN country_tax_form ON (entity_credit_account.taxform_id = country_tax_form.id) WHERE country_tax_form.id = in_tax_form_id GROUP BY legal_name, meta_number, company.entity_id, entity_credit_account.entity_class, entity.control_code, entity_credit_account.id LOOP RETURN NEXT out_row; END LOOP; END;
SELECT id FROM chart WHERE accno = $1;
DECLARE r_eclass entity_class; roll_pfx text; BEGIN IF TG_OP = 'DELETE' THEN RETURN OLD; ELSE SELECT value INTO roll_pfx FROM defaults WHERE setting_key = 'roll_prefix'; SELECT * INTO r_eclass from entity_class WHERE id = NEW.entity_class; IF pg_has_role(SESSION_USER, coalesce(roll_pfx, 'lsmb_' || current_database() || '__') || 'contact_class_' || lower(regexp_replace( r_eclass.class, ' ', '_')), 'USAGE') THEN RETURN NEW; ELSE RAISE EXCEPTION 'Access Denied for class'; END IF; END IF; END;
SELECT * FROM jcitems WHERE id = $1;
SELECT * FROM parts WHERE not obsolete AND ($1 OR inventory_accno_id IS NULL) AND ($2 OR (income_accno_id IS NOT NULL AND inventory_accno_id IS NULL)) ORDER BY partnumber;
WITH RECURSIVE bu_tree (id, path) AS ( SELECT id, id::text AS path, control_code, description FROM business_unit WHERE id = any($1) UNION SELECT bu.id, bu_tree.path || ',' || bu.id, bu.control_code, bu.description FROM business_unit bu JOIN bu_tree ON bu_tree.id = bu.parent_id ) SELECT j.id, j.description, j.qty, j.allocated, j.checkedin::time as checkedin, j.checkedout::time as checkedout, j.checkedin::date as transdate, extract('dow' from j.checkedin) as weekday, extract('week' from j.checkedin) as workweek, p.partnumber, bu.control_code as business_unit_code, bu.description AS businessunit_description, ee.employeenumber, e.name AS employee, j.parts_id, j.sellprice FROM jcitems j JOIN parts p ON p.id = j.parts_id JOIN person ON person.id = j.person_id JOIN entity_employee ee ON ee.entity_id = person.entity_id JOIN entity e ON ee.entity_id = e.id JOIN bu_tree bu ON bu.id = j.business_unit_id WHERE (p.partnumber = $2 OR $2 IS NULL) AND (ee.entity_id = $3 OR $3 IS NULL) AND (j.checkedin::date <= $4 OR $4 IS NULL) AND (j.checkedin::date >= $5 OR $5 IS NULL) AND (j.qty > j.allocated AND $6) AND (j.qty <= j.allocated AND $7);
DECLARE retval jcitems; BEGIN UPDATE jcitems SET description = in_description, qty = in_qty, allocated = in_allocated, serialnumber = in_serialnumber, checkedin = in_checkedin, checkedout = in_checkedout, person_id = person__get_my_entity_id(), notes = in_notes, total = in_total, non_billable = in_non_billable WHERE id = in_id; IF FOUND THEN SELECT * INTO retval WHERE id = in_id; return retval; END IF; INSERT INTO jcitems (business_unit_id, parts_id, description, qty, allocated, sellprice, fxsellprice, serialnumber, checkedin, checkedout, person_id, notes, total, non_billable) VALUES (in_business_unit_id, in_parts_id, in_description, in_qty, in_allocated, in_sellprice, in_fxsellprice, in_serialnumber, in_checkedin, in_checkedout, in_person_id, in_notes, in_total, in_non_billable); SELECT * INTO retval WHERE id = currval('jcitems_id_seq')::int; RETURN retval; END;
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;
Turns a setof ARRAY[key,value] into an ARRAY[key||'='||value, key||'='||value,...]
aggregate_dummy
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;
BEGIN IF NEW.onhand >= NEW.rop THEN NOTIFY parts_short; END IF; RETURN NEW; END;
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;
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;
Alloes a user to change his or her own password. The password is set to expire setting_get('password_duration') days after the password change.
DECLARE t_expires timestamp; t_password_duration text; BEGIN SELECT value INTO t_password_duration FROM defaults WHERE setting_key = 'password_duration'; IF t_password_duration IS NULL or t_password_duration='' THEN t_expires := 'infinity'; ELSE t_expires := now() + (t_password_duration::numeric::text || ' days')::interval; END IF; UPDATE users SET notify_password = DEFAULT where username = SESSION_USER; EXECUTE 'ALTER USER ' || quote_ident(SESSION_USER) || ' with ENCRYPTED password ' || quote_literal(in_new_password) || ' VALID UNTIL '|| quote_literal(t_expires); return 1; END;
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;
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';
select * from user_listable;
Returns the preferences row for the user.
declare v_row user_preference; BEGIN select * into v_row from user_preference where id = in_user_id; IF NOT FOUND THEN RAISE EXCEPTION 'Could not find user preferences for id %', in_user_id; ELSE return next v_row; END IF; END;
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;
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;
Retrieves a list of vouchers and amounts attached to the batch.
declare voucher_item record; BEGIN FOR voucher_item IN SELECT v.id, a.invoice, a.invnumber, e.name, v.batch_id, v.trans_id, a.amount, a.transdate, 'Payable' FROM voucher v JOIN ap a ON (v.trans_id = a.id) JOIN entity_credit_account eca ON (eca.id = a.entity_credit_account) JOIN entity e ON (eca.entity_id = e.id) WHERE v.batch_id = in_batch_id AND v.batch_class = (select id from batch_class WHERE class = 'ap') UNION SELECT v.id, a.invoice, a.invnumber, e.name, v.batch_id, v.trans_id, a.amount, a.transdate, 'Receivable' FROM voucher v JOIN ar a ON (v.trans_id = a.id) JOIN entity_credit_account eca ON (eca.id = a.entity_credit_account) JOIN entity e ON (eca.entity_id = e.id) WHERE v.batch_id = in_batch_id AND v.batch_class = (select id from batch_class WHERE class = 'ar') UNION ALL -- TODO: Add the class labels to the class table. SELECT v.id, false, a.source, cr.meta_number || '--' || co.legal_name , v.batch_id, v.trans_id, sum(CASE WHEN bc.class LIKE 'payment%' THEN a.amount * -1 ELSE a.amount END), a.transdate, CASE WHEN bc.class = 'payment' THEN 'Payment' WHEN bc.class = 'payment_reversal' THEN 'Payment Reversal' END FROM voucher v JOIN acc_trans a ON (v.id = a.voucher_id) JOIN batch_class bc ON (bc.id = v.batch_class) JOIN chart c ON (a.chart_id = c.id) JOIN ap ON (ap.id = a.trans_id) JOIN entity_credit_account cr ON (ap.entity_credit_account = cr.id) JOIN company co ON (cr.entity_id = co.entity_id) WHERE v.batch_id = in_batch_id AND a.voucher_id = v.id AND (bc.class like 'payment%' AND c.link = 'AP') GROUP BY v.id, a.source, cr.meta_number, co.legal_name , v.batch_id, v.trans_id, a.transdate, bc.class UNION ALL SELECT v.id, false, a.source, a.memo, v.batch_id, v.trans_id, CASE WHEN bc.class LIKE 'receipt%' THEN sum(a.amount) * -1 ELSE sum(a.amount) END, a.transdate, CASE WHEN bc.class = 'receipt' THEN 'Receipt' WHEN bc.class = 'receipt_reversal' THEN 'Receipt Reversal' END FROM voucher v JOIN acc_trans a ON (v.id = a.voucher_id) JOIN batch_class bc ON (bc.id = v.batch_class) JOIN chart c ON (a.chart_id = c.id) JOIN ar ON (ar.id = a.trans_id) JOIN entity_credit_account cr ON (ar.entity_credit_account = cr.id) JOIN company co ON (cr.entity_id = co.entity_id) WHERE v.batch_id = in_batch_id AND a.voucher_id = v.id AND (bc.class like 'receipt%' AND c.link = 'AR') GROUP BY v.id, a.source, cr.meta_number, co.legal_name , a.memo, v.batch_id, v.trans_id, a.transdate, bc.class UNION ALL SELECT v.id, false, g.reference, g.description, v.batch_id, v.trans_id, sum(a.amount), g.transdate, 'GL' FROM voucher v JOIN gl g ON (g.id = v.trans_id) JOIN acc_trans a ON (v.trans_id = a.trans_id) WHERE a.amount > 0 AND v.batch_id = in_batch_id AND v.batch_class IN (select id from batch_class where class = 'gl') GROUP BY v.id, g.reference, g.description, v.batch_id, v.trans_id, g.transdate ORDER BY 7, 1 LOOP RETURN NEXT voucher_item; END LOOP; END;
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;
SELECT * FROM payroll_wage WHERE entity_id = $1;
SELECT * FROM payroll_income_type where country_id = $1
BEGIN UPDATE payroll_wage SET rate = in_rate WHERE entity_id = in_entity_id and in_type_id; IF NOT FOUND THEN INSERT INTO payroll_wage (entity_id, type_id, rate) VALUES (in_entity_id, in_type_id, in_rate); END IF; RETURN QUERY SELECT * FROM payroll_wage WHERE entity_id = in_entity_id and in_type_id; END;
SELECT * FROM warehouse order by description;
Generated by PostgreSQL Autodoc