File: Journal.sql

package info (click to toggle)
ledgersmb 1.6.33%2Bds-2.1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 33,000 kB
  • sloc: perl: 52,612; sql: 43,562; xml: 36,194; javascript: 2,428; sh: 1,099; makefile: 361; pascal: 25
file content (228 lines) | stat: -rw-r--r-- 6,867 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228

set client_min_messages = 'warning';


begin;

--Journal entry stored procedures for LedgerSMB
--Copyright (C) 2011, The LedgerSMB Core Team

--Permission granted to use this work in accordance with the GNU General Public
--License version 2 or at your option any later version.  Please see included
--LICENSE file for details.

-- This file contains many functions which are by nature security definer
-- functions.  The tradeoff security-wise is that we can more tightly control
-- what can be inserted into the tables via security definer functions, but
-- at the same time the opportunity for privilege escallation is also higher
-- because security definer functions to some extent break a declarative
-- security model.   As always avoid executing dynamic SQL as much as possible,
-- etc.

CREATE TYPE journal_entry_ext AS (
    id int,
    reference text,
    description text,
    journal int,
    journal_name text,
    post_date date,
    effective_start date,
    effective_end date,
    currency char(3),
    approved bool,
    is_template bool,
    entered_by int,
    entered_by_name text,
    approved_by int,
    approved_by_name text,
    lines journal_line[]
);

COMMENT ON TYPE journal_entry_ext IS
$$ Contains all relevant data for journal entries. $$;

CREATE OR REPLACE FUNCTION je_get (arg_id int) returns journal_entry_ext AS
$$
SELECT je.id, je.reference, je.journal, j.name, je.post_date,
       je.effective_start, je.effective_end, je.currency, je.approved,
       je.is_template, je.entered_by, ee.name, je.approved_by, ae.name,
       array_agg(row(jl.*))
  FROM journal_entry je
  JOIN journal j ON je.journal = j.id
  JOIN entity ee ON je.entered_by = ee.id
  JOIN entity ae ON je.approved_by = ae.id
  JOIN journal_line jl ON jl.je_id = je.id
 WHERE je.id = $1
 GROUP BY je.id, je.reference, je.journal, j.name, je.post_date,
       je.effective_start, je.effective_end, je.currency, je.approved,
       je.is_template, je.entered_by, ee.name, je.approved_by, ae.name;
$$ language sql;

COMMENT ON FUNCTION je_get (arg_id int) IS
$$ This is a simple function to retrieve the journal item of the id sent in the
search crieria.$$;

CREATE OR REPLACE FUNCTION je_approve (prop_id int) returns journal_entry_ext
AS $$
-- Must be security definer.  otherwise we risk giving people permission to
-- de-approve transactions which is bad, even with column perms.  --CT
UPDATE journal_entry
   SET approved = true,
       approved_by = person__get_my_entity_id()
 WHERE id = $1;

SELECT je_get($1);
$$ LANGUAGE SQL SECURITY DEFINER;

COMMENT ON FUNCTION je_approve (prop_id int) IS
$$ This function approvies the journal entry specified.$$;

CREATE OR REPLACE FUNCTION je_delete_unapproved(arg_id int)
RETURNS journal_entry_ext AS
$$
DELETE FROM journal_line
 WHERE je_id = (select id
                 from journal_entry
                where id = $1 and approved is false);

DELETE FROM journal_id
 WHERE id = $1 and approved is false;

SELECT je_get($1);
$$ language sql SECURITY DEFINER;

REVOKE EXECUTE ON je_approve FROM public;

CREATE OR REPLACE FUNCTION je_modify_and_approve (
prop_id int
prop_reference text,
prop_description text,
prop_post_date date,
prop_currency char(3),
prop_effective_start date,
prop_effective_end date,
prop_lines journal_line[]
) RETURNS journal_entry_ext AS
$$
DECLARE
    test bool;
BEGIN

-- error handling and checks before we begin
IF (pg_has_role(lsmb_role('draft_modify')) IS NOT TRUE THEN
    RAISE EXCEPTION 'Access denied';
END IF;

SELECT sum(amount) = 0 INTO test FROM expand(prop_lines);

IF TEST IS NOT TRUE THEN
   RAISE EXCEPTION 'Unbalanced transaction';
END IF;

SELECT approved IS FALSE INTO test FROM journal_entry WHERE id = prop_id;

IF TEST IS NOT TRUE THEN
   RAISE EXCEPTION 'Transaction laready approved';
END IF;

-- main function

DELETE FROM journal_line WHERE je_id = prop_id;

UPDATE journal_entry
   SET reference = prop_reference,
       description = prop_description,
       post_date = prop_post_date,
       effective_start = coalesce(prop_effective_start, prop_post_date),
       effective_end = coalesce(prop_effective_end, prop_post_date)
 WHERE id = prop_id

IF NOT FOUND THEN
    RAISE EXCEPTION 'Entry not found'
END IF;

INSERT INTO journal_line
            (je_id, account_id, amount, project_id, department_id)
     SELECT prop_id, account_id, amount, project_id, department_id
       FROM expand(prop_lines);

RETURN je_get(prop_id);

END;

$$ LANGUAGE PLPGSQL SECURITY DEFINER;

CREATE OR REPLACE FUNCTION je_add(
prop_reference text,
prop_description text,
prop_journal int,
prop_post_date date,
prop_is_template bool,
prop_currency char(3),
prop_effective_start date,
prop_effective_end date,
prop_lines journal_line[]
) RETURNS journal_entry_ext AS
$$
DECLARE retval journal_entry_ext;
     test bool;
     separate_duties bool;
BEGIN
   -- must be security definer because otherwise we can't guarantee balanced
   -- transactions --CT
   SELECT sum(amount) = 0 into test FROM expand(prop_lines);
   IF test is not true
     RAISE EXCEPTION 'Unbalanced transaction';
   END IF;

   SELECT value <> '0' INTO separate_duties
     FROM defaults
    WHERE setting_key = 'separate_duties';

   INSERT INTO journal_entry
               (reference, description, journal, post_date, is_template,
               currency, effective_start, effective_end, approved)
        VALUES (prop_reference, prop_description, prop_journal, prop_post_date,
               prop_is_template, prop_currency,
               coalesce(prop_effective_start, prop_post_date),
               coalesce(prop_effective_end, prop_post_date),
               separate_duties is false);

   INSERT
     INTO journal_line
          (je_id, account_id, amount, project_id, department_id)
   SELECT currval('journal_entry_id_seq'), account_id, amount,
          project_id, department_id
     FROM expand(prop_lines);

   RETURN je_get(currval('journal_entry_id_seq'));
END;
$$ LANGUAGE PLPGSQL SECURITY DEFINER;

CREATE OR REPLACE FUNCTION je_reverse
(arg_id int, arg_reference text, arg_post_date date)
RETURNS journal_entry_ext AS
$$
INSERT
  INTO journal_entry
       (reference, description, journal, post_date, is_template,
       currency, effective_start, effective_end, approved)
SELECT $2, description, journal, coalesce($3, post_date),
       0, currency, effective_strt, effective_end, d.value = '0'
  FROM journal_entry je, defaults d
 WHERE d.setting_key = 'separate_duties' and je.id = $1;

INSERT
  INTO journal_line
       (je_id, account_id, amount, project_id, department_id)
SELECT currval('journal_entry_id_seq'), account_id, amount * -1, project_id,
       department_id
  FROM journal_line
 WHERE je_id = $1;

SELECT je_get(currval('journal_entry_id_seq'));
$$ LANGUAGE SQL SECURITY DEFINER;

update defaults set value = 'yes' where setting_key = 'module_load_ok';

commit;