| Type | Name | Status | PK | FK | Columns | Index | Script | Diff Script |
|---|---|---|---|---|---|---|---|---|
| Table | payroll_workflow | Missing in Target |
|
|||||
| Table | departments | Match | ||||||
| Table | companies | Match | ||||||
| Table | leave_records | Missing in Target |
|
|||||
| Table | leave_statuses | Match | ||||||
| Table | leave_workflow | Match | ||||||
| Table | payment_headers | Mismatch |
Source Script
Target Script
1
CREATE TABLE "payment_headers" ("id" uuid NOT NULL, "company_id" uuid NOT NULL, "paid_date" timestamp without time zone NOT NULL, "paid_amount" numeric(,) NOT NULL, "tds_amount" numeric(,), "advance_amount" numeric(,), "description" text NOT NULL, "debit_account_id" uuid NOT NULL, "credit_account_id" uuid NOT NULL, "mode_of_payment" text NOT NULL, "reference" text NOT NULL, "created_on_utc" timestamp without time zone NOT NULL, "created_by" uuid NOT NULL, "modified_on_utc" timestamp without time zone, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "payment_number" text DEFAULT ''::text NOT NULL, "is_posted" boolean DEFAULT false NOT NULL, "transaction_id" bigint, "modified_by" uuid DEFAULT '00000000-0000-0000-0000-000000000000'::uuid NOT NULL, PRIMARY KEY ("id"));
1
CREATE TABLE "payment_headers" ("id" uuid NOT NULL, "company_id" uuid NOT NULL, "paid_date" timestamp without time zone NOT NULL, "paid_amount" numeric(,) NOT NULL, "tds_amount" numeric(,), "advance_amount" numeric(,), "description" text NOT NULL, "debit_account_id" uuid NOT NULL, "credit_account_id" uuid NOT NULL, "mode_of_payment" text NOT NULL, "reference" text NOT NULL, "created_on_utc" timestamp without time zone NOT NULL, "created_by" uuid NOT NULL, "modified_on_utc" timestamp without time zone, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "payment_number" text DEFAULT ''::text NOT NULL, PRIMARY KEY ("id"));
|
|||||
| Table | payments | Missing in Target |
|
|||||
| Table | workdays | Mismatch |
|
|||||
| Table | employee_leave_balances | Missing in Target |
|
|||||
| Table | banks | Match | ||||||
| Table | __EFMigrationsHistory | Match | ||||||
| Table | designations | Match | ||||||
| Table | benefits | Match | ||||||
| Table | addresses | Mismatch |
|
|||||
| Table | advance_payments | Match | ||||||
| Table | bank_accounts | Mismatch |
Source Script
Target Script
1
CREATE TABLE "bank_accounts" ("id" uuid NOT NULL, "bank_id" integer NOT NULL, "branch_name" text NOT NULL, "account_number" text NOT NULL, "ifsc" text NOT NULL, "rx_fund_account_id" text, "rp_linked_account_id" text, "created_on_utc" timestamp without time zone NOT NULL, "modified_on_utc" timestamp without time zone, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "created_by" uuid NOT NULL, "modified_by" uuid, PRIMARY KEY ("id"));
1
CREATE TABLE "bank_accounts" ("id" uuid NOT NULL, "bank_id" integer NOT NULL, "branch_name" text NOT NULL, "account_number" text NOT NULL, "ifsc" text NOT NULL, "created_on_utc" timestamp without time zone NOT NULL, "modified_on_utc" timestamp without time zone, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "created_by" uuid NOT NULL, "modified_by" uuid, "rx_fund_account_id" text, "rp_linked_account_id" text, PRIMARY KEY ("id"));
|
|||||
| Table | cities | Match | ||||||
| Table | countries | Match | ||||||
| Table | component_types | Match | ||||||
| Table | deductions | Match | ||||||
| Table | attendance | Match | ||||||
| Table | leave_applications | Mismatch |
|
|||||
| Table | loans | Mismatch |
Source Script
Target Script
1
CREATE TABLE "loans" ("id" uuid NOT NULL, "employee_id" uuid NOT NULL, "loan_amount" numeric(,) NOT NULL, "interest_rate" numeric(,) NOT NULL, "repayment_start_date" date NOT NULL, "repayment_end_date" date, "monthly_installment" numeric(,) NOT NULL, "outstanding_balance" numeric(,) NOT NULL, "description" text NOT NULL, "status_id" integer NOT NULL, "created_on_utc" timestamp without time zone NOT NULL, "modified_on_utc" timestamp without time zone, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "created_by" uuid NOT NULL, "modified_by" uuid, PRIMARY KEY ("id"));
1
CREATE TABLE "loans" ("id" uuid NOT NULL, "employee_id" uuid NOT NULL, "loan_amount" numeric(15,2) NOT NULL, "interest_rate" numeric(5,2) NOT NULL, "repayment_start_date" date NOT NULL, "repayment_end_date" date, "monthly_installment" numeric(15,2) NOT NULL, "outstanding_balance" numeric(15,2) NOT NULL, "description" text NOT NULL, "status_id" integer NOT NULL, "created_on_utc" timestamp without time zone NOT NULL, "modified_on_utc" timestamp without time zone, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "created_by" uuid NOT NULL, "modified_by" uuid, PRIMARY KEY ("id"));
|
|||||
| Table | employees | Match | ||||||
| Table | payroll_transaction_header_ids | Match | ||||||
| Table | organizations | Match | ||||||
| Table | payroll_transaction_details | Match | ||||||
| Table | payment_header_ids | Match | ||||||
| Table | timesheet_statuses | Match | ||||||
| Table | sub_projects | Match | ||||||
| Table | timesheet_lines | Match | ||||||
| Table | upis | Match | ||||||
| Table | users | Mismatch |
|
|||||
| Table | employee_bank_accounts | Match | ||||||
| Table | employee_upis | Match | ||||||
| Table | holidays | Match | ||||||
| Table | leave_types | Match | ||||||
| Table | payment_details | Match | ||||||
| Table | payroll_configuration_headers | Match | ||||||
| Table | statuses | Match | ||||||
| Table | timesheet_hours | Match | ||||||
| Table | leave_entitlements | Mismatch |
|
|||||
| Table | payroll_transaction_headers | Mismatch |
Source Script
Target Script
1
CREATE TABLE "payroll_transaction_headers" ("id" uuid NOT NULL, "employee_id" uuid NOT NULL, "pay_period_start" date NOT NULL, "pay_period_end" date NOT NULL, "basic_salary" numeric(,) NOT NULL, "allowances" numeric(,), "deductions" numeric(,), "net_pay" numeric(,) NOT NULL, "payment_date" date, "payment_method_id" integer NOT NULL, "created_on_utc" timestamp without time zone NOT NULL, "modified_on_utc" timestamp without time zone, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "created_by" uuid NOT NULL, "modified_by" uuid, "company_id" uuid DEFAULT '00000000-0000-0000-0000-000000000000'::uuid NOT NULL, "status_id" integer DEFAULT 1 NOT NULL, "payroll_number" text DEFAULT ''::text NOT NULL, "is_posted" boolean DEFAULT false NOT NULL, "transaction_id" bigint, PRIMARY KEY ("id"));
1
CREATE TABLE "payroll_transaction_headers" ("id" uuid NOT NULL, "employee_id" uuid NOT NULL, "pay_period_start" date NOT NULL, "pay_period_end" date NOT NULL, "basic_salary" numeric(15,2) NOT NULL, "allowances" numeric(15,2), "deductions" numeric(15,2), "net_pay" numeric(15,2) NOT NULL, "payment_date" date, "payment_method_id" integer NOT NULL, "created_on_utc" timestamp without time zone NOT NULL, "modified_on_utc" timestamp without time zone, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "created_by" uuid NOT NULL, "modified_by" uuid, "company_id" uuid DEFAULT '00000000-0000-0000-0000-000000000000'::uuid NOT NULL, "status_id" integer DEFAULT 1 NOT NULL, "payroll_number" text DEFAULT ''::text NOT NULL, PRIMARY KEY ("id"));
|
|||||
| Table | payroll_components | Missing in Target |
|
|||||
| Table | projects | Mismatch |
|
|||||
| Table | tasks | Mismatch |
Source Script
Target Script
1
CREATE TABLE "tasks" ("project_id" uuid NOT NULL, "sub_project_id" integer NOT NULL, "parent_task_id" integer, "name" text NOT NULL, "planned_hours" integer NOT NULL, "created_on_utc" timestamp without time zone NOT NULL, "modified_on_utc" timestamp without time zone, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "created_by" uuid NOT NULL, "modified_by" uuid, "id" integer NOT NULL, "company_id" uuid DEFAULT '00000000-0000-0000-0000-000000000000'::uuid NOT NULL, PRIMARY KEY ("id"));
1
CREATE TABLE "tasks" ("id" integer NOT NULL, "project_id" uuid NOT NULL, "sub_project_id" integer NOT NULL, "parent_task_id" integer, "name" text NOT NULL, "planned_hours" integer NOT NULL, "created_on_utc" timestamp without time zone NOT NULL, "modified_on_utc" timestamp without time zone, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "created_by" uuid NOT NULL, "modified_by" uuid, "company_id" uuid DEFAULT '00000000-0000-0000-0000-000000000000'::uuid NOT NULL, PRIMARY KEY ("id"));
|
|||||
| Table | timesheets | Mismatch |
|
|||||
| Table | payroll_configuration_details | Mismatch |
|
|||||
| Table | states | Mismatch |
|
|||||
| Function | delete_payroll_configuration | Match | ||||||
| Function | delete_payroll_data | Match | ||||||
| Function | get_all_employee_leave_report | Match | ||||||
| Function | get_leave_records_with_balance | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.get_leave_records_with_balance(p_company_id uuid, p_fin_year_id integer)
2
RETURNS TABLE(leave_record_id uuid, employee_id uuid, employee_name text, manager_id uuid, leave_type_id integer, leave_type_name text, start_date date, end_date date, reason text, leave_status_id integer, leave_status text, total_leaves integer, used_leaves integer, remaining_leaves integer)
3
LANGUAGE plpgsql
4
AS $function$
5
DECLARE
6
financial_year_start date := make_date(p_fin_year_id, 4, 1);
7
financial_year_end date := make_date(p_fin_year_id + 1, 3, 31);
8
BEGIN
9
RETURN QUERY
10
SELECT
11
lr.id AS leave_record_id,
12
lr.employee_id AS employee_id,
13
CONCAT(e.first_name, ' ', e.last_name) AS employee_name,
14
e.manager_id, -- ⬅️ Select manager_id
15
lt.id AS leave_type_id,
16
lt.name::TEXT AS leave_type_name,
17
lr.start_date,
18
lr.end_date,
19
lr.reason,
20
ls.id AS leave_status_id,
21
ls.name::TEXT AS leave_status,
22
COALESCE(elb.total_leaves, 0) AS total_leaves,
23
COALESCE(elb.used_leaves, 0) AS used_leaves,
24
COALESCE(elb.total_leaves - elb.used_leaves, 0) AS remaining_leaves
25
FROM leave_records lr
26
INNER JOIN employees e
27
ON lr.employee_id = e.id
28
INNER JOIN leave_statuses ls
29
ON lr.leave_status_id = ls.id
30
INNER JOIN leave_types lt
31
ON lr.leave_type_id = lt.id
32
LEFT JOIN (
33
SELECT
34
elb_inner.employee_id,
35
elb_inner.leave_type_id,
36
elb_inner.total_leaves,
37
elb_inner.used_leaves
38
FROM (
39
SELECT
40
elb.employee_id,
41
elb.leave_type_id,
42
elb.total_leaves,
43
elb.used_leaves,
44
ROW_NUMBER() OVER (
45
PARTITION BY elb.employee_id, elb.leave_type_id
46
ORDER BY elb.id DESC
47
) AS rn
48
FROM employee_leave_balances elb
49
) elb_inner
50
WHERE elb_inner.rn = 1
51
) elb
52
ON lr.employee_id = elb.employee_id
53
AND lr.leave_type_id = elb.leave_type_id
54
WHERE lr.company_id = p_company_id
55
AND (lr.is_deleted IS NULL OR lr.is_deleted = FALSE)
56
AND lr.start_date <= financial_year_end
57
AND lr.end_date >= financial_year_start;
58
END;
59
$function$
|
|||||
| Function | get_payroll | Match | ||||||
| Function | pg_get_tabledef | Match | ||||||
| Function | get_all_employee_leave_balances | Match | ||||||
| Function | get_states_by_country | Match | ||||||
| Function | get_all_leave_applications | Match | ||||||
| Function | get_leave_records_with_balance_also_check_managerid | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.get_leave_records_with_balance_also_check_managerid(p_company_id uuid, p_fin_year_id integer, p_employee_id uuid)
2
RETURNS TABLE(leave_record_id uuid, employee_id uuid, employee_name text, manager_id uuid, leave_type_id integer, leave_type_name text, start_date date, end_date date, reason text, leave_status_id integer, leave_status text, total_leaves integer, used_leaves integer, remaining_leaves integer, created_by uuid, modified_by uuid)
3
LANGUAGE plpgsql
4
AS $function$
5
DECLARE
6
financial_year_start date := make_date(p_fin_year_id, 4, 1);
7
financial_year_end date := make_date(p_fin_year_id + 1, 3, 31);
8
is_manager boolean := false;
9
BEGIN
10
-- Is the given employee a manager in this company?
11
SELECT EXISTS (
12
SELECT 1
13
FROM employees e
14
WHERE e.manager_id = p_employee_id
15
AND e.company_id = p_company_id
16
) INTO is_manager;
17
18
RETURN QUERY
19
SELECT
20
lr.id AS leave_record_id,
21
lr.employee_id AS employee_id,
22
CONCAT(e.first_name, ' ', e.last_name) AS employee_name,
23
e.manager_id,
24
lt.id AS leave_type_id,
25
lt.name::text AS leave_type_name,
26
lr.start_date,
27
lr.end_date,
28
lr.reason,
29
ls.id AS leave_status_id,
30
ls.name::text AS leave_status,
31
COALESCE(elb.total_leaves, 0) AS total_leaves,
32
COALESCE(elb.used_leaves, 0) AS used_leaves,
33
COALESCE(elb.total_leaves - elb.used_leaves, 0) AS remaining_leaves,
34
lr.created_by,
35
lr.modified_by
36
FROM leave_records lr
37
INNER JOIN employees e ON lr.employee_id = e.id
38
INNER JOIN leave_statuses ls ON lr.leave_status_id = ls.id
39
INNER JOIN leave_types lt ON lr.leave_type_id = lt.id
40
LEFT JOIN (
41
SELECT
42
elb_inner.employee_id,
43
elb_inner.leave_type_id,
44
elb_inner.total_leaves,
45
elb_inner.used_leaves
46
FROM (
47
SELECT
48
elb.employee_id,
49
elb.leave_type_id,
50
elb.total_leaves,
51
elb.used_leaves,
52
ROW_NUMBER() OVER (
53
PARTITION BY elb.employee_id, elb.leave_type_id
54
ORDER BY elb.id DESC
55
) AS rn
56
FROM employee_leave_balances elb
57
) elb_inner
58
WHERE elb_inner.rn = 1
59
) elb
60
ON lr.employee_id = elb.employee_id
61
AND lr.leave_type_id = elb.leave_type_id
62
WHERE lr.company_id = p_company_id
63
AND (lr.is_deleted IS NULL OR lr.is_deleted = FALSE)
64
AND lr.start_date <= financial_year_end
65
AND lr.end_date >= financial_year_start
66
AND (
67
is_manager = TRUE
68
OR lr.employee_id = p_employee_id
69
);
70
END;
71
$function$
|
|||||
| Function | get_all_payroll | Mismatch |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.get_all_payroll(p_employee_id uuid, p_department_id integer, p_company_id uuid)
1
CREATE OR REPLACE FUNCTION public.get_all_payroll(p_employee_id uuid, p_department_id integer, p_company_id uuid)
2
RETURNS TABLE(id uuid, employee_name character varying, basic_salary numeric, addition numeric, deduction numeric, net_pay numeric, components jsonb)
2
RETURNS TABLE(id uuid, employee_name character varying, basic_salary numeric, addition numeric, deduction numeric, net_pay numeric, components jsonb)
3
LANGUAGE plpgsql
3
LANGUAGE plpgsql
4
AS $function$
4
AS $function$
5
BEGIN
5
BEGIN
6
RETURN QUERY
6
RETURN QUERY
7
SELECT
7
SELECT
8
e.id AS id,
8
e.id AS employee_id,
9
(e.first_name || ' ' || e.last_name)::VARCHAR AS employee_name,
9
(e.first_name || ' ' || e.last_name)::VARCHAR AS employee_name,
10
10
11
-- Basic salary (monthly) : sum of basic amounts divided by 12
11
-- Basic salary
12
ROUND(COALESCE(SUM(CASE WHEN ct.id = 1 THEN (pcd.amount / 12.0) ELSE 0 END), 0)::NUMERIC, 2) AS basic_salary,
12
ROUND(COALESCE(SUM(CASE
13
WHEN ct.id = 1 THEN pcd.amount / 12
14
ELSE 0 END)::NUMERIC, 0), 2) AS basic_salary,
13
15
14
-- Addition (monthly): DA, HRA, Conveyance, Special, LTA, Medical => divided by 12
16
-- Addition
15
ROUND(COALESCE(SUM(CASE WHEN ct.id IN (2,3,4,5,6,7) THEN (pcd.amount / 12.0) ELSE 0 END), 0)::NUMERIC, 2) AS addition,
17
ROUND(COALESCE(SUM(CASE
18
WHEN ct.id IN (2, 3, 4, 5, 6, 7)
19
THEN pcd.amount / 12 ELSE 0 END)::NUMERIC, 0), 2) AS addition,
16
20
17
-- Deduction (monthly): Professional Tax, PF => divided by 12
21
-- Deduction
18
ROUND(COALESCE(SUM(CASE WHEN ct.id IN (8,9) THEN (pcd.amount / 12.0) ELSE 0 END), 0)::NUMERIC, 2) AS deduction,
22
ROUND(COALESCE(SUM(CASE
23
WHEN ct.id IN (8, 9, 10)
24
THEN pcd.amount / 12 ELSE 0 END)::NUMERIC, 0), 2) AS deduction,
19
25
20
-- Net Pay (monthly) = Basic(monthly) + Addition(monthly) - Deduction(monthly)
26
-- Net Pay (Basic salary + Addition - Deduction)
21
ROUND(
27
ROUND(
22
COALESCE(SUM(CASE WHEN ct.id = 1 THEN (pcd.amount / 12.0) ELSE 0 END), 0)
28
(COALESCE(SUM(CASE WHEN ct.id = 1 THEN pcd.amount / 12 ELSE 0 END), 0)::NUMERIC) +
23
+
29
(COALESCE(SUM(CASE
24
COALESCE(SUM(CASE WHEN ct.id IN (2,3,4,5,6,7) THEN (pcd.amount / 12.0) ELSE 0 END), 0)
30
WHEN ct.id IN (2, 3, 4, 5, 6, 7)
25
-
31
THEN pcd.amount / 12 ELSE 0 END), 0)::NUMERIC) -
26
COALESCE(SUM(CASE WHEN ct.id IN (9) THEN (pcd.amount / 12.0) ELSE 0 END), 0)
32
(COALESCE(SUM(CASE
27
, 2) AS net_pay,
33
WHEN ct.id IN (8, 9, 10)
34
THEN pcd.amount / 12 ELSE 0 END), 0)::NUMERIC), 2) AS net_pay,
28
35
29
-- Components as JSONB with monthly amounts
36
-- Components as JSONB
30
jsonb_agg(
37
jsonb_agg(jsonb_build_object(
31
jsonb_build_object(
32
'componentTypeId', ct.id,
38
'componentTypeId', ct.id,
33
'componentTypeName', ct.name,
39
'amount', ROUND(pcd.amount / 12, 2)
34
'amount', ROUND(COALESCE(pcd.amount, 0) / 12.0, 2)
40
)) AS components
35
) ORDER BY ct.id
36
) AS components
37
41
38
FROM public.employees e
42
FROM
39
LEFT JOIN public.payroll_configuration_headers pch ON e.id = pch.employee_id
43
public.employees e
40
LEFT JOIN public.payroll_configuration_details pcd ON pch.id = pcd.configuration_header_id
44
LEFT JOIN
41
LEFT JOIN public.component_types ct ON pcd.component_type_id = ct.id
45
public.payroll_configuration_headers pch on e.id = pch.employee_id
46
LEFT JOIN
47
public.payroll_configuration_details pcd on pch.id = pcd.configuration_header_id
48
LEFT JOIN
49
public.component_types ct ON pcd.component_type_id = ct.id
42
WHERE
50
WHERE
43
(p_employee_id IS NULL OR e.id = p_employee_id) AND
51
(p_employee_id IS NULL OR e.id = p_employee_id) AND
44
(p_department_id IS NULL OR e.department_id = p_department_id) AND
52
(p_department_id IS NULL OR e.department_id = p_department_id) AND
45
pch.company_id = p_company_id AND
53
pch.company_id = p_company_id AND
46
pch.is_deleted = FALSE AND
54
pch.is_deleted = FALSE AND
47
e.is_deleted = FALSE
55
e.is_deleted = FALSE
48
GROUP BY
56
GROUP BY
49
e.id, pch.employee_id, e.first_name, e.last_name;
57
e.id, pch.employee_id, e.first_name, e.last_name;
50
END;
58
END;
51
$function$
59
$function$
|
|||||
| Function | get_payroll_config | Match | ||||||
| Function | get_employee_leave_summary | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.get_employee_leave_summary(p_company_id uuid, p_fin_year integer)
2
RETURNS TABLE(employee_id uuid, employee_name text, total_leaves integer, used_leave_count integer, pending_leave_count integer, remaining_leave integer)
3
LANGUAGE plpgsql
4
AS $function$
5
BEGIN
6
RETURN QUERY
7
WITH leave_data AS (
8
-- Gathering data related to each employee's leaves and counting leave requests
9
SELECT
10
elb.employee_id,
11
CAST(SUM(elb.total_leaves) AS integer) AS total_leaves, -- Sum of all leave types for the employee, cast to integer
12
CAST(COALESCE(COUNT(CASE WHEN lr.leave_status_id = 2 THEN 1 END), 0) AS integer) AS used_leave_count, -- Count of approved leave requests, cast to integer
13
CAST(COALESCE(COUNT(CASE WHEN lr.leave_status_id = 1 THEN 1 END), 0) AS integer) AS pending_leave_count, -- Count of pending leave requests, cast to integer
14
CAST(SUM(elb.total_leaves) - COALESCE(COUNT(CASE WHEN lr.leave_status_id = 2 THEN 1 END), 0) AS integer) AS remaining_leave -- Remaining leave (total - approved), cast to integer
15
FROM employee_leave_balances elb
16
LEFT JOIN leave_records lr
17
ON lr.employee_id = elb.employee_id
18
AND lr.leave_type_id = elb.leave_type_id
19
AND lr.start_date >= make_date(p_fin_year, 1, 1) -- Start date for the given financial year
20
AND lr.end_date <= make_date(p_fin_year, 12, 31) -- End date for the given financial year
21
WHERE elb.company_id = p_company_id
22
GROUP BY elb.employee_id
23
),
24
employees AS (
25
-- Getting the list of employees in the given company
26
SELECT e.id AS employee_id, CONCAT(e.first_name, ' ', e.last_name) AS employee_name
27
FROM employees e
28
WHERE e.company_id = p_company_id
29
)
30
SELECT
31
e.employee_id,
32
e.employee_name,
33
ld.total_leaves,
34
ld.used_leave_count,
35
ld.pending_leave_count,
36
ld.remaining_leave
37
FROM employees e
38
LEFT JOIN leave_data ld ON e.employee_id = ld.employee_id
39
ORDER BY e.employee_name; -- Sorting by employee name
40
END;
41
$function$
|
|||||
| Function | get_employee_leave_summarytemp | Match | ||||||
| Function | get_all_leave_records | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.get_all_leave_records(p_company_id uuid)
2
RETURNS TABLE(leave_record_id uuid, employee_id uuid, employee_name text, leave_type_id integer, start_date date, end_date date, reason text, leave_status_id integer, leave_status_name text)
3
LANGUAGE plpgsql
4
AS $function$
5
BEGIN
6
RETURN QUERY
7
SELECT
8
lr.id AS leave_record_id,
9
lr.employee_id,
10
e.first_name || ' ' || e.last_name AS employee_name,
11
lr.leave_type_id,
12
lr.start_date,
13
lr.end_date,
14
lr.reason,
15
lr.leave_status_id,
16
ls.name::TEXT AS leave_status_name -- Explicit casting to TEXT
17
FROM leave_records lr
18
JOIN employees e ON lr.employee_id = e.id
19
JOIN leave_statuses ls ON lr.leave_status_id = ls.id
20
WHERE lr.company_id = p_company_id;
21
END;
22
$function$
|
|||||
| Function | grant_full_schema_access | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.grant_full_schema_access(p_schema text, p_user text)
2
RETURNS void
3
LANGUAGE plpgsql
4
AS $function$
5
DECLARE
6
obj RECORD;
7
BEGIN
8
-- Grant on tables
9
FOR obj IN
10
SELECT table_name
11
FROM information_schema.tables
12
WHERE table_schema = p_schema
13
AND table_type = 'BASE TABLE'
14
LOOP
15
EXECUTE format('GRANT SELECT, INSERT, UPDATE, DELETE ON TABLE %I.%I TO %I;', p_schema, obj.table_name, p_user);
16
RAISE NOTICE 'GRANT SELECT, INSERT, UPDATE, DELETE ON TABLE %.% TO %;', p_schema, obj.table_name, p_user;
17
END LOOP;
18
19
-- Grant on sequences (USAGE + SELECT + UPDATE: full coverage)
20
FOR obj IN
21
SELECT c.relname AS sequence_name
22
FROM pg_class c
23
JOIN pg_namespace n ON n.oid = c.relnamespace
24
WHERE c.relkind = 'S'
25
AND n.nspname = p_schema
26
LOOP
27
EXECUTE format('GRANT USAGE, SELECT, UPDATE ON SEQUENCE %I.%I TO %I;', p_schema, obj.sequence_name, p_user);
28
RAISE NOTICE 'GRANT USAGE, SELECT, UPDATE ON SEQUENCE %.% TO %;', p_schema, obj.sequence_name, p_user;
29
END LOOP;
30
31
-- Grant on all functions (handles all argument types)
32
FOR obj IN
33
SELECT
34
p.proname AS function_name,
35
pg_get_function_identity_arguments(p.oid) AS args
36
FROM
37
pg_proc p
38
JOIN pg_namespace n ON p.pronamespace = n.oid
39
WHERE
40
n.nspname = p_schema
41
AND p.prokind = 'f' -- f = function
42
LOOP
43
EXECUTE format(
44
'GRANT EXECUTE ON FUNCTION %I.%I(%s) TO %I;',
45
p_schema, obj.function_name, obj.args, p_user
46
);
47
RAISE NOTICE 'GRANT EXECUTE ON FUNCTION %.%(%) TO %;', p_schema, obj.function_name, obj.args, p_user;
48
END LOOP;
49
50
-- Grant on all procedures (Postgres 11+)
51
FOR obj IN
52
SELECT
53
p.proname AS procedure_name,
54
pg_get_function_identity_arguments(p.oid) AS args
55
FROM
56
pg_proc p
57
JOIN pg_namespace n ON p.pronamespace = n.oid
58
WHERE
59
n.nspname = p_schema
60
AND p.prokind = 'p' -- p = procedure
61
LOOP
62
EXECUTE format(
63
'GRANT EXECUTE ON PROCEDURE %I.%I(%s) TO %I;',
64
p_schema, obj.procedure_name, obj.args, p_user
65
);
66
RAISE NOTICE 'GRANT EXECUTE ON PROCEDURE %.%(%) TO %;', p_schema, obj.procedure_name, obj.args, p_user;
67
END LOOP;
68
69
END;
70
$function$
|
|||||
| Function | update_payment_header_posted_fn | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.update_payment_header_posted_fn(p_payment_header_id uuid, p_transaction_id bigint)
2
RETURNS void
3
LANGUAGE plpgsql
4
AS $function$
5
BEGIN
6
UPDATE payment_headers
7
SET is_posted = true,
8
transaction_id = p_transaction_id,
9
modified_on_utc = now()
10
WHERE id = p_payment_header_id;
11
END;
12
$function$
|
|||||
| Function | get_employee_leave_balance | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.get_employee_leave_balance(p_employee_id uuid DEFAULT NULL::uuid)
2
RETURNS TABLE(id uuid, employee_id uuid, employee_name text, leave_type_id integer, leave_type_name text, total_leaves integer, used_leaves integer, remaining_leaves integer)
3
LANGUAGE plpgsql
4
AS $function$
5
BEGIN
6
RETURN QUERY
7
SELECT
8
gen_random_uuid() AS id, -- âś… Auto-generate UUID for each row
9
elb.employee_id,
10
CONCAT(e.first_name, ' ', e.last_name) AS employee_name,
11
elb.leave_type_id,
12
lt.name::TEXT AS leave_type_name,
13
COALESCE(elb.total_leaves, 0) AS total_leaves,
14
COALESCE(elb.used_leaves, 0) AS used_leaves,
15
COALESCE(elb.total_leaves - elb.used_leaves, 0) AS remaining_leaves
16
FROM employee_leave_balances elb
17
JOIN leave_types lt ON elb.leave_type_id = lt.id
18
JOIN employees e ON elb.employee_id = e.id
19
WHERE (p_employee_id IS NULL OR elb.employee_id = p_employee_id);
20
END;
21
$function$
|
|||||
| Function | get_new_payment_number | Match | ||||||
| Function | get_payroll_transactions_by_company | Match | ||||||
| Function | get_leave_applications_with_balance | Match | ||||||
| Function | get_all_leave_entitlements | Match | ||||||
| Function | get_all_tasks | Match | ||||||
| Function | get_employee_leave_entitlement | Match | ||||||
| Function | get_leave_applications_with_balance | Match | ||||||
| Function | get_new_payroll_transaction_number | Match | ||||||
| Function | get_payroll_transactions_by_fin_year_num | Match | ||||||
| Function | get_holidays_with_user_names | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.get_holidays_with_user_names(in_year integer, in_company_id uuid)
2
RETURNS TABLE(id integer, holiday_date date, name character varying, description text, created_on_utc timestamp without time zone, modified_on_utc timestamp without time zone, deleted_on_utc timestamp without time zone, is_deleted boolean, created_by_id uuid, created_by character varying, modified_by_id uuid, modified_by character varying, company_id uuid)
3
LANGUAGE plpgsql
4
STABLE
5
AS $function$
6
BEGIN
7
RETURN QUERY
8
SELECT
9
h.id,
10
h.holiday_date,
11
h.name,
12
h.description,
13
h.created_on_utc,
14
h.modified_on_utc,
15
h.deleted_on_utc,
16
h.is_deleted,
17
h.created_by AS created_by_id,
18
(u1.first_name || ' ' || u1.last_name)::varchar AS created_by,
19
h.modified_by AS modified_by_id,
20
(u2.first_name || ' ' || u2.last_name)::varchar AS modified_by,
21
h.company_id
22
FROM public.holidays h
23
LEFT JOIN public.users u1 ON h.created_by = u1.id
24
LEFT JOIN public.users u2 ON h.modified_by = u2.id
25
WHERE
26
h.holiday_date >= make_date(in_year, 1, 1)
27
AND h.holiday_date < make_date(in_year + 1, 1, 1)
28
AND h.company_id = in_company_id
29
AND h.is_deleted = FALSE
30
ORDER BY h.holiday_date;
31
END;
32
$function$
|
|||||
| Function | get_leave_application_with_balance_also_check_managerid | Match | ||||||
| Function | update_payroll_status | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.update_payroll_status(p_company_id uuid, p_payroll_ids uuid[], p_target_status integer, p_modified_by uuid)
2
RETURNS TABLE(payroll_id uuid, new_status text, error_msg text)
3
LANGUAGE plpgsql
4
AS $function$
5
DECLARE
6
v_exists boolean;
7
v_current_status int;
8
v_allowed boolean;
9
BEGIN
10
FOREACH payroll_id IN ARRAY p_payroll_ids LOOP
11
12
BEGIN
13
--------------------------------------------------------
14
-- 1. Check payroll exists
15
--------------------------------------------------------
16
SELECT EXISTS(
17
SELECT 1
18
FROM payroll_transaction_headers
19
WHERE id = payroll_id
20
AND company_id = p_company_id
21
) INTO v_exists;
22
23
IF NOT v_exists THEN
24
RETURN QUERY SELECT payroll_id, NULL, 'Payroll not found';
25
CONTINUE;
26
END IF;
27
28
--------------------------------------------------------
29
-- 2. Get current status
30
--------------------------------------------------------
31
SELECT status_id INTO v_current_status
32
FROM payroll_transaction_headers
33
WHERE id = payroll_id;
34
35
--------------------------------------------------------
36
-- 3. Validate transition from workflow table
37
--------------------------------------------------------
38
SELECT EXISTS(
39
SELECT 1
40
FROM payroll_workflow
41
WHERE company_id = p_company_id
42
AND status = v_current_status
43
AND next_status = p_target_status
44
AND is_enabled = TRUE
45
) INTO v_allowed;
46
47
IF NOT v_allowed THEN
48
RETURN QUERY
49
SELECT payroll_id, NULL,
50
format('Transition not allowed: %s → %s', v_current_status, p_target_status);
51
CONTINUE;
52
END IF;
53
54
--------------------------------------------------------
55
-- 4. Update status
56
--------------------------------------------------------
57
UPDATE payroll_transaction_headers
58
SET status_id = p_target_status,
59
modified_by = p_modified_by,
60
modified_on_utc = NOW()
61
WHERE id = payroll_id;
62
63
--------------------------------------------------------
64
-- 5. Return readable status name
65
--------------------------------------------------------
66
RETURN QUERY
67
SELECT
68
payroll_id,
69
CASE p_target_status
70
WHEN 1 THEN 'DRAFT'
71
WHEN 2 THEN 'SUBMITTED'
72
WHEN 3 THEN 'APPROVED'
73
WHEN 4 THEN 'PAID'
74
WHEN 5 THEN 'REJECTED'
75
WHEN 6 THEN 'CANCELLED'
76
ELSE 'UNKNOWN'
77
END,
78
NULL;
79
80
EXCEPTION WHEN OTHERS THEN
81
RETURN QUERY SELECT payroll_id, NULL, 'Error: ' || SQLERRM;
82
END;
83
84
END LOOP;
85
END;
86
$function$
|
|||||
| Procedure | delete_payments_by_company | Match | ||||||
| Procedure | delete_payroll_and_payment_data | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE PROCEDURE public.delete_payroll_and_payment_data(IN p_company_id uuid)
2
LANGUAGE plpgsql
3
AS $procedure$
4
BEGIN
5
-- Delete payment details associated with payment headers of the given company
6
DELETE FROM public.payment_details
7
WHERE payment_header_id IN (
8
SELECT id FROM public.payment_headers WHERE company_id = p_company_id
9
);
10
11
-- Delete payment headers for the given company
12
DELETE FROM public.payment_headers
13
WHERE company_id = p_company_id;
14
15
-- Delete payroll transaction details associated with payroll transaction headers of the given company
16
DELETE FROM public.payroll_transaction_details
17
WHERE payroll_transaction_id IN (
18
SELECT id FROM public.payroll_transaction_headers WHERE company_id = p_company_id
19
);
20
21
-- Delete payroll transaction headers for the given company
22
DELETE FROM public.payroll_transaction_headers
23
WHERE company_id = p_company_id;
24
25
-- Commit transaction
26
COMMIT;
27
END;
28
$procedure$
|
|||||
| Procedure | delete_payroll_transactions_by_company | Match | ||||||
| Procedure | initialize_payroll_transaction_header_ids | Match | ||||||
| Procedure | hard_delete_organization | Match | ||||||
| Procedure | initialize_company | Match | ||||||
| Procedure | initialize_leave_workflow | Match | ||||||
| Procedure | create_payroll_transaction | Match | ||||||
| Procedure | save_payroll_transactions | Match | ||||||
| Procedure | hard_delete_org_payroll | Match | ||||||
| Procedure | initialize_organization | Match | ||||||
| Procedure | initialize_payment_header_ids | Match | ||||||
| Procedure | insert_payment_headers_from_payroll | Match | ||||||
| Procedure | purge_payroll_organization_data | Match | ||||||
| Procedure | clean_up_org_payroll | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE PROCEDURE public.clean_up_org_payroll(IN p_organization_id uuid)
2
LANGUAGE plpgsql
3
AS $procedure$
4
DECLARE
5
v_company_id uuid;
6
v_employee_id uuid;
7
v_payroll_configuration_header_id uuid;
8
v_payroll_transaction_header_id uuid;
9
v_payment_header_id uuid;
10
v_timesheet_id uuid;
11
v_timesheet_line_id uuid;
12
BEGIN
13
FOR v_company_id IN SELECT id FROM companies WHERE organization_id = p_organization_id LOOP
14
15
-- Employees and all related tables
16
FOR v_employee_id IN SELECT id FROM employees WHERE company_id = v_company_id LOOP
17
DELETE FROM benefits WHERE employee_id = v_employee_id;
18
DELETE FROM deductions WHERE employee_id = v_employee_id;
19
DELETE FROM loans WHERE employee_id = v_employee_id;
20
DELETE FROM advance_payments WHERE employee_id = v_employee_id;
21
DELETE FROM employee_bank_accounts WHERE employee_id = v_employee_id;
22
DELETE FROM employee_upis WHERE employee_id = v_employee_id;
23
DELETE FROM leave_applications WHERE employee_id = v_employee_id;
24
DELETE FROM leave_entitlements WHERE employee_id = v_employee_id;
25
DELETE FROM attendance WHERE employee_id = v_employee_id;
26
DELETE FROM payroll_transaction_headers WHERE employee_id = v_employee_id;
27
DELETE FROM payroll_configuration_headers WHERE employee_id = v_employee_id;
28
END LOOP;
29
30
-- Payroll configuration headers/details
31
FOR v_payroll_configuration_header_id IN
32
SELECT id FROM payroll_configuration_headers WHERE company_id = v_company_id
33
LOOP
34
DELETE FROM payroll_configuration_details WHERE configuration_header_id = v_payroll_configuration_header_id;
35
END LOOP;
36
DELETE FROM payroll_configuration_headers WHERE company_id = v_company_id;
37
38
-- Payroll transaction headers/details
39
FOR v_payroll_transaction_header_id IN
40
SELECT id FROM payroll_transaction_headers WHERE company_id = v_company_id
41
LOOP
42
DELETE FROM payroll_transaction_details WHERE payroll_transaction_id = v_payroll_transaction_header_id;
43
END LOOP;
44
DELETE FROM payroll_transaction_headers WHERE company_id = v_company_id;
45
46
-- Payment headers/details
47
FOR v_payment_header_id IN
48
SELECT id FROM payment_headers WHERE company_id = v_company_id
49
LOOP
50
DELETE FROM payment_details WHERE payment_header_id = v_payment_header_id;
51
END LOOP;
52
DELETE FROM payment_headers WHERE company_id = v_company_id;
53
54
DELETE FROM payment_header_ids WHERE company_id = v_company_id;
55
DELETE FROM payroll_transaction_header_ids WHERE company_id = v_company_id;
56
57
-- Leave/workflow/records
58
DELETE FROM leave_workflow WHERE company_id = v_company_id;
59
DELETE FROM holidays WHERE company_id = v_company_id;
60
61
-- Timesheets and details
62
FOR v_timesheet_id IN SELECT id FROM timesheets WHERE company_id = v_company_id LOOP
63
FOR v_timesheet_line_id IN SELECT id FROM timesheet_lines WHERE timesheet_id = v_timesheet_id LOOP
64
DELETE FROM timesheet_hours WHERE timesheet_line_id = v_timesheet_line_id;
65
END LOOP;
66
DELETE FROM timesheet_lines WHERE timesheet_id = v_timesheet_id;
67
END LOOP;
68
DELETE FROM timesheets WHERE company_id = v_company_id;
69
70
-- Tasks/Projects
71
DELETE FROM tasks WHERE company_id = v_company_id;
72
DELETE FROM projects WHERE company_id = v_company_id;
73
74
-- Workdays
75
DELETE FROM workdays WHERE company_id = v_company_id;
76
77
-- Users
78
DELETE FROM users WHERE company_id = v_company_id;
79
80
-- Employees (must come after all dependent deletes)
81
DELETE FROM employees WHERE company_id = v_company_id;
82
83
-- Companies
84
DELETE FROM companies WHERE id = v_company_id;
85
86
RAISE NOTICE 'Deleted payroll data for company_id %', v_company_id;
87
END LOOP;
88
89
-- Organization
90
DELETE FROM organizations WHERE id = p_organization_id;
91
92
RAISE NOTICE 'Deleted payroll data for organization_id %', p_organization_id;
93
END;
94
$procedure$
|
|||||
| Procedure | update_payment_header_posted | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE PROCEDURE public.update_payment_header_posted(IN p_payment_header_id uuid, IN p_transaction_id bigint)
2
LANGUAGE plpgsql
3
AS $procedure$
4
BEGIN
5
UPDATE payment_headers
6
SET is_posted = true,
7
transaction_id = p_transaction_id,
8
modified_on_utc = now()
9
WHERE id = p_payment_header_id;
10
END;
11
$procedure$
|
|||||
| Procedure | insert_payment_headers_from_payroll | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE PROCEDURE public.insert_payment_headers_from_payroll(IN p_payment_header_id uuid, IN p_company_id uuid, IN p_payment_date timestamp without time zone, IN p_reference text, IN p_credit_account_id uuid, IN p_debit_account_id uuid, IN p_mode_of_payment text, IN p_created_by uuid, IN p_payroll_transaction_ids uuid[])
2
LANGUAGE plpgsql
3
AS $procedure$
4
DECLARE
5
v_total_paid_amount numeric := 0;
6
v_serial_number integer := 1;
7
v_payroll_record RECORD;
8
v_payment_number varchar;
9
BEGIN
10
v_payment_number := get_new_payment_number(
11
p_company_id,
12
p_payment_date::date
13
);
14
15
IF array_length(p_payroll_transaction_ids, 1) IS NULL THEN
16
RAISE NOTICE 'No payroll transaction IDs provided.';
17
RETURN;
18
END IF;
19
20
-- Insert into payment_headers
21
INSERT INTO public.payment_headers (
22
id,
23
company_id,
24
paid_date,
25
paid_amount,
26
tds_amount,
27
advance_amount,
28
description,
29
credit_account_id,
30
debit_account_id,
31
mode_of_payment, -- âś… dynamic
32
reference,
33
payment_number,
34
created_on_utc,
35
created_by,
36
is_deleted
37
)
38
VALUES (
39
p_payment_header_id,
40
p_company_id,
41
p_payment_date,
42
0,
43
0,
44
0,
45
'Payment of salary',
46
p_credit_account_id,
47
p_debit_account_id,
48
p_mode_of_payment, -- âś… using parameter
49
p_reference,
50
v_payment_number,
51
NOW(),
52
p_created_by,
53
false
54
);
55
56
-- Insert payment details + update payroll transactions
57
FOR v_payroll_record IN
58
SELECT id, employee_id, net_pay
59
FROM public.payroll_transaction_headers
60
WHERE id = ANY(p_payroll_transaction_ids)
61
LOOP
62
INSERT INTO public.payment_details (
63
id,
64
payment_header_id,
65
employee_id,
66
paid_amount,
67
serial_number,
68
is_deleted
69
)
70
VALUES (
71
gen_random_uuid(),
72
p_payment_header_id,
73
v_payroll_record.employee_id,
74
v_payroll_record.net_pay,
75
v_serial_number,
76
false
77
);
78
79
UPDATE public.payroll_transaction_headers
80
SET status_id = 2
81
WHERE id = v_payroll_record.id;
82
83
v_total_paid_amount := v_total_paid_amount + v_payroll_record.net_pay;
84
v_serial_number := v_serial_number + 1;
85
END LOOP;
86
87
-- Update total paid amount
88
UPDATE public.payment_headers
89
SET paid_amount = v_total_paid_amount
90
WHERE id = p_payment_header_id;
91
92
RAISE NOTICE
93
'Payment Header % inserted with total paid amount %',
94
p_payment_header_id,
95
v_total_paid_amount;
96
END;
97
$procedure$
|
-- Table: payroll_workflow
Exists in source, missing in target
-- Table: leave_records
Exists in source, missing in target
-- Table: payment_headers
-- StructuralDiff: Mismatch
-- SOURCE SIGNATURE
COL:advance_amount:numeric:True:::False|COL:company_id:uuid:False:::False|COL:created_by:uuid:False:::False|COL:created_on_utc:timestamp without time zone:False:::False|COL:credit_account_id:uuid:False:::False|COL:debit_account_id:uuid:False:::False|COL:deleted_on_utc:timestamp without time zone:True:::False|COL:description:text:False:::False|COL:id:uuid:False:::False|COL:is_deleted:boolean:False::false:False|COL:is_posted:boolean:False::false:False|COL:mode_of_payment:text:False:::False|COL:modified_by:uuid:False::'00000000-0000-0000-0000-000000000000'::uuid:False|COL:modified_on_utc:timestamp without time zone:True:::False|COL:paid_amount:numeric:False:::False|COL:paid_date:timestamp without time zone:False:::False|COL:payment_number:text:False::''::text:False|COL:reference:text:False:::False|COL:tds_amount:numeric:True:::False|COL:transaction_id:bigint:True:::False|PK:|FK:company_id→companies.id|IDX:btree:unique:id:
-- TARGET SIGNATURE
COL:advance_amount:numeric:True:::False|COL:company_id:uuid:False:::False|COL:created_by:uuid:False:::False|COL:created_on_utc:timestamp without time zone:False:::False|COL:credit_account_id:uuid:False:::False|COL:debit_account_id:uuid:False:::False|COL:deleted_on_utc:timestamp without time zone:True:::False|COL:description:text:False:::False|COL:id:uuid:False:::False|COL:is_deleted:boolean:False::false:False|COL:mode_of_payment:text:False:::False|COL:modified_on_utc:timestamp without time zone:True:::False|COL:paid_amount:numeric:False:::False|COL:paid_date:timestamp without time zone:False:::False|COL:payment_number:text:False::''::text:False|COL:reference:text:False:::False|COL:tds_amount:numeric:True:::False|PK:|IDX:btree:unique:id:
-- SOURCE SCRIPT
CREATE TABLE "payment_headers" ("id" uuid NOT NULL, "company_id" uuid NOT NULL, "paid_date" timestamp without time zone NOT NULL, "paid_amount" numeric(,) NOT NULL, "tds_amount" numeric(,), "advance_amount" numeric(,), "description" text NOT NULL, "debit_account_id" uuid NOT NULL, "credit_account_id" uuid NOT NULL, "mode_of_payment" text NOT NULL, "reference" text NOT NULL, "created_on_utc" timestamp without time zone NOT NULL, "created_by" uuid NOT NULL, "modified_on_utc" timestamp without time zone, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "payment_number" text DEFAULT ''::text NOT NULL, "is_posted" boolean DEFAULT false NOT NULL, "transaction_id" bigint, "modified_by" uuid DEFAULT '00000000-0000-0000-0000-000000000000'::uuid NOT NULL, PRIMARY KEY ("id"));
-- TARGET SCRIPT
CREATE TABLE "payment_headers" ("id" uuid NOT NULL, "company_id" uuid NOT NULL, "paid_date" timestamp without time zone NOT NULL, "paid_amount" numeric(,) NOT NULL, "tds_amount" numeric(,), "advance_amount" numeric(,), "description" text NOT NULL, "debit_account_id" uuid NOT NULL, "credit_account_id" uuid NOT NULL, "mode_of_payment" text NOT NULL, "reference" text NOT NULL, "created_on_utc" timestamp without time zone NOT NULL, "created_by" uuid NOT NULL, "modified_on_utc" timestamp without time zone, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "payment_number" text DEFAULT ''::text NOT NULL, PRIMARY KEY ("id"));
-- Columns: MissingInTarget
ALTER TABLE "payment_headers" ADD COLUMN "is_posted" boolean NOT NULL;
-- Columns: MissingInTarget
ALTER TABLE "payment_headers" ADD COLUMN "transaction_id" bigint ;
-- Columns: MissingInTarget
ALTER TABLE "payment_headers" ADD COLUMN "modified_by" uuid NOT NULL;
-- ForeignKeys: MissingInTarget
ALTER TABLE "payment_headers" ADD CONSTRAINT "fk_payment_headers_company" FOREIGN KEY (company_id) REFERENCES companies(id);
-- Table: payments
Exists in source, missing in target
-- Table: workdays
-- ForeignKeys: MissingInTarget
ALTER TABLE "workdays" ADD CONSTRAINT "fk_workdays_company" FOREIGN KEY (company_id) REFERENCES companies(id);
-- Table: employee_leave_balances
Exists in source, missing in target
-- Table: addresses
-- ForeignKeys: MissingInTarget
ALTER TABLE "addresses" ADD CONSTRAINT "fk_addresses_created_by" FOREIGN KEY (created_by) REFERENCES users(id);
-- Table: bank_accounts
-- StructuralDiff: Mismatch
-- SOURCE SIGNATURE
COL:account_number:text:False:::False|COL:bank_id:integer:False:::False|COL:branch_name:text:False:::False|COL:created_by:uuid:False:::False|COL:created_on_utc:timestamp without time zone:False:::False|COL:deleted_on_utc:timestamp without time zone:True:::False|COL:id:uuid:False:::False|COL:ifsc:text:False:::False|COL:is_deleted:boolean:False::false:False|COL:modified_by:uuid:True:::False|COL:modified_on_utc:timestamp without time zone:True:::False|COL:rp_linked_account_id:text:True:::False|COL:rx_fund_account_id:text:True:::False|PK:|FK:bank_id→banks.id|FK:created_by→users.id|FK:modified_by→users.id|IDX:btree:unique:id:
-- TARGET SIGNATURE
COL:account_number:text:False:::False|COL:bank_id:integer:False:::False|COL:branch_name:text:False:::False|COL:created_by:uuid:False:::False|COL:created_on_utc:timestamp without time zone:False:::False|COL:deleted_on_utc:timestamp without time zone:True:::False|COL:id:uuid:False:::False|COL:ifsc:text:False:::False|COL:is_deleted:boolean:False::false:False|COL:modified_by:uuid:True:::False|COL:modified_on_utc:timestamp without time zone:True:::False|COL:rp_linked_account_id:text:True:::False|COL:rx_fund_account_id:text:True:::False|PK:|FK:bank_id→banks.id|FK:modified_by→users.id|IDX:btree:unique:id:
-- SOURCE SCRIPT
CREATE TABLE "bank_accounts" ("id" uuid NOT NULL, "bank_id" integer NOT NULL, "branch_name" text NOT NULL, "account_number" text NOT NULL, "ifsc" text NOT NULL, "rx_fund_account_id" text, "rp_linked_account_id" text, "created_on_utc" timestamp without time zone NOT NULL, "modified_on_utc" timestamp without time zone, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "created_by" uuid NOT NULL, "modified_by" uuid, PRIMARY KEY ("id"));
-- TARGET SCRIPT
CREATE TABLE "bank_accounts" ("id" uuid NOT NULL, "bank_id" integer NOT NULL, "branch_name" text NOT NULL, "account_number" text NOT NULL, "ifsc" text NOT NULL, "created_on_utc" timestamp without time zone NOT NULL, "modified_on_utc" timestamp without time zone, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "created_by" uuid NOT NULL, "modified_by" uuid, "rx_fund_account_id" text, "rp_linked_account_id" text, PRIMARY KEY ("id"));
-- ForeignKeys: MissingInTarget
ALTER TABLE "bank_accounts" ADD CONSTRAINT "fk_bank_accounts_created_by" FOREIGN KEY (created_by) REFERENCES users(id);
-- Table: leave_applications
-- ForeignKeys: MissingInTarget
ALTER TABLE "leave_applications" ADD CONSTRAINT "fk_leave_app_company" FOREIGN KEY (company_id) REFERENCES companies(id);
-- ForeignKeys: MissingInTarget
ALTER TABLE "leave_applications" ADD CONSTRAINT "fk_leave_applications_employee" FOREIGN KEY (employee_id) REFERENCES employees(id);
-- ForeignKeys: MissingInTarget
ALTER TABLE "leave_applications" ADD CONSTRAINT "fk_leave_applications_status" FOREIGN KEY (leave_status_id) REFERENCES leave_statuses(id);
-- Table: loans
-- StructuralDiff: Mismatch
-- SOURCE SIGNATURE
COL:created_by:uuid:False:::False|COL:created_on_utc:timestamp without time zone:False:::False|COL:deleted_on_utc:timestamp without time zone:True:::False|COL:description:text:False:::False|COL:employee_id:uuid:False:::False|COL:id:uuid:False:::False|COL:interest_rate:numeric:False:::False|COL:is_deleted:boolean:False::false:False|COL:loan_amount:numeric:False:::False|COL:modified_by:uuid:True:::False|COL:modified_on_utc:timestamp without time zone:True:::False|COL:monthly_installment:numeric:False:::False|COL:outstanding_balance:numeric:False:::False|COL:repayment_end_date:date:True:::False|COL:repayment_start_date:date:False:::False|COL:status_id:integer:False:::False|PK:|FK:employee_id→employees.id|FK:status_id→statuses.id|IDX:btree:unique:id:
-- TARGET SIGNATURE
COL:created_by:uuid:False:::False|COL:created_on_utc:timestamp without time zone:False:::False|COL:deleted_on_utc:timestamp without time zone:True:::False|COL:description:text:False:::False|COL:employee_id:uuid:False:::False|COL:id:uuid:False:::False|COL:interest_rate:numeric:False:::False|COL:is_deleted:boolean:False::false:False|COL:loan_amount:numeric:False:::False|COL:modified_by:uuid:True:::False|COL:modified_on_utc:timestamp without time zone:True:::False|COL:monthly_installment:numeric:False:::False|COL:outstanding_balance:numeric:False:::False|COL:repayment_end_date:date:True:::False|COL:repayment_start_date:date:False:::False|COL:status_id:integer:False:::False|PK:|FK:status_id→statuses.id|IDX:btree:unique:id:
-- SOURCE SCRIPT
CREATE TABLE "loans" ("id" uuid NOT NULL, "employee_id" uuid NOT NULL, "loan_amount" numeric(,) NOT NULL, "interest_rate" numeric(,) NOT NULL, "repayment_start_date" date NOT NULL, "repayment_end_date" date, "monthly_installment" numeric(,) NOT NULL, "outstanding_balance" numeric(,) NOT NULL, "description" text NOT NULL, "status_id" integer NOT NULL, "created_on_utc" timestamp without time zone NOT NULL, "modified_on_utc" timestamp without time zone, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "created_by" uuid NOT NULL, "modified_by" uuid, PRIMARY KEY ("id"));
-- TARGET SCRIPT
CREATE TABLE "loans" ("id" uuid NOT NULL, "employee_id" uuid NOT NULL, "loan_amount" numeric(15,2) NOT NULL, "interest_rate" numeric(5,2) NOT NULL, "repayment_start_date" date NOT NULL, "repayment_end_date" date, "monthly_installment" numeric(15,2) NOT NULL, "outstanding_balance" numeric(15,2) NOT NULL, "description" text NOT NULL, "status_id" integer NOT NULL, "created_on_utc" timestamp without time zone NOT NULL, "modified_on_utc" timestamp without time zone, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "created_by" uuid NOT NULL, "modified_by" uuid, PRIMARY KEY ("id"));
-- ForeignKeys: MissingInTarget
ALTER TABLE "loans" ADD CONSTRAINT "fk_loans_employee" FOREIGN KEY (employee_id) REFERENCES employees(id);
-- Table: users
-- Indexes: MissingInTarget
CREATE UNIQUE INDEX uq_users_email ON public.users USING btree (email)
-- Table: leave_entitlements
-- ForeignKeys: MissingInTarget
ALTER TABLE "leave_entitlements" ADD CONSTRAINT "fk_leave_entitlements_company" FOREIGN KEY (company_id) REFERENCES companies(id);
-- Table: payroll_transaction_headers
-- StructuralDiff: Mismatch
-- SOURCE SIGNATURE
COL:allowances:numeric:True:::False|COL:basic_salary:numeric:False:::False|COL:company_id:uuid:False::'00000000-0000-0000-0000-000000000000'::uuid:False|COL:created_by:uuid:False:::False|COL:created_on_utc:timestamp without time zone:False:::False|COL:deductions:numeric:True:::False|COL:deleted_on_utc:timestamp without time zone:True:::False|COL:employee_id:uuid:False:::False|COL:id:uuid:False:::False|COL:is_deleted:boolean:False::false:False|COL:is_posted:boolean:False::false:False|COL:modified_by:uuid:True:::False|COL:modified_on_utc:timestamp without time zone:True:::False|COL:net_pay:numeric:False:::False|COL:payment_date:date:True:::False|COL:payment_method_id:integer:False:::False|COL:payroll_number:text:False::''::text:False|COL:pay_period_end:date:False:::False|COL:pay_period_start:date:False:::False|COL:status_id:integer:False::1:False|COL:transaction_id:bigint:True:::False|PK:|FK:company_id→companies.id|FK:created_by→users.id|FK:employee_id→employees.id|FK:modified_by→users.id|FK:status_id→statuses.id|IDX:btree:unique:id:
-- TARGET SIGNATURE
COL:allowances:numeric:True:::False|COL:basic_salary:numeric:False:::False|COL:company_id:uuid:False::'00000000-0000-0000-0000-000000000000'::uuid:False|COL:created_by:uuid:False:::False|COL:created_on_utc:timestamp without time zone:False:::False|COL:deductions:numeric:True:::False|COL:deleted_on_utc:timestamp without time zone:True:::False|COL:employee_id:uuid:False:::False|COL:id:uuid:False:::False|COL:is_deleted:boolean:False::false:False|COL:modified_by:uuid:True:::False|COL:modified_on_utc:timestamp without time zone:True:::False|COL:net_pay:numeric:False:::False|COL:payment_date:date:True:::False|COL:payment_method_id:integer:False:::False|COL:payroll_number:text:False::''::text:False|COL:pay_period_end:date:False:::False|COL:pay_period_start:date:False:::False|COL:status_id:integer:False::1:False|PK:|FK:company_id→companies.id|FK:created_by→users.id|FK:employee_id→employees.id|FK:modified_by→users.id|FK:status_id→statuses.id|IDX:btree:unique:id:
-- SOURCE SCRIPT
CREATE TABLE "payroll_transaction_headers" ("id" uuid NOT NULL, "employee_id" uuid NOT NULL, "pay_period_start" date NOT NULL, "pay_period_end" date NOT NULL, "basic_salary" numeric(,) NOT NULL, "allowances" numeric(,), "deductions" numeric(,), "net_pay" numeric(,) NOT NULL, "payment_date" date, "payment_method_id" integer NOT NULL, "created_on_utc" timestamp without time zone NOT NULL, "modified_on_utc" timestamp without time zone, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "created_by" uuid NOT NULL, "modified_by" uuid, "company_id" uuid DEFAULT '00000000-0000-0000-0000-000000000000'::uuid NOT NULL, "status_id" integer DEFAULT 1 NOT NULL, "payroll_number" text DEFAULT ''::text NOT NULL, "is_posted" boolean DEFAULT false NOT NULL, "transaction_id" bigint, PRIMARY KEY ("id"));
-- TARGET SCRIPT
CREATE TABLE "payroll_transaction_headers" ("id" uuid NOT NULL, "employee_id" uuid NOT NULL, "pay_period_start" date NOT NULL, "pay_period_end" date NOT NULL, "basic_salary" numeric(15,2) NOT NULL, "allowances" numeric(15,2), "deductions" numeric(15,2), "net_pay" numeric(15,2) NOT NULL, "payment_date" date, "payment_method_id" integer NOT NULL, "created_on_utc" timestamp without time zone NOT NULL, "modified_on_utc" timestamp without time zone, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "created_by" uuid NOT NULL, "modified_by" uuid, "company_id" uuid DEFAULT '00000000-0000-0000-0000-000000000000'::uuid NOT NULL, "status_id" integer DEFAULT 1 NOT NULL, "payroll_number" text DEFAULT ''::text NOT NULL, PRIMARY KEY ("id"));
-- Columns: MissingInTarget
ALTER TABLE "payroll_transaction_headers" ADD COLUMN "is_posted" boolean NOT NULL;
-- Columns: MissingInTarget
ALTER TABLE "payroll_transaction_headers" ADD COLUMN "transaction_id" bigint ;
-- Table: payroll_components
Exists in source, missing in target
-- Table: projects
-- ForeignKeys: MissingInTarget
ALTER TABLE "projects" ADD CONSTRAINT "fk_projects_company" FOREIGN KEY (company_id) REFERENCES companies(id);
-- Table: tasks
-- StructuralDiff: Mismatch
-- SOURCE SIGNATURE
COL:company_id:uuid:False::'00000000-0000-0000-0000-000000000000'::uuid:False|COL:created_by:uuid:False:::False|COL:created_on_utc:timestamp without time zone:False:::False|COL:deleted_on_utc:timestamp without time zone:True:::False|COL:id:integer:False:::False|COL:is_deleted:boolean:False::false:False|COL:modified_by:uuid:True:::False|COL:modified_on_utc:timestamp without time zone:True:::False|COL:name:text:False:::False|COL:parent_task_id:integer:True:::False|COL:planned_hours:integer:False:::False|COL:project_id:uuid:False:::False|COL:sub_project_id:integer:False:::False|PK:|FK:company_id→companies.id|IDX:btree:unique:id:
-- TARGET SIGNATURE
COL:company_id:uuid:False::'00000000-0000-0000-0000-000000000000'::uuid:False|COL:created_by:uuid:False:::False|COL:created_on_utc:timestamp without time zone:False:::False|COL:deleted_on_utc:timestamp without time zone:True:::False|COL:id:integer:False:::False|COL:is_deleted:boolean:False::false:False|COL:modified_by:uuid:True:::False|COL:modified_on_utc:timestamp without time zone:True:::False|COL:name:text:False:::False|COL:parent_task_id:integer:True:::False|COL:planned_hours:integer:False:::False|COL:project_id:uuid:False:::False|COL:sub_project_id:integer:False:::False|PK:|IDX:btree:unique:id:
-- SOURCE SCRIPT
CREATE TABLE "tasks" ("project_id" uuid NOT NULL, "sub_project_id" integer NOT NULL, "parent_task_id" integer, "name" text NOT NULL, "planned_hours" integer NOT NULL, "created_on_utc" timestamp without time zone NOT NULL, "modified_on_utc" timestamp without time zone, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "created_by" uuid NOT NULL, "modified_by" uuid, "id" integer NOT NULL, "company_id" uuid DEFAULT '00000000-0000-0000-0000-000000000000'::uuid NOT NULL, PRIMARY KEY ("id"));
-- TARGET SCRIPT
CREATE TABLE "tasks" ("id" integer NOT NULL, "project_id" uuid NOT NULL, "sub_project_id" integer NOT NULL, "parent_task_id" integer, "name" text NOT NULL, "planned_hours" integer NOT NULL, "created_on_utc" timestamp without time zone NOT NULL, "modified_on_utc" timestamp without time zone, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "created_by" uuid NOT NULL, "modified_by" uuid, "company_id" uuid DEFAULT '00000000-0000-0000-0000-000000000000'::uuid NOT NULL, PRIMARY KEY ("id"));
-- ForeignKeys: MissingInTarget
ALTER TABLE "tasks" ADD CONSTRAINT "fk_tasks_company" FOREIGN KEY (company_id) REFERENCES companies(id);
-- Table: timesheets
-- ForeignKeys: MissingInTarget
ALTER TABLE "timesheets" ADD CONSTRAINT "fk_timesheets_company" FOREIGN KEY (company_id) REFERENCES companies(id);
-- Table: payroll_configuration_details
-- ForeignKeys: MissingInTarget
ALTER TABLE "payroll_configuration_details" ADD CONSTRAINT "fk_payroll_config_details_header" FOREIGN KEY (configuration_header_id) REFERENCES payroll_configuration_headers(id);
-- Table: states
-- ForeignKeys: MissingInTarget
ALTER TABLE "states" ADD CONSTRAINT "fk_states_country" FOREIGN KEY (country_id) REFERENCES countries(id);
-- Function: get_leave_records_with_balance
CREATE OR REPLACE FUNCTION public.get_leave_records_with_balance(p_company_id uuid, p_fin_year_id integer)
RETURNS TABLE(leave_record_id uuid, employee_id uuid, employee_name text, manager_id uuid, leave_type_id integer, leave_type_name text, start_date date, end_date date, reason text, leave_status_id integer, leave_status text, total_leaves integer, used_leaves integer, remaining_leaves integer)
LANGUAGE plpgsql
AS $function$
DECLARE
financial_year_start date := make_date(p_fin_year_id, 4, 1);
financial_year_end date := make_date(p_fin_year_id + 1, 3, 31);
BEGIN
RETURN QUERY
SELECT
lr.id AS leave_record_id,
lr.employee_id AS employee_id,
CONCAT(e.first_name, ' ', e.last_name) AS employee_name,
e.manager_id, -- ⬅️ Select manager_id
lt.id AS leave_type_id,
lt.name::TEXT AS leave_type_name,
lr.start_date,
lr.end_date,
lr.reason,
ls.id AS leave_status_id,
ls.name::TEXT AS leave_status,
COALESCE(elb.total_leaves, 0) AS total_leaves,
COALESCE(elb.used_leaves, 0) AS used_leaves,
COALESCE(elb.total_leaves - elb.used_leaves, 0) AS remaining_leaves
FROM leave_records lr
INNER JOIN employees e
ON lr.employee_id = e.id
INNER JOIN leave_statuses ls
ON lr.leave_status_id = ls.id
INNER JOIN leave_types lt
ON lr.leave_type_id = lt.id
LEFT JOIN (
SELECT
elb_inner.employee_id,
elb_inner.leave_type_id,
elb_inner.total_leaves,
elb_inner.used_leaves
FROM (
SELECT
elb.employee_id,
elb.leave_type_id,
elb.total_leaves,
elb.used_leaves,
ROW_NUMBER() OVER (
PARTITION BY elb.employee_id, elb.leave_type_id
ORDER BY elb.id DESC
) AS rn
FROM employee_leave_balances elb
) elb_inner
WHERE elb_inner.rn = 1
) elb
ON lr.employee_id = elb.employee_id
AND lr.leave_type_id = elb.leave_type_id
WHERE lr.company_id = p_company_id
AND (lr.is_deleted IS NULL OR lr.is_deleted = FALSE)
AND lr.start_date <= financial_year_end
AND lr.end_date >= financial_year_start;
END;
$function$
-- Function: get_leave_records_with_balance_also_check_managerid
CREATE OR REPLACE FUNCTION public.get_leave_records_with_balance_also_check_managerid(p_company_id uuid, p_fin_year_id integer, p_employee_id uuid)
RETURNS TABLE(leave_record_id uuid, employee_id uuid, employee_name text, manager_id uuid, leave_type_id integer, leave_type_name text, start_date date, end_date date, reason text, leave_status_id integer, leave_status text, total_leaves integer, used_leaves integer, remaining_leaves integer, created_by uuid, modified_by uuid)
LANGUAGE plpgsql
AS $function$
DECLARE
financial_year_start date := make_date(p_fin_year_id, 4, 1);
financial_year_end date := make_date(p_fin_year_id + 1, 3, 31);
is_manager boolean := false;
BEGIN
-- Is the given employee a manager in this company?
SELECT EXISTS (
SELECT 1
FROM employees e
WHERE e.manager_id = p_employee_id
AND e.company_id = p_company_id
) INTO is_manager;
RETURN QUERY
SELECT
lr.id AS leave_record_id,
lr.employee_id AS employee_id,
CONCAT(e.first_name, ' ', e.last_name) AS employee_name,
e.manager_id,
lt.id AS leave_type_id,
lt.name::text AS leave_type_name,
lr.start_date,
lr.end_date,
lr.reason,
ls.id AS leave_status_id,
ls.name::text AS leave_status,
COALESCE(elb.total_leaves, 0) AS total_leaves,
COALESCE(elb.used_leaves, 0) AS used_leaves,
COALESCE(elb.total_leaves - elb.used_leaves, 0) AS remaining_leaves,
lr.created_by,
lr.modified_by
FROM leave_records lr
INNER JOIN employees e ON lr.employee_id = e.id
INNER JOIN leave_statuses ls ON lr.leave_status_id = ls.id
INNER JOIN leave_types lt ON lr.leave_type_id = lt.id
LEFT JOIN (
SELECT
elb_inner.employee_id,
elb_inner.leave_type_id,
elb_inner.total_leaves,
elb_inner.used_leaves
FROM (
SELECT
elb.employee_id,
elb.leave_type_id,
elb.total_leaves,
elb.used_leaves,
ROW_NUMBER() OVER (
PARTITION BY elb.employee_id, elb.leave_type_id
ORDER BY elb.id DESC
) AS rn
FROM employee_leave_balances elb
) elb_inner
WHERE elb_inner.rn = 1
) elb
ON lr.employee_id = elb.employee_id
AND lr.leave_type_id = elb.leave_type_id
WHERE lr.company_id = p_company_id
AND (lr.is_deleted IS NULL OR lr.is_deleted = FALSE)
AND lr.start_date <= financial_year_end
AND lr.end_date >= financial_year_start
AND (
is_manager = TRUE
OR lr.employee_id = p_employee_id
);
END;
$function$
-- Function: get_all_payroll
-- SOURCE
CREATE OR REPLACE FUNCTION public.get_all_payroll(p_employee_id uuid, p_department_id integer, p_company_id uuid)
RETURNS TABLE(id uuid, employee_name character varying, basic_salary numeric, addition numeric, deduction numeric, net_pay numeric, components jsonb)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT
e.id AS id,
(e.first_name || ' ' || e.last_name)::VARCHAR AS employee_name,
-- Basic salary (monthly) : sum of basic amounts divided by 12
ROUND(COALESCE(SUM(CASE WHEN ct.id = 1 THEN (pcd.amount / 12.0) ELSE 0 END), 0)::NUMERIC, 2) AS basic_salary,
-- Addition (monthly): DA, HRA, Conveyance, Special, LTA, Medical => divided by 12
ROUND(COALESCE(SUM(CASE WHEN ct.id IN (2,3,4,5,6,7) THEN (pcd.amount / 12.0) ELSE 0 END), 0)::NUMERIC, 2) AS addition,
-- Deduction (monthly): Professional Tax, PF => divided by 12
ROUND(COALESCE(SUM(CASE WHEN ct.id IN (8,9) THEN (pcd.amount / 12.0) ELSE 0 END), 0)::NUMERIC, 2) AS deduction,
-- Net Pay (monthly) = Basic(monthly) + Addition(monthly) - Deduction(monthly)
ROUND(
COALESCE(SUM(CASE WHEN ct.id = 1 THEN (pcd.amount / 12.0) ELSE 0 END), 0)
+
COALESCE(SUM(CASE WHEN ct.id IN (2,3,4,5,6,7) THEN (pcd.amount / 12.0) ELSE 0 END), 0)
-
COALESCE(SUM(CASE WHEN ct.id IN (9) THEN (pcd.amount / 12.0) ELSE 0 END), 0)
, 2) AS net_pay,
-- Components as JSONB with monthly amounts
jsonb_agg(
jsonb_build_object(
'componentTypeId', ct.id,
'componentTypeName', ct.name,
'amount', ROUND(COALESCE(pcd.amount, 0) / 12.0, 2)
) ORDER BY ct.id
) AS components
FROM public.employees e
LEFT JOIN public.payroll_configuration_headers pch ON e.id = pch.employee_id
LEFT JOIN public.payroll_configuration_details pcd ON pch.id = pcd.configuration_header_id
LEFT JOIN public.component_types ct ON pcd.component_type_id = ct.id
WHERE
(p_employee_id IS NULL OR e.id = p_employee_id) AND
(p_department_id IS NULL OR e.department_id = p_department_id) AND
pch.company_id = p_company_id AND
pch.is_deleted = FALSE AND
e.is_deleted = FALSE
GROUP BY
e.id, pch.employee_id, e.first_name, e.last_name;
END;
$function$
-- TARGET
CREATE OR REPLACE FUNCTION public.get_all_payroll(p_employee_id uuid, p_department_id integer, p_company_id uuid)
RETURNS TABLE(id uuid, employee_name character varying, basic_salary numeric, addition numeric, deduction numeric, net_pay numeric, components jsonb)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT
e.id AS employee_id,
(e.first_name || ' ' || e.last_name)::VARCHAR AS employee_name,
-- Basic salary
ROUND(COALESCE(SUM(CASE
WHEN ct.id = 1 THEN pcd.amount / 12
ELSE 0 END)::NUMERIC, 0), 2) AS basic_salary,
-- Addition
ROUND(COALESCE(SUM(CASE
WHEN ct.id IN (2, 3, 4, 5, 6, 7)
THEN pcd.amount / 12 ELSE 0 END)::NUMERIC, 0), 2) AS addition,
-- Deduction
ROUND(COALESCE(SUM(CASE
WHEN ct.id IN (8, 9, 10)
THEN pcd.amount / 12 ELSE 0 END)::NUMERIC, 0), 2) AS deduction,
-- Net Pay (Basic salary + Addition - Deduction)
ROUND(
(COALESCE(SUM(CASE WHEN ct.id = 1 THEN pcd.amount / 12 ELSE 0 END), 0)::NUMERIC) +
(COALESCE(SUM(CASE
WHEN ct.id IN (2, 3, 4, 5, 6, 7)
THEN pcd.amount / 12 ELSE 0 END), 0)::NUMERIC) -
(COALESCE(SUM(CASE
WHEN ct.id IN (8, 9, 10)
THEN pcd.amount / 12 ELSE 0 END), 0)::NUMERIC), 2) AS net_pay,
-- Components as JSONB
jsonb_agg(jsonb_build_object(
'componentTypeId', ct.id,
'amount', ROUND(pcd.amount / 12, 2)
)) AS components
FROM
public.employees e
LEFT JOIN
public.payroll_configuration_headers pch on e.id = pch.employee_id
LEFT JOIN
public.payroll_configuration_details pcd on pch.id = pcd.configuration_header_id
LEFT JOIN
public.component_types ct ON pcd.component_type_id = ct.id
WHERE
(p_employee_id IS NULL OR e.id = p_employee_id) AND
(p_department_id IS NULL OR e.department_id = p_department_id) AND
pch.company_id = p_company_id AND
pch.is_deleted = FALSE AND
e.is_deleted = FALSE
GROUP BY
e.id, pch.employee_id, e.first_name, e.last_name;
END;
$function$
-- Function: get_employee_leave_summary
CREATE OR REPLACE FUNCTION public.get_employee_leave_summary(p_company_id uuid, p_fin_year integer)
RETURNS TABLE(employee_id uuid, employee_name text, total_leaves integer, used_leave_count integer, pending_leave_count integer, remaining_leave integer)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
WITH leave_data AS (
-- Gathering data related to each employee's leaves and counting leave requests
SELECT
elb.employee_id,
CAST(SUM(elb.total_leaves) AS integer) AS total_leaves, -- Sum of all leave types for the employee, cast to integer
CAST(COALESCE(COUNT(CASE WHEN lr.leave_status_id = 2 THEN 1 END), 0) AS integer) AS used_leave_count, -- Count of approved leave requests, cast to integer
CAST(COALESCE(COUNT(CASE WHEN lr.leave_status_id = 1 THEN 1 END), 0) AS integer) AS pending_leave_count, -- Count of pending leave requests, cast to integer
CAST(SUM(elb.total_leaves) - COALESCE(COUNT(CASE WHEN lr.leave_status_id = 2 THEN 1 END), 0) AS integer) AS remaining_leave -- Remaining leave (total - approved), cast to integer
FROM employee_leave_balances elb
LEFT JOIN leave_records lr
ON lr.employee_id = elb.employee_id
AND lr.leave_type_id = elb.leave_type_id
AND lr.start_date >= make_date(p_fin_year, 1, 1) -- Start date for the given financial year
AND lr.end_date <= make_date(p_fin_year, 12, 31) -- End date for the given financial year
WHERE elb.company_id = p_company_id
GROUP BY elb.employee_id
),
employees AS (
-- Getting the list of employees in the given company
SELECT e.id AS employee_id, CONCAT(e.first_name, ' ', e.last_name) AS employee_name
FROM employees e
WHERE e.company_id = p_company_id
)
SELECT
e.employee_id,
e.employee_name,
ld.total_leaves,
ld.used_leave_count,
ld.pending_leave_count,
ld.remaining_leave
FROM employees e
LEFT JOIN leave_data ld ON e.employee_id = ld.employee_id
ORDER BY e.employee_name; -- Sorting by employee name
END;
$function$
-- Function: get_all_leave_records
CREATE OR REPLACE FUNCTION public.get_all_leave_records(p_company_id uuid)
RETURNS TABLE(leave_record_id uuid, employee_id uuid, employee_name text, leave_type_id integer, start_date date, end_date date, reason text, leave_status_id integer, leave_status_name text)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT
lr.id AS leave_record_id,
lr.employee_id,
e.first_name || ' ' || e.last_name AS employee_name,
lr.leave_type_id,
lr.start_date,
lr.end_date,
lr.reason,
lr.leave_status_id,
ls.name::TEXT AS leave_status_name -- Explicit casting to TEXT
FROM leave_records lr
JOIN employees e ON lr.employee_id = e.id
JOIN leave_statuses ls ON lr.leave_status_id = ls.id
WHERE lr.company_id = p_company_id;
END;
$function$
-- Function: grant_full_schema_access
CREATE OR REPLACE FUNCTION public.grant_full_schema_access(p_schema text, p_user text)
RETURNS void
LANGUAGE plpgsql
AS $function$
DECLARE
obj RECORD;
BEGIN
-- Grant on tables
FOR obj IN
SELECT table_name
FROM information_schema.tables
WHERE table_schema = p_schema
AND table_type = 'BASE TABLE'
LOOP
EXECUTE format('GRANT SELECT, INSERT, UPDATE, DELETE ON TABLE %I.%I TO %I;', p_schema, obj.table_name, p_user);
RAISE NOTICE 'GRANT SELECT, INSERT, UPDATE, DELETE ON TABLE %.% TO %;', p_schema, obj.table_name, p_user;
END LOOP;
-- Grant on sequences (USAGE + SELECT + UPDATE: full coverage)
FOR obj IN
SELECT c.relname AS sequence_name
FROM pg_class c
JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE c.relkind = 'S'
AND n.nspname = p_schema
LOOP
EXECUTE format('GRANT USAGE, SELECT, UPDATE ON SEQUENCE %I.%I TO %I;', p_schema, obj.sequence_name, p_user);
RAISE NOTICE 'GRANT USAGE, SELECT, UPDATE ON SEQUENCE %.% TO %;', p_schema, obj.sequence_name, p_user;
END LOOP;
-- Grant on all functions (handles all argument types)
FOR obj IN
SELECT
p.proname AS function_name,
pg_get_function_identity_arguments(p.oid) AS args
FROM
pg_proc p
JOIN pg_namespace n ON p.pronamespace = n.oid
WHERE
n.nspname = p_schema
AND p.prokind = 'f' -- f = function
LOOP
EXECUTE format(
'GRANT EXECUTE ON FUNCTION %I.%I(%s) TO %I;',
p_schema, obj.function_name, obj.args, p_user
);
RAISE NOTICE 'GRANT EXECUTE ON FUNCTION %.%(%) TO %;', p_schema, obj.function_name, obj.args, p_user;
END LOOP;
-- Grant on all procedures (Postgres 11+)
FOR obj IN
SELECT
p.proname AS procedure_name,
pg_get_function_identity_arguments(p.oid) AS args
FROM
pg_proc p
JOIN pg_namespace n ON p.pronamespace = n.oid
WHERE
n.nspname = p_schema
AND p.prokind = 'p' -- p = procedure
LOOP
EXECUTE format(
'GRANT EXECUTE ON PROCEDURE %I.%I(%s) TO %I;',
p_schema, obj.procedure_name, obj.args, p_user
);
RAISE NOTICE 'GRANT EXECUTE ON PROCEDURE %.%(%) TO %;', p_schema, obj.procedure_name, obj.args, p_user;
END LOOP;
END;
$function$
-- Function: update_payment_header_posted_fn
CREATE OR REPLACE FUNCTION public.update_payment_header_posted_fn(p_payment_header_id uuid, p_transaction_id bigint)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
UPDATE payment_headers
SET is_posted = true,
transaction_id = p_transaction_id,
modified_on_utc = now()
WHERE id = p_payment_header_id;
END;
$function$
-- Function: get_employee_leave_balance
CREATE OR REPLACE FUNCTION public.get_employee_leave_balance(p_employee_id uuid DEFAULT NULL::uuid)
RETURNS TABLE(id uuid, employee_id uuid, employee_name text, leave_type_id integer, leave_type_name text, total_leaves integer, used_leaves integer, remaining_leaves integer)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT
gen_random_uuid() AS id, -- âś… Auto-generate UUID for each row
elb.employee_id,
CONCAT(e.first_name, ' ', e.last_name) AS employee_name,
elb.leave_type_id,
lt.name::TEXT AS leave_type_name,
COALESCE(elb.total_leaves, 0) AS total_leaves,
COALESCE(elb.used_leaves, 0) AS used_leaves,
COALESCE(elb.total_leaves - elb.used_leaves, 0) AS remaining_leaves
FROM employee_leave_balances elb
JOIN leave_types lt ON elb.leave_type_id = lt.id
JOIN employees e ON elb.employee_id = e.id
WHERE (p_employee_id IS NULL OR elb.employee_id = p_employee_id);
END;
$function$
-- Function: get_holidays_with_user_names
CREATE OR REPLACE FUNCTION public.get_holidays_with_user_names(in_year integer, in_company_id uuid)
RETURNS TABLE(id integer, holiday_date date, name character varying, description text, created_on_utc timestamp without time zone, modified_on_utc timestamp without time zone, deleted_on_utc timestamp without time zone, is_deleted boolean, created_by_id uuid, created_by character varying, modified_by_id uuid, modified_by character varying, company_id uuid)
LANGUAGE plpgsql
STABLE
AS $function$
BEGIN
RETURN QUERY
SELECT
h.id,
h.holiday_date,
h.name,
h.description,
h.created_on_utc,
h.modified_on_utc,
h.deleted_on_utc,
h.is_deleted,
h.created_by AS created_by_id,
(u1.first_name || ' ' || u1.last_name)::varchar AS created_by,
h.modified_by AS modified_by_id,
(u2.first_name || ' ' || u2.last_name)::varchar AS modified_by,
h.company_id
FROM public.holidays h
LEFT JOIN public.users u1 ON h.created_by = u1.id
LEFT JOIN public.users u2 ON h.modified_by = u2.id
WHERE
h.holiday_date >= make_date(in_year, 1, 1)
AND h.holiday_date < make_date(in_year + 1, 1, 1)
AND h.company_id = in_company_id
AND h.is_deleted = FALSE
ORDER BY h.holiday_date;
END;
$function$
-- Function: update_payroll_status
CREATE OR REPLACE FUNCTION public.update_payroll_status(p_company_id uuid, p_payroll_ids uuid[], p_target_status integer, p_modified_by uuid)
RETURNS TABLE(payroll_id uuid, new_status text, error_msg text)
LANGUAGE plpgsql
AS $function$
DECLARE
v_exists boolean;
v_current_status int;
v_allowed boolean;
BEGIN
FOREACH payroll_id IN ARRAY p_payroll_ids LOOP
BEGIN
--------------------------------------------------------
-- 1. Check payroll exists
--------------------------------------------------------
SELECT EXISTS(
SELECT 1
FROM payroll_transaction_headers
WHERE id = payroll_id
AND company_id = p_company_id
) INTO v_exists;
IF NOT v_exists THEN
RETURN QUERY SELECT payroll_id, NULL, 'Payroll not found';
CONTINUE;
END IF;
--------------------------------------------------------
-- 2. Get current status
--------------------------------------------------------
SELECT status_id INTO v_current_status
FROM payroll_transaction_headers
WHERE id = payroll_id;
--------------------------------------------------------
-- 3. Validate transition from workflow table
--------------------------------------------------------
SELECT EXISTS(
SELECT 1
FROM payroll_workflow
WHERE company_id = p_company_id
AND status = v_current_status
AND next_status = p_target_status
AND is_enabled = TRUE
) INTO v_allowed;
IF NOT v_allowed THEN
RETURN QUERY
SELECT payroll_id, NULL,
format('Transition not allowed: %s → %s', v_current_status, p_target_status);
CONTINUE;
END IF;
--------------------------------------------------------
-- 4. Update status
--------------------------------------------------------
UPDATE payroll_transaction_headers
SET status_id = p_target_status,
modified_by = p_modified_by,
modified_on_utc = NOW()
WHERE id = payroll_id;
--------------------------------------------------------
-- 5. Return readable status name
--------------------------------------------------------
RETURN QUERY
SELECT
payroll_id,
CASE p_target_status
WHEN 1 THEN 'DRAFT'
WHEN 2 THEN 'SUBMITTED'
WHEN 3 THEN 'APPROVED'
WHEN 4 THEN 'PAID'
WHEN 5 THEN 'REJECTED'
WHEN 6 THEN 'CANCELLED'
ELSE 'UNKNOWN'
END,
NULL;
EXCEPTION WHEN OTHERS THEN
RETURN QUERY SELECT payroll_id, NULL, 'Error: ' || SQLERRM;
END;
END LOOP;
END;
$function$
-- Procedure: delete_payroll_and_payment_data
CREATE OR REPLACE PROCEDURE public.delete_payroll_and_payment_data(IN p_company_id uuid)
LANGUAGE plpgsql
AS $procedure$
BEGIN
-- Delete payment details associated with payment headers of the given company
DELETE FROM public.payment_details
WHERE payment_header_id IN (
SELECT id FROM public.payment_headers WHERE company_id = p_company_id
);
-- Delete payment headers for the given company
DELETE FROM public.payment_headers
WHERE company_id = p_company_id;
-- Delete payroll transaction details associated with payroll transaction headers of the given company
DELETE FROM public.payroll_transaction_details
WHERE payroll_transaction_id IN (
SELECT id FROM public.payroll_transaction_headers WHERE company_id = p_company_id
);
-- Delete payroll transaction headers for the given company
DELETE FROM public.payroll_transaction_headers
WHERE company_id = p_company_id;
-- Commit transaction
COMMIT;
END;
$procedure$
-- Procedure: clean_up_org_payroll
CREATE OR REPLACE PROCEDURE public.clean_up_org_payroll(IN p_organization_id uuid)
LANGUAGE plpgsql
AS $procedure$
DECLARE
v_company_id uuid;
v_employee_id uuid;
v_payroll_configuration_header_id uuid;
v_payroll_transaction_header_id uuid;
v_payment_header_id uuid;
v_timesheet_id uuid;
v_timesheet_line_id uuid;
BEGIN
FOR v_company_id IN SELECT id FROM companies WHERE organization_id = p_organization_id LOOP
-- Employees and all related tables
FOR v_employee_id IN SELECT id FROM employees WHERE company_id = v_company_id LOOP
DELETE FROM benefits WHERE employee_id = v_employee_id;
DELETE FROM deductions WHERE employee_id = v_employee_id;
DELETE FROM loans WHERE employee_id = v_employee_id;
DELETE FROM advance_payments WHERE employee_id = v_employee_id;
DELETE FROM employee_bank_accounts WHERE employee_id = v_employee_id;
DELETE FROM employee_upis WHERE employee_id = v_employee_id;
DELETE FROM leave_applications WHERE employee_id = v_employee_id;
DELETE FROM leave_entitlements WHERE employee_id = v_employee_id;
DELETE FROM attendance WHERE employee_id = v_employee_id;
DELETE FROM payroll_transaction_headers WHERE employee_id = v_employee_id;
DELETE FROM payroll_configuration_headers WHERE employee_id = v_employee_id;
END LOOP;
-- Payroll configuration headers/details
FOR v_payroll_configuration_header_id IN
SELECT id FROM payroll_configuration_headers WHERE company_id = v_company_id
LOOP
DELETE FROM payroll_configuration_details WHERE configuration_header_id = v_payroll_configuration_header_id;
END LOOP;
DELETE FROM payroll_configuration_headers WHERE company_id = v_company_id;
-- Payroll transaction headers/details
FOR v_payroll_transaction_header_id IN
SELECT id FROM payroll_transaction_headers WHERE company_id = v_company_id
LOOP
DELETE FROM payroll_transaction_details WHERE payroll_transaction_id = v_payroll_transaction_header_id;
END LOOP;
DELETE FROM payroll_transaction_headers WHERE company_id = v_company_id;
-- Payment headers/details
FOR v_payment_header_id IN
SELECT id FROM payment_headers WHERE company_id = v_company_id
LOOP
DELETE FROM payment_details WHERE payment_header_id = v_payment_header_id;
END LOOP;
DELETE FROM payment_headers WHERE company_id = v_company_id;
DELETE FROM payment_header_ids WHERE company_id = v_company_id;
DELETE FROM payroll_transaction_header_ids WHERE company_id = v_company_id;
-- Leave/workflow/records
DELETE FROM leave_workflow WHERE company_id = v_company_id;
DELETE FROM holidays WHERE company_id = v_company_id;
-- Timesheets and details
FOR v_timesheet_id IN SELECT id FROM timesheets WHERE company_id = v_company_id LOOP
FOR v_timesheet_line_id IN SELECT id FROM timesheet_lines WHERE timesheet_id = v_timesheet_id LOOP
DELETE FROM timesheet_hours WHERE timesheet_line_id = v_timesheet_line_id;
END LOOP;
DELETE FROM timesheet_lines WHERE timesheet_id = v_timesheet_id;
END LOOP;
DELETE FROM timesheets WHERE company_id = v_company_id;
-- Tasks/Projects
DELETE FROM tasks WHERE company_id = v_company_id;
DELETE FROM projects WHERE company_id = v_company_id;
-- Workdays
DELETE FROM workdays WHERE company_id = v_company_id;
-- Users
DELETE FROM users WHERE company_id = v_company_id;
-- Employees (must come after all dependent deletes)
DELETE FROM employees WHERE company_id = v_company_id;
-- Companies
DELETE FROM companies WHERE id = v_company_id;
RAISE NOTICE 'Deleted payroll data for company_id %', v_company_id;
END LOOP;
-- Organization
DELETE FROM organizations WHERE id = p_organization_id;
RAISE NOTICE 'Deleted payroll data for organization_id %', p_organization_id;
END;
$procedure$
-- Procedure: update_payment_header_posted
CREATE OR REPLACE PROCEDURE public.update_payment_header_posted(IN p_payment_header_id uuid, IN p_transaction_id bigint)
LANGUAGE plpgsql
AS $procedure$
BEGIN
UPDATE payment_headers
SET is_posted = true,
transaction_id = p_transaction_id,
modified_on_utc = now()
WHERE id = p_payment_header_id;
END;
$procedure$
-- Procedure: insert_payment_headers_from_payroll
CREATE OR REPLACE PROCEDURE public.insert_payment_headers_from_payroll(IN p_payment_header_id uuid, IN p_company_id uuid, IN p_payment_date timestamp without time zone, IN p_reference text, IN p_credit_account_id uuid, IN p_debit_account_id uuid, IN p_mode_of_payment text, IN p_created_by uuid, IN p_payroll_transaction_ids uuid[])
LANGUAGE plpgsql
AS $procedure$
DECLARE
v_total_paid_amount numeric := 0;
v_serial_number integer := 1;
v_payroll_record RECORD;
v_payment_number varchar;
BEGIN
v_payment_number := get_new_payment_number(
p_company_id,
p_payment_date::date
);
IF array_length(p_payroll_transaction_ids, 1) IS NULL THEN
RAISE NOTICE 'No payroll transaction IDs provided.';
RETURN;
END IF;
-- Insert into payment_headers
INSERT INTO public.payment_headers (
id,
company_id,
paid_date,
paid_amount,
tds_amount,
advance_amount,
description,
credit_account_id,
debit_account_id,
mode_of_payment, -- âś… dynamic
reference,
payment_number,
created_on_utc,
created_by,
is_deleted
)
VALUES (
p_payment_header_id,
p_company_id,
p_payment_date,
0,
0,
0,
'Payment of salary',
p_credit_account_id,
p_debit_account_id,
p_mode_of_payment, -- âś… using parameter
p_reference,
v_payment_number,
NOW(),
p_created_by,
false
);
-- Insert payment details + update payroll transactions
FOR v_payroll_record IN
SELECT id, employee_id, net_pay
FROM public.payroll_transaction_headers
WHERE id = ANY(p_payroll_transaction_ids)
LOOP
INSERT INTO public.payment_details (
id,
payment_header_id,
employee_id,
paid_amount,
serial_number,
is_deleted
)
VALUES (
gen_random_uuid(),
p_payment_header_id,
v_payroll_record.employee_id,
v_payroll_record.net_pay,
v_serial_number,
false
);
UPDATE public.payroll_transaction_headers
SET status_id = 2
WHERE id = v_payroll_record.id;
v_total_paid_amount := v_total_paid_amount + v_payroll_record.net_pay;
v_serial_number := v_serial_number + 1;
END LOOP;
-- Update total paid amount
UPDATE public.payment_headers
SET paid_amount = v_total_paid_amount
WHERE id = p_payment_header_id;
RAISE NOTICE
'Payment Header % inserted with total paid amount %',
p_payment_header_id,
v_total_paid_amount;
END;
$procedure$