summaryrefslogtreecommitdiff
path: root/sql/modules/Payment.sql
blob: 2ab6cf69316beb76a8de7602cb099a2f3ad125de (plain)
  1. -- payment_get_open_accounts and the option to get all accounts need to be
  2. -- refactored and redesigned. -- CT
  3. CREATE OR REPLACE FUNCTION payment_get_open_accounts(in_account_class int)
  4. returns SETOF entity AS
  5. $$
  6. DECLARE out_entity entity%ROWTYPE;
  7. BEGIN
  8. FOR out_entity IN
  9. SELECT ec.id, cp.legal_name as name, e.entity_class, e.created
  10. FROM entity e
  11. JOIN entity_credit_account ec ON (ec.entity_id = e.id)
  12. JOIN company cp ON (cp.entity_id = e.id)
  13. WHERE ec.entity_class = in_account_class
  14. AND CASE WHEN in_account_class = 1 THEN
  15. e.id IN (SELECT entity_id FROM ap
  16. WHERE amount <> paid
  17. GROUP BY entity_id)
  18. WHEN in_account_class = 2 THEN
  19. e.id IN (SELECT entity_id FROM ar
  20. WHERE amount <> paid
  21. GROUP BY entity_id)
  22. END
  23. LOOP
  24. RETURN NEXT out_entity;
  25. END LOOP;
  26. END;
  27. $$ LANGUAGE PLPGSQL;
  28. COMMENT ON FUNCTION payment_get_open_accounts(int) IS
  29. $$ This function takes a single argument (1 for vendor, 2 for customer as
  30. always) and returns all entities with open accounts of the appropriate type. $$;
  31. CREATE OR REPLACE FUNCTION payment_get_all_accounts(in_account_class int)
  32. RETURNS SETOF entity AS
  33. $$
  34. DECLARE out_entity entity%ROWTYPE;
  35. BEGIN
  36. FOR out_entity IN
  37. SELECT ec.id,
  38. e.name, e.entity_class, e.created
  39. FROM entity e
  40. JOIN entity_credit_account ec ON (ec.entity_id = e.id)
  41. WHERE e.entity_class = in_account_class
  42. LOOP
  43. RETURN NEXT out_entity;
  44. END LOOP;
  45. END;
  46. $$ LANGUAGE PLPGSQL;
  47. COMMENT ON FUNCTION payment_get_open_accounts(int) IS
  48. $$ This function takes a single argument (1 for vendor, 2 for customer as
  49. always) and returns all entities with accounts of the appropriate type. $$;
  50. CREATE TYPE payment_invoice AS (
  51. invoice_id int,
  52. invnumber text,
  53. invoice_date date,
  54. amount numeric,
  55. discount numeric,
  56. due numeric
  57. );
  58. CREATE OR REPLACE FUNCTION payment_get_open_invoices
  59. (in_account_class int,
  60. in_entity_credit_id int,
  61. in_curr char(3),
  62. in_datefrom date,
  63. in_dateto date,
  64. in_amountfrom numeric,
  65. in_amountto numeric,
  66. in_department_id int)
  67. RETURNS SETOF payment_invoice AS
  68. $$
  69. DECLARE payment_inv payment_invoice;
  70. BEGIN
  71. FOR payment_inv IN
  72. SELECT a.id AS invoice_id, a.invnumber,
  73. a.transdate AS invoice_date, a.amount,
  74. CASE WHEN discount_terms
  75. > extract('days' FROM age(a.transdate))
  76. THEN 0
  77. ELSE (a.amount - a.paid) * c.discount / 100
  78. END AS discount,
  79. a.amount - a.paid -
  80. CASE WHEN discount_terms
  81. > extract('days' FROM age(a.transdate))
  82. THEN 0
  83. ELSE (a.amount - a.paid) * c.discount / 100
  84. END
  85. AS due
  86. FROM (SELECT id, invnumber, transdate, amount, entity_id,
  87. 1 as invoice_class, paid, curr,
  88. entity_credit_account, department_id
  89. FROM ap
  90. UNION
  91. SELECT id, invnumber, transdate, amount, entity_id,
  92. 2 AS invoice_class, paid, curr,
  93. entity_credit_account, department_id
  94. FROM ar
  95. ) a
  96. JOIN entity_credit_account c ON (c.id = a.entity_credit_account
  97. OR (a.entity_credit_account IS NULL and
  98. a.entity_id = c.entity_id))
  99. WHERE a.invoice_class = in_account_class
  100. AND c.entity_class = in_account_class
  101. AND a.amount - a.paid <> 0
  102. AND a.curr = in_curr
  103. AND (a.transdate >= in_datefrom
  104. OR in_datefrom IS NULL)
  105. AND (a.transdate <= in_dateto
  106. OR in_dateto IS NULL)
  107. AND (a.amount >= in_amountfrom
  108. OR in_amountfrom IS NULL)
  109. AND (a.amount <= in_amountto
  110. OR in_amountto IS NULL)
  111. AND (a.department_id = in_department_id
  112. OR in_department_id IS NULL)
  113. LOOP
  114. RETURN NEXT payment_inv;
  115. END LOOP;
  116. END;
  117. $$ LANGUAGE PLPGSQL;
  118. COMMENT ON FUNCTION payment_get_open_invoices(int, int, char(3), date, date, numeric, numeric, int) IS
  119. $$ This function takes three arguments:
  120. Type: 1 for vendor, 2 for customer
  121. Entity_id: The entity_id of the customer or vendor
  122. Currency: 3 characters for currency ('USD' for example).
  123. Returns all open invoices for the entity in question. $$;
  124. CREATE TYPE payment_contact_invoice AS (
  125. contact_id int,
  126. contact_name text,
  127. account_number text,
  128. total_due numeric,
  129. invoices text[],
  130. has_vouchers int
  131. );
  132. CREATE OR REPLACE FUNCTION payment_get_all_contact_invoices
  133. (in_account_class int, in_business_id int, in_currency char(3),
  134. in_date_from date, in_date_to date, in_batch_id int,
  135. in_ar_ap_accno text, in_meta_number text)
  136. RETURNS SETOF payment_contact_invoice AS
  137. $$
  138. DECLARE payment_item payment_contact_invoice;
  139. BEGIN
  140. FOR payment_item IN
  141. SELECT c.id AS contact_id, e.name AS contact_name,
  142. c.meta_number AS account_number,
  143. sum(p.due) AS total_due,
  144. compound_array(ARRAY[[
  145. a.id::text, a.invnumber, a.transdate::text,
  146. a.amount::text, (a.amount - p.due)::text,
  147. (CASE WHEN c.discount_terms
  148. > extract('days' FROM age(a.transdate))
  149. THEN 0
  150. ELSE (a.amount - coalesce((a.amount - p.due), 0)) * coalesce(c.discount, 0) / 100
  151. END)::text,
  152. (coalesce(p.due, 0) -
  153. (CASE WHEN c.discount_terms
  154. > extract('days' FROM age(a.transdate))
  155. THEN 0
  156. ELSE (coalesce(p.due, 0)) * coalesce(c.discount, 0) / 100
  157. END))::text]]),
  158. sum(case when a.batch_id = in_batch_id then 1
  159. else 0 END),
  160. bool_and(lock_record(a.id, (select max(session_id) FROM "session" where users_id = (
  161. select id from users WHERE username =
  162. SESSION_USER))))
  163. FROM entity e
  164. JOIN entity_credit_account c ON (e.id = c.entity_id)
  165. JOIN (SELECT ap.id, invnumber, transdate, amount, entity_id,
  166. paid, curr, 1 as invoice_class,
  167. entity_credit_account, on_hold, v.batch_id
  168. FROM ap
  169. LEFT JOIN (select * from voucher where batch_class = 1) v
  170. ON (ap.id = v.trans_id)
  171. WHERE in_account_class = 1
  172. AND (v.batch_class = 1 or v.batch_id IS NULL)
  173. UNION
  174. SELECT ar.id, invnumber, transdate, amount, entity_id,
  175. paid, curr, 2 as invoice_class,
  176. entity_credit_account, on_hold, v.batch_id
  177. FROM ar
  178. LEFT JOIN (select * from voucher where batch_class = 2) v
  179. ON (ar.id = v.trans_id)
  180. WHERE in_account_class = 2
  181. AND (v.batch_class = 2 or v.batch_id IS NULL)
  182. ORDER BY transdate
  183. ) a ON (a.entity_credit_account = c.id)
  184. JOIN transactions t ON (a.id = t.id)
  185. JOIN (SELECT trans_id,
  186. sum(CASE WHEN in_account_class = 1 THEN amount
  187. WHEN in_account_class = 2
  188. THEN amount * -1
  189. END) AS due
  190. FROM acc_trans
  191. JOIN chart ON (chart.id = acc_trans.chart_id)
  192. WHERE ((chart.link = 'AP' AND in_account_class = 1)
  193. OR (chart.link = 'AR' AND in_account_class = 2))
  194. GROUP BY trans_id) p ON (a.id = p.trans_id)
  195. WHERE a.batch_id = in_batch_id
  196. OR (a.invoice_class = in_account_class
  197. AND c.business_id =
  198. coalesce(in_business_id, c.business_id)
  199. AND ((a.transdate >= COALESCE(in_date_from, a.transdate)
  200. AND a.transdate <= COALESCE(in_date_to, a.transdate)))
  201. AND c.entity_class = in_account_class
  202. AND a.curr = in_currency
  203. AND a.entity_credit_account = c.id
  204. AND p.due <> 0
  205. AND a.amount <> a.paid
  206. AND NOT a.on_hold
  207. AND (in_meta_number IS NULL
  208. OR in_meta_number = c.meta_number)
  209. AND NOT (t.locked_by IS NOT NULL AND t.locked_by IN
  210. (select "session_id" FROM "session"
  211. WHERE users_id IN
  212. (select id from users
  213. where username <> SESSION_USER)))
  214. AND EXISTS (select trans_id FROM acc_trans
  215. WHERE trans_id = a.id AND
  216. chart_id = (SELECT id frOM chart
  217. WHERE accno
  218. = in_ar_ap_accno)
  219. ))
  220. GROUP BY c.id, e.name, c.meta_number, c.threshold
  221. HAVING sum(p.due) > c.threshold
  222. OR sum(case when a.batch_id = in_batch_id then 1
  223. else 0 END) > 0
  224. LOOP
  225. RETURN NEXT payment_item;
  226. END LOOP;
  227. END;
  228. $$ LANGUAGE plpgsql;
  229. COMMENT ON FUNCTION payment_get_all_contact_invoices
  230. (in_account_class int, in_business_type int, in_currency char(3),
  231. in_date_from date, in_date_to date, in_batch_id int,
  232. in_ar_ap_accno text) IS
  233. $$
  234. This function takes the following arguments (all prefaced with in_ in the db):
  235. account_class: 1 for vendor, 2 for customer
  236. business_type: integer of business.id.
  237. currency: char(3) of currency (for example 'USD')
  238. date_from, date_to: These dates are inclusive.
  239. 1;3B
  240. batch_id: For payment batches, where fees are concerned.
  241. ar_ap_accno: The AR/AP account number.
  242. This then returns a set of contact information with a 2 dimensional array
  243. cnsisting of outstanding invoices.
  244. $$;
  245. CREATE OR REPLACE FUNCTION payment_bulk_queue
  246. (in_transactions numeric[], in_batch_id int, in_source text, in_total numeric,
  247. in_ar_ap_accno text, in_cash_accno text,
  248. in_payment_date date, in_account_class int)
  249. returns int as
  250. $$
  251. BEGIN
  252. INSERT INTO payments_queue
  253. (transactions, batch_id, source, total, ar_ap_accno, cash_accno,
  254. payment_date, account_class)
  255. VALUES
  256. (in_transactions, in_batch_id, in_source, in_total, in_ar_ap_accno,
  257. in_cash_accno, in_payment_date, in_account_class);
  258. RETURN array_upper(in_transactions, 1) -
  259. array_lower(in_transactions, 1);
  260. END;
  261. $$ LANGUAGE PLPGSQL;
  262. CREATE OR REPLACE FUNCTION job__process_payment(in_job_id int)
  263. RETURNS bool AS $$
  264. DECLARE
  265. queue_record RECORD;
  266. t_auth_name text;
  267. t_counter int;
  268. BEGIN
  269. -- TODO: Move the set session authorization into a utility function
  270. SELECT entered_by INTO t_auth_name FROM pending_job
  271. WHERE id = in_job_id;
  272. EXECUTE 'SET SESSION AUTHORIZATION ' || quote_ident(t_auth_name);
  273. t_counter := 0;
  274. FOR queue_record IN
  275. SELECT *
  276. FROM payments_queue WHERE job_id = in_job_id
  277. LOOP
  278. PERFORM payment_bulk_post
  279. (queue_record.transactions, queue_record.batch_id,
  280. queue_record.source, queue_record.total,
  281. queue_record.ar_ap_accno,
  282. queue_record.cash_accno,
  283. queue_record.payment_date,
  284. queue_record.account_class);
  285. t_counter := t_counter + 1;
  286. RAISE NOTICE 'Processed record %, starting transaction %',
  287. t_counter, queue_record.transactions[1][1];
  288. END LOOP;
  289. DELETE FROM payments_queue WHERE job_id = in_job_id;
  290. UPDATE pending_job
  291. SET completed_at = timeofday()::timestamp,
  292. success = true
  293. WHERE id = in_job_id;
  294. RETURN TRUE;
  295. END;
  296. $$ language plpgsql;
  297. CREATE OR REPLACE FUNCTION job__create(in_batch_class int, in_batch_id int)
  298. RETURNS int AS
  299. $$
  300. BEGIN
  301. INSERT INTO pending_job (batch_class, batch_id)
  302. VALUES (coalesce(in_batch_class, 3), in_batch_id);
  303. RETURN currval('pending_job_id_seq');
  304. END;
  305. $$ LANGUAGE PLPGSQL;
  306. CREATE TYPE job__status AS (
  307. completed int, -- 1 for completed, 0 for no
  308. success int, -- 1 for success, 0 for no
  309. completed_at timestamp,
  310. error_condition text -- error if not successful
  311. );
  312. CREATE OR REPLACE FUNCTION job__status(in_job_id int) RETURNS job__status AS
  313. $$
  314. DECLARE out_row job__status;
  315. BEGIN
  316. SELECT (completed_at IS NULL)::INT, success::int, completed_at,
  317. error_condition
  318. INTO out_row
  319. FROM pending_job
  320. WHERE id = in_job_id;
  321. RETURN out_row;
  322. END;
  323. $$ language plpgsql;
  324. CREATE OR REPLACE FUNCTION payment_bulk_post
  325. (in_transactions numeric[], in_batch_id int, in_source text, in_total numeric,
  326. in_ar_ap_accno text, in_cash_accno text,
  327. in_payment_date date, in_account_class int)
  328. RETURNS int AS
  329. $$
  330. DECLARE
  331. out_count int;
  332. t_voucher_id int;
  333. t_trans_id int;
  334. t_amount numeric;
  335. t_ar_ap_id int;
  336. t_cash_id int;
  337. BEGIN
  338. IF in_batch_id IS NULL THEN
  339. -- t_voucher_id := NULL;
  340. RAISE EXCEPTION 'Bulk Post Must be from Batch!';
  341. ELSE
  342. INSERT INTO voucher (batch_id, batch_class, trans_id)
  343. values (in_batch_id, 3, in_transactions[1][1]);
  344. t_voucher_id := currval('voucher_id_seq');
  345. END IF;
  346. select id into t_ar_ap_id from chart where accno = in_ar_ap_accno;
  347. select id into t_cash_id from chart where accno = in_cash_accno;
  348. FOR out_count IN
  349. array_lower(in_transactions, 1) ..
  350. array_upper(in_transactions, 1)
  351. LOOP
  352. INSERT INTO acc_trans
  353. (trans_id, chart_id, amount, approved, voucher_id,
  354. transdate, source)
  355. VALUES
  356. (in_transactions[out_count][1],
  357. case when in_account_class = 1 THEN t_cash_id
  358. WHEN in_account_class = 2 THEN t_ar_ap_id
  359. ELSE -1 END,
  360. in_transactions[out_count][2],
  361. CASE WHEN t_voucher_id IS NULL THEN true
  362. ELSE false END,
  363. t_voucher_id, in_payment_date, in_source);
  364. INSERT INTO acc_trans
  365. (trans_id, chart_id, amount, approved, voucher_id,
  366. transdate, source)
  367. VALUES
  368. (in_transactions[out_count][1],
  369. case when in_account_class = 1 THEN t_ar_ap_id
  370. WHEN in_account_class = 2 THEN t_cash_id
  371. ELSE -1 END,
  372. in_transactions[out_count][2]* -1,
  373. CASE WHEN t_voucher_id IS NULL THEN true
  374. ELSE false END,
  375. t_voucher_id, in_payment_date, in_source);
  376. UPDATE ap
  377. set paid = paid +in_transactions[out_count][2]
  378. where id =in_transactions[out_count][1];
  379. END LOOP;
  380. return out_count;
  381. END;
  382. $$ language plpgsql;
  383. COMMENT ON FUNCTION payment_bulk_post
  384. (in_transactions numeric[], in_batch_id int, in_source text, in_total numeric,
  385. in_ar_ap_accno text, in_cash_accno text,
  386. in_payment_date date, in_account_class int)
  387. IS
  388. $$ Note that in_transactions is a two-dimensional numeric array. Of each
  389. sub-array, the first element is the (integer) transaction id, and the second
  390. is the amount for that transaction. If the total of the amounts do not add up
  391. to in_total, then an error is generated. $$;
  392. CREATE OR REPLACE FUNCTION payment_post
  393. (in_payment_date date,
  394. in_account_class int,
  395. in_person_id int,
  396. in_currency char(3),
  397. in_notes text,
  398. in_department int,
  399. in_gl_description text,
  400. in_cash_accno int[],
  401. in_cash_amount int[],
  402. in_cash_approved bool[],
  403. in_cash_source text[],
  404. in_accno int[],
  405. in_amount int[],
  406. in_approved bool[],
  407. in_source text[],
  408. in_transaction_id int[],
  409. in_type int[],
  410. in_approved bool)
  411. RETURNS INT AS
  412. $$
  413. DECLARE var_payment_id int;
  414. DECLARE var_gl_id int;
  415. DECLARE var_entry_id int[];
  416. DECLARE out_count int;
  417. BEGIN
  418. -- FIRST WE HAVE TO INSERT THE PAYMENT
  419. -- THE ID IS GENERATED BY payment_id_seq
  420. --
  421. INSERT INTO payment (reference, payment_class, payment_date,
  422. person_id, currency, notes, department_id)
  423. VALUES ((CASE WHEN in_account_class = 1 THEN
  424. setting_increment('rcptnumber') -- I FOUND THIS ON sql/modules/Settings.sql
  425. ELSE -- and it is very usefull
  426. setting_increment('paynumber')
  427. END),
  428. in_account_class, in_payment_date, in_person_id,
  429. in_currency, in_notes, in_department);
  430. SELECT currval('payment_id_seq') INTO var_payment_id; -- WE'LL NEED THIS VALUE TO USE payment_link table
  431. -- SECOND WE HAVE TO MAKE THE GL TO HOLD THE TRANSACTIONS
  432. -- THE ID IS GENERATED BY gl_id_seq
  433. --
  434. INSERT INTO gl (reference, description, transdate,
  435. person_id, notes, approved, department_id)
  436. VALUES (setting_increment('glnumber'),
  437. in_gl_description, in_payment_date, in_person_id,
  438. in_notes, in_department, coalesce(in_approved, true));
  439. SELECT currval('id') INTO var_gl_id; -- WE'LL NEED THIS VALUE TO JOIN WITH PAYMENT
  440. -- NOW COMES THE HEAVY PART, STORING ALL THE POSSIBLE TRANSACTIONS...
  441. --
  442. -- FIRST WE SHOULD INSERT THE CASH ACCOUNTS
  443. --
  444. -- WE SHOULD HAVE THE DATA STORED AS (ACCNO, AMOUNT), SO
  445. FOR out_count IN
  446. array_lower(in_cash_accno, 1) ..
  447. array_upper(in_cash_accno, 1)
  448. LOOP
  449. INSERT INTO acc_trans (chart_id, amount,
  450. trans_id, transdate, approved, source)
  451. VALUES ((SELECT id FROM chart WHERE accno = in_cash_accno[out_count]),
  452. CASE WHEN in_account_class = 2 THEN in_cash_amount[out_count] * -1
  453. ELSE in_cash_amount[out_count]
  454. END,
  455. var_gl_id, in_payment_date, coalesce(in_cash_approved[1], true),
  456. in_cash_source[out_count]);
  457. --SELECT currval('acc_trans_entry_id_seq') INTO var_entry_id[out_count];--WE'LL NEED THIS FOR THE PAYMENT_LINK
  458. END LOOP;
  459. --
  460. -- NOW LETS HANDLE THE AR/AP/OVERPAYMENT ACCOUNT
  461. --
  462. FOR var_count IN
  463. array_lower(in_accno, 1) ..
  464. array_upper(in_accno, 1)
  465. LOOP
  466. INSERT INTO acc_trans (chart_id, amount,
  467. trans_id, transdate, approved, source)
  468. VALUES ((SELECT id FROM chart WHERE accno = in_accno[out_count]),
  469. CASE WHEN in_account_class = 2 THEN in_amount[out_count] * -1
  470. ELSE in_amount[out_count]
  471. END,
  472. var_gl_id, in_payment_date, coalesce(in_approved[1], true),
  473. in_source[out_count]);
  474. --
  475. -- WE WILL INSERT THE LINK INTO PAYMENT_LINKS NOW
  476. --
  477. INSERT INTO payment_links
  478. VALUES (var_payment_id, currval(acc_trans_entry_id_seq),
  479. in_transaction_id[out_count], in_type[var_count]);
  480. END LOOP;
  481. return 0;
  482. END;
  483. $$ LANGUAGE PLPGSQL;
  484. -- I HAVE TO MAKE A COMMENT ON THIS FUNCTION
  485. -- Move this to the projects module when we start on that. CT
  486. CREATE OR REPLACE FUNCTION project_list_open(in_date date)
  487. RETURNS SETOF project AS
  488. $$
  489. DECLARE out_project project%ROWTYPE;
  490. BEGIN
  491. FOR out_project IN
  492. SELECT * from project
  493. WHERE startdate <= in_date AND enddate >= in_date
  494. AND completed = 0
  495. LOOP
  496. return next out_project;
  497. END LOOP;
  498. END;
  499. $$ language plpgsql;
  500. comment on function project_list_open(in_date date) is
  501. $$ This function returns all projects that were open as on the date provided as
  502. the argument.$$;
  503. -- Move this to the projects module when we start on that. CT
  504. CREATE OR REPLACE FUNCTION department_list(in_role char)
  505. RETURNS SETOF department AS
  506. $$
  507. DECLARE out_department department%ROWTYPE;
  508. BEGIN
  509. FOR out_department IN
  510. SELECT * from department
  511. WHERE role = coalesce(in_role, role)
  512. LOOP
  513. return next out_department;
  514. END LOOP;
  515. END;
  516. $$ language plpgsql;
  517. -- Move this into another module.
  518. comment on function department_list(in_role char) is
  519. $$ This function returns all department that match the role provided as
  520. the argument.$$;
  521. CREATE OR REPLACE FUNCTION payments_get_open_currencies(in_account_class int)
  522. RETURNS SETOF char(3) AS
  523. $$
  524. DECLARE resultrow record;
  525. BEGIN
  526. FOR resultrow IN
  527. SELECT curr AS curr FROM ar
  528. WHERE amount <> paid
  529. OR paid IS NULL
  530. AND in_account_class=2
  531. UNION
  532. SELECT curr FROM ap
  533. WHERE amount <> paid
  534. OR paid IS NULL
  535. AND in_account_class=1
  536. ORDER BY curr
  537. LOOP
  538. return next resultrow.curr;
  539. END LOOP;
  540. END;
  541. $$ language plpgsql;
  542. CREATE OR REPLACE FUNCTION currency_get_exchangerate(in_currency char(3), in_date date, in_account_class int)
  543. RETURNS NUMERIC AS
  544. $$
  545. DECLARE
  546. out_exrate exchangerate.buy%TYPE;
  547. BEGIN
  548. IF in_account_class = 1 THEN
  549. SELECT INTO out_exrate buy
  550. FROM exchangerate
  551. WHERE transdate = in_date AND curr = in_currency;
  552. ELSE
  553. SELECT INTO out_exrate sell
  554. FROM exchangerate
  555. WHERE transdate = in_date AND curr = in_currency;
  556. END IF;
  557. RETURN out_exrate;
  558. END;
  559. $$ language plpgsql;
  560. COMMENT ON FUNCTION currency_get_exchangerate(in_currency char(3), in_date date, in_account_class int) IS
  561. $$ This function return the exchange rate of a given currency, date and exchange rate class (buy or sell). $$;
  562. --
  563. -- payment_location_result has the same arch as location_result, except for one field
  564. -- This should be unified on the API when we get things working - David Mora
  565. --
  566. CREATE TYPE payment_location_result AS (
  567. id int,
  568. line_one text,
  569. line_two text,
  570. line_three text,
  571. city text,
  572. state text,
  573. mail_code text,
  574. country text,
  575. class text
  576. );
  577. --
  578. -- payment_get_vc_info has the same arch as company__list_locations, except for the filtering capabilities
  579. -- This should be unified on the API when we get things working - David Mora
  580. --
  581. CREATE OR REPLACE FUNCTION payment_get_vc_info(in_entity_id int, in_location_class_id int)
  582. RETURNS SETOF payment_location_result AS
  583. $$
  584. DECLARE out_row RECORD;
  585. BEGIN
  586. FOR out_row IN
  587. SELECT l.id, l.line_one, l.line_two, l.line_three, l.city,
  588. l.state, l.mail_code, c.name, lc.class
  589. FROM location l
  590. JOIN company_to_location ctl ON (ctl.location_id = l.id)
  591. JOIN company cp ON (ctl.company_id = cp.id)
  592. JOIN location_class lc ON (ctl.location_class = lc.id)
  593. JOIN country c ON (c.id = l.country_id)
  594. WHERE cp.entity_id = in_entity_id AND
  595. lc.id = in_location_class_id
  596. ORDER BY lc.id, l.id, c.name
  597. LOOP
  598. RETURN NEXT out_row;
  599. END LOOP;
  600. END;
  601. $$ LANGUAGE PLPGSQL;
  602. COMMENT ON FUNCTION payment_get_vc_info(in_entity_id int, in_location_class_id int) IS
  603. $$ This function returns vendor or customer info $$;
  604. CREATE TYPE payment_record AS (
  605. amount numeric,
  606. meta_number text,
  607. credit_id int,
  608. company_paid text,
  609. accounts text[],
  610. source text,
  611. date_paid date
  612. );
  613. CREATE OR REPLACE FUNCTION payment__search
  614. (in_source text, in_date_from date, in_date_to date, in_credit_id int,
  615. in_cash_accno text, in_account_class int)
  616. RETURNS SETOF payment_record AS
  617. $$
  618. DECLARE
  619. out_row payment_record;
  620. BEGIN
  621. FOR out_row IN
  622. select sum(CASE WHEN c.entity_class = 1 then a.amount
  623. ELSE a.amount * -1 END), c.meta_number,
  624. c.id, co.legal_name,
  625. compound_array(ARRAY[ARRAY[ch.id::text, ch.accno,
  626. ch.description]]), a.source, a.transdate
  627. FROM entity_credit_account c
  628. JOIN ( select entity_credit_account, id
  629. FROM ar WHERE in_account_class = 2
  630. UNION
  631. SELECT entity_credit_account, id
  632. FROM ap WHERE in_account_class = 1
  633. ) arap ON (arap.entity_credit_account = c.id)
  634. JOIN acc_trans a ON (arap.id = a.trans_id)
  635. JOIN chart ch ON (ch.id = a.chart_id)
  636. JOIN company co ON (c.entity_id = co.entity_id)
  637. WHERE (ch.accno = in_cash_accno)
  638. AND (c.id = in_credit_id OR in_credit_id IS NULL)
  639. AND (a.transdate >= in_date_from
  640. OR in_date_from IS NULL)
  641. AND (a.transdate <= in_date_to OR in_date_to IS NULL)
  642. AND (source = in_source OR in_source IS NULL)
  643. GROUP BY c.meta_number, c.id, co.legal_name, a.transdate,
  644. a.source
  645. ORDER BY a.transdate, c.meta_number, a.source
  646. LOOP
  647. RETURN NEXT out_row;
  648. END LOOP;
  649. END;
  650. $$ language plpgsql;
  651. CREATE OR REPLACE FUNCTION payment__reverse
  652. (in_source text, in_date_paid date, in_credit_id int, in_cash_accno text,
  653. in_date_reversed date, in_account_class int, in_batch_id int)
  654. RETURNS INT
  655. AS $$
  656. DECLARE
  657. pay_row record;
  658. t_voucher_id int;
  659. t_voucher_inserted bool;
  660. BEGIN
  661. IF in_batch_id IS NOT NULL THEN
  662. t_voucher_id := nextval('voucher_id_seq');
  663. t_voucher_inserted := FALSE;
  664. END IF;
  665. FOR pay_row IN
  666. SELECT a.*, c.ar_ap_account_id
  667. FROM acc_trans a
  668. JOIN (select id, entity_credit_account
  669. FROM ar WHERE in_account_class = 2
  670. UNION
  671. SELECT id, entity_credit_account
  672. FROM ap WHERE in_account_class = 1
  673. ) arap ON (a.trans_id = arap.id)
  674. JOIN entity_credit_account c
  675. ON (arap.entity_credit_account = c.id)
  676. JOIN chart ch ON (a.chart_id = ch.id)
  677. WHERE coalesce(source, '') = coalesce(in_source, '')
  678. AND transdate = in_date_paid
  679. AND in_credit_id = c.id
  680. AND in_cash_accno = ch.accno
  681. LOOP
  682. IF in_batch_id IS NOT NULL
  683. AND t_voucher_inserted IS NOT TRUE
  684. THEN
  685. INSERT INTO voucher
  686. (id, trans_id, batch_id, batch_class)
  687. VALUES
  688. (t_voucher_id, pay_row.trans_id, in_batch_id,
  689. CASE WHEN in_account_class = 1 THEN 4
  690. WHEN in_account_class = 2 THEN 7
  691. END);
  692. t_voucher_inserted := TRUE;
  693. END IF;
  694. INSERT INTO acc_trans
  695. (trans_id, chart_id, amount, transdate, source, memo, approved,
  696. voucher_id)
  697. VALUES
  698. (pay_row.trans_id, pay_row.chart_id, pay_row.amount * -1,
  699. in_date_reversed, in_source, 'Reversing ' ||
  700. COALESCE(in_source, ''),
  701. case when in_batch_id is not null then false
  702. else true end, t_voucher_id);
  703. INSERT INTO acc_trans
  704. (trans_id, chart_id, amount, transdate, source, memo, approved,
  705. voucher_id)
  706. VALUES
  707. (pay_row.trans_id, pay_row.ar_ap_account_id, pay_row.amount,
  708. in_date_reversed, in_source, 'Reversing ' ||
  709. COALESCE(in_source, ''),
  710. case when in_batch_id is not null then false
  711. else true end, t_voucher_id);
  712. IF in_account_class = 1 THEN
  713. UPDATE ap SET paid = amount -
  714. (SELECT sum(a.amount)
  715. FROM acc_trans a
  716. JOIN chart c ON (a.chart_id = c.id)
  717. WHERE c.link = 'AP'
  718. AND trans_id = pay_row.trans_id
  719. )
  720. WHERE id = pay_row.trans_id;
  721. ELSIF in_account_class = 2 THEN
  722. update ar SET paid = amount -
  723. (SELECT sum(a.amount)
  724. FROM acc_trans a
  725. JOIN chart c ON (a.chart_id = c.id)
  726. WHERE c.link = 'AR'
  727. AND trans_id = pay_row.trans_id
  728. ) * -1
  729. WHERE id = pay_row.trans_id;
  730. ELSE
  731. RAISE EXCEPTION 'Unknown account class for payments %',
  732. in_account_class;
  733. END IF;
  734. END LOOP;
  735. RETURN 1;
  736. END;
  737. $$ LANGUAGE PLPGSQL;