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 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270
|
set client_min_messages = 'warning';
BEGIN;
CREATE OR REPLACE FUNCTION eoy__latest_checkpoint() RETURNS account_checkpoint
LANGUAGE SQL AS
$$
SELECT * FROM account_checkpoint ORDER BY end_date DESC LIMIT 1;
$$;
COMMENT ON FUNCTION eoy__latest_checkpoint() IS $$
This returns a single checkpoint from the latest set. Which account and info
is returned is non-determinative and so only the end date shoudl be relied on.
$$;
CREATE OR REPLACE FUNCTION eoy_create_checkpoint(in_end_date date)
RETURNS int AS
$$
DECLARE ret_val int;
approval_check int;
cp_date date;
BEGIN
IF in_end_date > now()::date THEN
RAISE EXCEPTION 'Invalid date: Must be earlier than present';
END IF;
SELECT count(*) into approval_check
FROM acc_trans ac
JOIN (
select id, approved, transdate FROM ar UNION
SELECT id, approved, transdate FROM gl UNION
SELECT id, approved, transdate FROM ap
) gl ON (gl.id = ac.trans_id)
WHERE (ac.approved IS NOT TRUE AND ac.transdate <= in_end_date)
OR (gl.approved IS NOT TRUE AND gl.transdate <= in_end_date);
if approval_check > 0 THEN
RAISE EXCEPTION 'Unapproved transactions in closed period';
END IF;
SELECT max(end_date) INTO cp_date FROM account_checkpoint WHERE
end_date < in_end_date;
INSERT INTO
account_checkpoint (end_date, account_id, amount, debits, credits)
SELECT in_end_date, account.id,
COALESCE(SUM (a.amount),0) + coalesce(MAX (cp.amount), 0),
COALESCE(SUM (CASE WHEN (a.amount < 0) THEN a.amount ELSE 0 END), 0) +
COALESCE( MIN (cp.debits), 0),
COALESCE(SUM (CASE WHEN (a.amount > 0) THEN a.amount ELSE 0 END), 0) +
COALESCE( MAX (cp.credits), 0)
FROM
(SELECT * FROM acc_trans WHERE transdate <= in_end_date AND
transdate > COALESCE(cp_date, '1200-01-01')) a
FULL OUTER JOIN (
select account_id, end_date, amount, debits, credits
from account_checkpoint
WHERE end_date = cp_date
) cp on (a.chart_id = cp.account_id)
RIGHT JOIN account ON account.id = a.chart_id or account.id = cp.account_id
group by COALESCE(a.chart_id, cp.account_id), account.id;
SELECT count(*) INTO ret_val FROM account_checkpoint
where end_date = in_end_date;
return ret_val;
END;
$$ language plpgsql;
COMMENT ON FUNCTION eoy_create_checkpoint(in_end_date date) IS
$$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).$$;
CREATE OR REPLACE FUNCTION eoy_zero_accounts
(in_end_date date, in_reference text, in_description text,
in_retention_acc_id int)
RETURNS int AS
$$
DECLARE ret_val int;
BEGIN
INSERT INTO gl (transdate, reference, description, approved,
trans_type_code)
VALUES (in_end_date, in_reference, in_description, true, 'ye');
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;
$$ language plpgsql;
COMMENT ON FUNCTION eoy_zero_accounts
(in_end_date date, in_reference text, in_description text,
in_retention_acc_id int) IS
$$ 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.$$;
CREATE OR REPLACE FUNCTION eoy_close_books
(in_end_date date, in_reference text, in_description text,
in_retention_acc_id int)
RETURNS bool AS
$$
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;
$$ LANGUAGE PLPGSQL;
COMMENT ON FUNCTION eoy_close_books
(in_end_date date, in_reference text, in_description text,
in_retention_acc_id int) IS
$$ 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.$$;
CREATE OR REPLACE FUNCTION eoy_reopen_books(in_end_date date)
RETURNS bool AS
$$
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,
trans_type_code)
SELECT 'Reversing ' || reference, 'Reversing ' || description,
true, 'ye'
FROM gl WHERE id = (select trans_id from yearend
where transdate = in_end_date and reversed is not true);
INSERT INTO acc_trans (chart_id, amount, transdate, trans_id,
approved)
SELECT chart_id, amount * -1, in_end_date, currval('id'), true
FROM acc_trans where trans_id = (select trans_id from yearend
where transdate = in_end_date and reversed is not true);
UPDATE yearend SET reversed = true where transdate = in_end_date
and reversed is not true;
END IF;
DELETE FROM account_checkpoint WHERE end_date = in_end_date;
RETURN TRUE;
END;
$$ LANGUAGE PLPGSQL;
COMMENT ON FUNCTION eoy_reopen_books(in_end_date date) IS
$$ Removes checkpoints and reverses yearend transactions on in_end_date$$;
CREATE OR REPLACE FUNCTION eoy__reopen_books_at(in_reopen_date date)
RETURNS BOOL
LANGUAGE SQL AS
$$
WITH eoy_dates AS (
SELECT end_date
FROM account_checkpoint
WHERE end_date >= $1
GROUP BY end_date
ORDER BY end_date DESC
)
SELECT eoy_reopen_books(end_date)
FROM eoy_dates;
SELECT CASE WHEN (SELECT count(*) > 0 from account_checkpoint
where end_date = $1 - 1)
THEN true
ELSE eoy_create_checkpoint($1 - 1) > 0
END;
$$;
CREATE OR REPLACE FUNCTION account__obtain_balance
(in_transdate date, in_account_id int)
RETURNS numeric AS
$$
WITH cp AS (
SELECT amount, end_date, account_id
FROM account_checkpoint
WHERE account_id = in_account_id
AND end_date <= in_transdate
ORDER BY end_date DESC LIMIT 1
),
ac AS (
SELECT acc_trans.amount
FROM acc_trans
JOIN (select id from ar where approved
union select id from ap where approved
union select id from gl where approved) a on acc_trans.trans_id = a.id
LEFT JOIN cp ON acc_trans.chart_id = cp.account_id
WHERE (cp.end_date IS NULL OR transdate > cp.end_date)
AND transdate <= in_transdate
AND chart_id = in_account_id)
SELECT coalesce((select sum(amount)
from (select sum(amount) as amount from cp
union all
select sum(amount) from ac) as a),
0);
$$ LANGUAGE SQL;
COMMENT ON FUNCTION account__obtain_balance
(in_transdate date, in_account_id int) is
$$Returns the account balance at a given point in time, calculating forward
from most recent check point. This function is inclusive of in_transdate. For
an exclusive function see account__obtain_starting_balance below.$$;
CREATE OR REPLACE FUNCTION account__obtain_starting_balance
(in_transdate date, in_account_id int)
RETURNS numeric LANGUAGE SQL AS
$$
SELECT account__obtain_balance($1 - 1, $2);
$$;
CREATE OR REPLACE FUNCTION eoy_earnings_accounts() RETURNS setof account AS
$$
SELECT *
FROM account
WHERE category = 'Q'
ORDER BY accno;
$$ language sql;
COMMENT ON FUNCTION eoy_earnings_accounts() IS
$$ Lists equity accounts for the retained earnings dropdown.$$;
update defaults set value = 'yes' where setting_key = 'module_load_ok';
COMMIT;
|