| Type | Name | Status | PK | FK | Columns | Index | Script | Diff Script |
|---|---|---|---|---|---|---|---|---|
| Table | customer_products | Missing in Target |
|
|||||
| Table | product_group | Missing in Target |
|
|||||
| Table | orders | Missing in Target |
|
|||||
| Table | roles | Missing in Target |
|
|||||
| Table | product_tax_category_details | Missing in Target |
|
|||||
| Table | schema_versions | Missing in Target |
|
|||||
| Table | products | Missing in Target |
|
|||||
| Table | companies | Missing in Target |
|
|||||
| Table | company_categories | Missing in Target |
|
|||||
| Table | order_details | Missing in Target |
|
|||||
| Table | units | Missing in Target |
|
|||||
| Table | organizations | Missing in Target |
|
|||||
| Table | product_tax_categories | Missing in Target |
|
|||||
| Table | product_warehouse_quantities | Missing in Target |
|
|||||
| Table | company_products | Missing in Target |
|
|||||
| Table | gate_pass_logs | Missing in Target |
|
|||||
| Table | __EFMigrationsHistory | Missing in Target |
|
|||||
| Table | users | Missing in Target |
|
|||||
| Table | categories | Missing in Target |
|
|||||
| Table | default_apartment_products | Missing in Target |
|
|||||
| Table | vendor_products | Missing in Target |
|
|||||
| Table | stock_adjustment_headers | Missing in Target |
|
|||||
| Table | stock_adjustment_details | Missing in Target |
|
|||||
| Table | stock_adjustment_statuses | Missing in Target |
|
|||||
| Table | stock_ledgers | Missing in Target |
|
|||||
| Table | stock_movement_types | Missing in Target |
|
|||||
| Table | stock_reservation_statuses | Missing in Target |
|
|||||
| Table | stock_reservations | Missing in Target |
|
|||||
| Table | stock_transfer_details | Missing in Target |
|
|||||
| Table | stock_transfer_headers | Missing in Target |
|
|||||
| Table | stock_transfer_statuses | Missing in Target |
|
|||||
| 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 | pg_get_tabledef | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.pg_get_tabledef(p_table_name text)
2
RETURNS text
3
LANGUAGE plpgsql
4
AS $function$
5
DECLARE
6
col RECORD;
7
col_defs TEXT := '';
8
pk_cols TEXT := '';
9
result TEXT;
10
BEGIN
11
FOR col IN
12
SELECT
13
column_name,
14
data_type,
15
character_maximum_length,
16
numeric_precision,
17
numeric_scale,
18
is_nullable,
19
column_default
20
FROM information_schema.columns
21
WHERE table_schema = 'public' AND table_name = p_table_name
22
ORDER BY ordinal_position
23
LOOP
24
col_defs := col_defs ||
25
format('"%s" %s%s%s%s, ',
26
col.column_name,
27
CASE
28
WHEN col.data_type = 'character varying' THEN format('varchar(%s)', col.character_maximum_length)
29
WHEN col.data_type = 'numeric' THEN format('numeric(%s,%s)', col.numeric_precision, col.numeric_scale)
30
ELSE col.data_type
31
END,
32
CASE WHEN col.column_default IS NOT NULL THEN ' DEFAULT ' || col.column_default ELSE '' END,
33
CASE WHEN col.is_nullable = 'NO' THEN ' NOT NULL' ELSE '' END,
34
''
35
);
36
END LOOP;
37
38
-- Get primary key columns
39
SELECT string_agg(format('"%s"', kcu.column_name), ', ')
40
INTO pk_cols
41
FROM information_schema.table_constraints tc
42
JOIN information_schema.key_column_usage kcu
43
ON tc.constraint_name = kcu.constraint_name
44
WHERE tc.table_schema = 'public'
45
AND tc.table_name = p_table_name
46
AND tc.constraint_type = 'PRIMARY KEY';
47
48
IF pk_cols IS NOT NULL THEN
49
col_defs := col_defs || format('PRIMARY KEY (%s), ', pk_cols);
50
END IF;
51
52
col_defs := left(col_defs, length(col_defs) - 2);
53
result := format('CREATE TABLE "%s" (%s);', p_table_name, col_defs);
54
RETURN result;
55
END;
56
$function$
|
|||||
| Function | get_all_products | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.get_all_products(p_company_id uuid, p_product_group_id integer)
2
RETURNS TABLE(product_id uuid, product_group_id integer, product_group_name text, product_name text, unit_id uuid, unit_name text, category_id uuid, category_name text, description text, hsn_code text, sac text, purchase_price numeric, selling_price numeric, opening_stock numeric, minimum_stock numeric, vendor_id uuid, vendor_name text, tax_category_id integer, tax_category_name text, maximum_stock numeric, cgst_rate numeric, sgst_rate numeric, igst_rate numeric)
3
LANGUAGE plpgsql
4
AS $function$
5
BEGIN
6
RETURN QUERY
7
SELECT
8
p.id AS product_id,
9
p.product_group_id,
10
pg.name::TEXT AS product_group_name,
11
p.product_name::TEXT,
12
p.unit_id,
13
COALESCE(u.unit_name, '')::TEXT AS unit_name,
14
p.category_id,
15
COALESCE(c.category_name, '')::TEXT AS category_name,
16
p.description::TEXT,
17
p.hsn_code::TEXT,
18
p.sac::TEXT,
19
p.purchase_price,
20
p.selling_price,
21
p.opening_stock,
22
p.minimum_stock,
23
p.vendor_id,
24
NULL::TEXT AS vendor_name, -- Replace with actual vendor join if needed
25
p.tax_category_id,
26
COALESCE(ptc.name, '')::TEXT AS tax_category_name,
27
p.maximum_stock,
28
ptcd.c_gst_rate,
29
ptcd.s_gst_rate,
30
ptcd.i_gst_rate
31
FROM products p
32
INNER JOIN company_products cp ON p.id = cp.product_id
33
LEFT JOIN categories c ON p.category_id = c.id
34
LEFT JOIN product_tax_categories ptc ON p.tax_category_id = ptc.id
35
LEFT JOIN product_tax_category_details ptcd ON ptc.id = ptcd.producttaxcategory_id
36
INNER JOIN product_group pg ON p.product_group_id = pg.id
37
LEFT JOIN units u ON p.unit_id = u.id
38
WHERE cp.company_id = p_company_id
39
AND p.product_group_id = p_product_group_id
40
AND (cp.is_deleted IS FALSE OR cp.is_deleted IS NULL)
41
AND (p.is_deleted IS FALSE OR p.is_deleted IS NULL);
42
END;
43
$function$
|
|||||
| Function | get_products_by_company_and_group | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.get_products_by_company_and_group(in_company_id uuid, in_product_group_id integer)
2
RETURNS TABLE(id uuid, product_group_id integer, product_name text, unit_id uuid, unit_name text, category_id uuid, category_name text, description text, hsn_code text, sac text, purchase_price numeric, selling_price numeric, opening_stock numeric, minimum_stock numeric, vendor_id uuid, tax_category_id uuid, maximum_stock numeric, cgst_rate numeric, sgst_rate numeric, igst_rate numeric)
3
LANGUAGE plpgsql
4
AS $function$
5
BEGIN
6
RETURN QUERY
7
SELECT
8
p.id,
9
p.product_group_id,
10
p.product_name::TEXT,
11
p.unit_id,
12
u.unit_name::TEXT,
13
p.category_id,
14
c.category_name::TEXT,
15
p.description::TEXT,
16
p.hsn_code::TEXT,
17
p.sac::TEXT,
18
p.purchase_price,
19
p.selling_price,
20
p.opening_stock,
21
p.minimum_stock,
22
p.vendor_id,
23
p.tax_category_id,
24
p.maximum_stock,
25
ptcd.c_gst_rate,
26
ptcd.s_gst_rate,
27
ptcd.i_gst_rate
28
FROM products p
29
INNER JOIN company_products cp ON p.id = cp.product_id
30
LEFT JOIN categories c ON p.category_id = c.id
31
LEFT JOIN product_tax_categories ptc ON p.tax_category_id = ptc.id
32
LEFT JOIN product_tax_category_details ptcd ON ptc.id = ptcd.producttaxcategory_id
33
INNER JOIN product_group pg ON p.product_group_id = pg.id
34
LEFT JOIN units u ON u.id = p.unit_id
35
WHERE cp.company_id = in_company_id
36
AND p.product_group_id = in_product_group_id;
37
END;
38
$function$
|
|||||
| Function | get_all_stock_adjustments_from_span | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.get_all_stock_adjustments_from_span(p_company_id uuid, p_user_id uuid, p_warehouse_id uuid DEFAULT NULL::uuid, p_start_date timestamp without time zone DEFAULT NULL::timestamp without time zone, p_end_date timestamp without time zone DEFAULT NULL::timestamp without time zone)
2
RETURNS TABLE(id uuid, organization_id uuid, company_id uuid, warehouse_id uuid, stock_adjustment_status text, stock_adjustment_status_id integer, reason_code text, stock_adjustment_number text, remarks text, adjustment_date date, created_by uuid, created_on_utc timestamp without time zone, modified_by uuid, modified_on_utc timestamp without time zone, created_by_name text, modified_by_name text, line_count integer, total_difference numeric)
3
LANGUAGE plpgsql
4
AS $function$
5
DECLARE
6
v_start_date date := COALESCE(p_start_date::date, '1970-01-01'::date);
7
v_end_date date := COALESCE(p_end_date::date, '9999-12-31'::date);
8
BEGIN
9
RETURN QUERY
10
WITH headers AS (
11
SELECT h.*
12
FROM public.stock_adjustment_headers h
13
WHERE h.company_id = p_company_id
14
AND h.is_deleted = false
15
AND (p_warehouse_id IS NULL OR p_warehouse_id = '00000000-0000-0000-0000-000000000000'::uuid OR h.warehouse_id = p_warehouse_id)
16
AND (h.adjustment_date_utc::date BETWEEN v_start_date AND v_end_date)
17
), line_agg AS (
18
SELECT
19
sd.stock_adjustment_header_id,
20
COUNT(*)::int AS line_count,
21
SUM(sd.difference_quantity)::numeric AS total_difference
22
FROM public.stock_adjustment_details sd
23
WHERE sd.is_deleted = false
24
GROUP BY sd.stock_adjustment_header_id
25
)
26
SELECT
27
h.id,
28
h.organization_id,
29
h.company_id,
30
h.warehouse_id,
31
COALESCE(s.name, '') AS stock_adjustment_status,
32
h.stock_adjustment_status_id,
33
h.reason_code,
34
h.stock_adjustment_number,
35
h.remarks,
36
h.adjustment_date_utc::date AS adjustment_date,
37
h.created_by,
38
h.created_on_utc,
39
h.modified_by,
40
h.modified_on_utc,
41
CONCAT(cu.first_name, ' ', cu.last_name)::text AS created_by_name,
42
CONCAT(mu.first_name, ' ', mu.last_name)::text AS modified_by_name,
43
COALESCE(la.line_count, 0) AS line_count,
44
COALESCE(la.total_difference, 0.0) AS total_difference
45
FROM headers h
46
LEFT JOIN public.stock_adjustment_statuses s ON s.id = h.stock_adjustment_status_id
47
LEFT JOIN public.users cu ON cu.id = h.created_by
48
LEFT JOIN public.users mu ON mu.id = h.modified_by
49
LEFT JOIN line_agg la ON la.stock_adjustment_header_id = h.id;
50
END;
51
$function$
|
|||||
| Procedure | hard_delete_inventory_data | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE PROCEDURE public.hard_delete_inventory_data(IN p_company_id uuid)
2
LANGUAGE plpgsql
3
AS $procedure$
4
BEGIN
5
-- Delete from dependent tables first (if foreign key constraints exist)
6
7
-- Orders (assuming these are transactional)
8
DELETE FROM orders WHERE company_id = p_company_id;
9
10
-- Inventory master data
11
DELETE FROM company_products WHERE company_id = p_company_id;
12
DELETE FROM company_categories WHERE company_id = p_company_id;
13
DELETE FROM product_tax_categories WHERE company_id = p_company_id;
14
15
-- Units and Users
16
DELETE FROM units WHERE company_id = p_company_id;
17
DELETE FROM users WHERE company_id = p_company_id;
18
19
RAISE NOTICE 'Inventory data for company_id % has been hard deleted.', p_company_id;
20
END;
21
$procedure$
|
|||||
| Procedure | hard_delete_org_inventory | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE PROCEDURE public.hard_delete_org_inventory(IN p_organization_id uuid)
2
LANGUAGE plpgsql
3
AS $procedure$
4
DECLARE
5
v_company_id uuid;
6
BEGIN
7
FOR v_company_id IN SELECT id FROM companies WHERE organization_id = p_organization_id LOOP
8
DELETE FROM orders WHERE company_id = v_company_id;
9
DELETE FROM company_products WHERE company_id = v_company_id;
10
DELETE FROM company_categories WHERE company_id = v_company_id;
11
DELETE FROM product_tax_categories WHERE company_id = v_company_id;
12
DELETE FROM units WHERE company_id = v_company_id;
13
DELETE FROM users WHERE company_id = v_company_id;
14
DELETE FROM companies WHERE id = v_company_id;
15
END LOOP;
16
17
DELETE FROM organizations WHERE id = p_organization_id;
18
19
RAISE NOTICE 'Deleted inventory data for organization_id: %', p_organization_id;
20
END;
21
$procedure$
|
|||||
| Procedure | initialize_company | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE PROCEDURE public.initialize_company(IN p_company_id uuid, IN p_organization_id uuid, IN p_is_apartment boolean, IN p_name text, IN p_created_by uuid, IN p_default_company_id uuid)
2
LANGUAGE plpgsql
3
AS $procedure$
4
DECLARE
5
v_company_exists boolean;
6
v_is_apartment boolean;
7
v_products_exist boolean;
8
v_categories_exist boolean;
9
BEGIN
10
-- Check if company already exists
11
SELECT EXISTS (
12
SELECT 1 FROM public.companies
13
WHERE id = p_company_id
14
) INTO v_company_exists;
15
16
IF NOT v_company_exists THEN
17
-- Insert into companies table
18
INSERT INTO public.companies (
19
id,
20
organization_id,
21
is_apartment,
22
name,
23
created_on_utc,
24
created_by
25
) VALUES (
26
p_company_id,
27
p_organization_id,
28
p_is_apartment,
29
p_name,
30
NOW(),
31
p_created_by
32
);
33
RAISE NOTICE 'Created company % (ID: %) for organization %',
34
p_name, p_company_id, p_organization_id;
35
ELSE
36
RAISE NOTICE 'Company % (ID: %) already exists for organization %. Skipping initialization.',
37
p_name, p_company_id, p_organization_id;
38
END IF;
39
40
-- Check if the company is an apartment
41
IF p_is_apartment THEN
42
-- Check if products already exist for this company
43
SELECT EXISTS (
44
SELECT 1 FROM public.company_products
45
WHERE company_id = p_company_id
46
) INTO v_products_exist;
47
48
IF NOT v_products_exist THEN
49
-- Insert into company_products
50
INSERT INTO public.company_products(
51
id,
52
company_id,
53
product_id,
54
created_on_utc,
55
created_by
56
)
57
SELECT
58
gen_random_uuid(),
59
p_company_id,
60
product_id,
61
NOW(),
62
p_created_by
63
FROM public.default_apartment_products;
64
65
RAISE NOTICE 'Added default products for apartment company %', p_name;
66
ELSE
67
RAISE NOTICE 'Products already exist for company %. Skipping product initialization.', p_name;
68
END IF;
69
END IF;
70
71
-- Check if categories already exist for this company
72
SELECT EXISTS (
73
SELECT 1 FROM public.company_categories
74
WHERE company_id = p_company_id
75
) INTO v_categories_exist;
76
77
IF NOT v_categories_exist THEN
78
-- Insert into company_categories
79
INSERT INTO public.company_categories (
80
id,
81
company_id,
82
category_id,
83
created_on_utc,
84
created_by
85
)
86
SELECT
87
nextval('company_categories_seq'),
88
p_company_id,
89
category_id,
90
NOW(),
91
p_created_by
92
FROM public.company_categories cc
93
WHERE cc.company_id = p_default_company_id;
94
95
RAISE NOTICE 'Copied categories from default company % to new company %',
96
p_default_company_id, p_company_id;
97
ELSE
98
RAISE NOTICE 'Categories already exist for company %. Skipping category initialization.', p_name;
99
END IF;
100
END;
101
$procedure$
|
|||||
| Procedure | initialize_company_products | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE PROCEDURE public.initialize_company_products(IN old_company_id uuid, IN new_company_id uuid)
2
LANGUAGE plpgsql
3
AS $procedure$
4
BEGIN
5
-- Insert new records for the new company
6
INSERT INTO company_products (
7
id,
8
company_id,
9
product_id,
10
created_on_utc,
11
created_by
12
)
13
SELECT
14
gen_random_uuid(), -- Generate a new UUID for the id column
15
new_company_id, -- Assign the new company ID
16
product_id,
17
created_on_utc,
18
created_by
19
FROM company_products
20
WHERE company_id = old_company_id;
21
22
-- Optional: Log the operation
23
RAISE NOTICE 'Company products have been copied from company % to company %.', old_company_id, new_company_id;
24
25
END;
26
$procedure$
|
|||||
| Procedure | hard_delete_organization | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE PROCEDURE public.hard_delete_organization(IN p_organization_id uuid)
2
LANGUAGE plpgsql
3
AS $procedure$
4
DECLARE
5
p_company_id UUID; -- Variable to hold company IDs associated with the organization
6
BEGIN
7
-- Get the associated company IDs for the organization
8
FOR p_company_id IN
9
SELECT c.id FROM public.companies c WHERE c.organization_id = p_organization_id
10
LOOP
11
-- Delete company-specific products
12
DELETE FROM public.company_products
13
WHERE company_id = p_company_id;
14
15
-- Delete company-specific categories
16
DELETE FROM public.company_categories
17
WHERE company_id = p_company_id;
18
19
-- Delete users associated with the company
20
DELETE FROM public.users
21
WHERE users.company_id = p_company_id;
22
23
-- Delete from companies
24
DELETE FROM public.companies
25
WHERE id = p_company_id;
26
END LOOP;
27
28
-- Delete the organization itself
29
DELETE FROM public.organizations
30
WHERE public.organizations.id = p_organization_id;
31
32
-- Log the operation
33
RAISE NOTICE 'Organization with ID % and all related data have been hard deleted.', p_organization_id;
34
35
END;
36
$procedure$
|
|||||
| Procedure | initialize_organization | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE PROCEDURE public.initialize_organization(IN p_id uuid, IN p_name text, IN p_company_ids text, IN p_company_names text, IN p_user_id uuid, IN p_user_first_name character varying, IN p_user_last_name character varying, IN p_phone_number character varying, IN p_email character varying, IN p_created_by uuid, IN p_default_company_id uuid)
2
LANGUAGE plpgsql
3
AS $procedure$
4
DECLARE
5
v_company_id uuid;
6
v_company_name text;
7
v_company_ids uuid[];
8
v_company_names text[];
9
i integer;
10
v_organization_exists boolean;
11
v_user_exists boolean;
12
v_company_exists boolean;
13
v_existing_user_id uuid;
14
v_update_count integer;
15
BEGIN
16
17
SELECT EXISTS (
18
SELECT 1 FROM public.organizations
19
WHERE id = p_id
20
) INTO v_organization_exists;
21
22
IF v_organization_exists THEN
23
RAISE NOTICE 'Organization with ID % already exists. Skipping organization creation.', p_id;
24
ELSE
25
-- Insert organization if it doesn't exist
26
INSERT INTO public.organizations (
27
id, name, created_on_utc, created_by, is_deleted
28
) VALUES (
29
p_id, p_name, NOW(), p_created_by, false );
30
RAISE NOTICE 'Initialized organization: % with ID: %', p_name, p_id;
31
END IF;
32
33
-- Parse company IDs and names
34
v_company_ids := string_to_array(p_company_ids, ',');
35
v_company_names := string_to_array(p_company_names, ',');
36
37
-- Initialize companies with duplicate checks
38
FOR i IN 1..array_length(v_company_ids, 1) LOOP
39
v_company_id := v_company_ids[i];
40
v_company_name := v_company_names[i];
41
42
CALL public.initialize_company(
43
v_company_id,
44
p_id,
45
true, -- Indicates it's an apartment
46
v_company_name,
47
p_created_by,
48
p_default_company_id
49
);
50
END LOOP;
51
52
-- Assign the first company ID from the array
53
v_company_id := v_company_ids[1];
54
55
-- Check if user already exists (by both ID and email)
56
SELECT id INTO v_existing_user_id FROM public.users
57
WHERE id = p_user_id OR email = p_email
58
LIMIT 1;
59
60
v_user_exists := (v_existing_user_id IS NOT NULL);
61
62
IF NOT v_user_exists THEN
63
INSERT INTO public.users (
64
id, first_name, last_name, email,
65
created_on_utc, created_by, password_hash,
66
phone_number, company_id
67
) VALUES (
68
p_user_id, p_user_first_name, p_user_last_name, p_email,
69
NOW(), p_created_by, '',
70
p_phone_number, v_company_id
71
);
72
RAISE NOTICE 'New user created with ID: % and email: %', p_user_id, p_email;
73
ELSE
74
UPDATE public.users
75
SET
76
company_id = v_company_id,
77
first_name = COALESCE(p_user_first_name, first_name),
78
last_name = COALESCE(p_user_last_name, last_name),
79
phone_number = COALESCE(p_phone_number, phone_number),
80
email = COALESCE(p_email, email)
81
WHERE id = v_existing_user_id;
82
83
GET DIAGNOSTICS v_update_count = ROW_COUNT;
84
85
IF v_update_count > 0 THEN
86
RAISE NOTICE 'Updated existing user % (ID: %) with new company association %',
87
p_email, v_existing_user_id, v_company_id;
88
ELSE
89
RAISE NOTICE 'User % (ID: %) already associated with company %. No changes made.',
90
p_email, v_existing_user_id, v_company_id;
91
END IF;
92
END IF;
93
94
RAISE NOTICE 'Organization initialization process completed for % (ID: %)', p_name, p_id;
95
END;
96
$procedure$
|
|||||
| Procedure | purge_inventory_organization_data | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE PROCEDURE public.purge_inventory_organization_data(IN p_organization_ids uuid[] DEFAULT NULL::uuid[])
2
LANGUAGE plpgsql
3
AS $procedure$
4
DECLARE
5
-- Retention policy
6
c_retention_hours integer := 24;
7
v_cutoff_24h timestamp := now() - make_interval(hours => GREATEST(c_retention_hours, 1));
8
9
-- Targets
10
v_orgs uuid[];
11
v_companies uuid[] := '{}';
12
13
-- Orders
14
v_order_ids uuid[] := '{}';
15
v_order_detail_ids uuid[] := '{}';
16
17
-- Company-scoped master/config
18
v_company_product_ids uuid[] := '{}';
19
v_company_category_ids int[] := '{}';
20
v_ptc_ids int[] := '{}'; -- product_tax_categories.id
21
v_ptcd_ids int[] := '{}'; -- product_tax_category_details.id
22
v_unit_ids uuid[] := '{}';
23
24
-- Inventory quantities tied to company products
25
v_pwq_ids int[] := '{}'; -- product_warehouse_quantities.id
26
BEGIN
27
START TRANSACTION;
28
29
--------------------------------------------------------------------
30
-- 1) Resolve target organizations and companies (uniform block)
31
--------------------------------------------------------------------
32
IF p_organization_ids IS NULL OR array_length(p_organization_ids, 1) IS NULL THEN
33
SELECT array_agg(id)
34
INTO v_orgs
35
FROM organizations
36
WHERE created_on_utc > '2025-05-23'
37
AND created_on_utc < (NOW() - interval '24 hours');
38
ELSE
39
v_orgs := p_organization_ids;
40
END IF;
41
42
IF v_orgs IS NULL OR array_length(v_orgs, 1) IS NULL THEN
43
RAISE NOTICE 'No organizations found for cleanup.';
44
COMMIT;
45
RETURN;
46
END IF;
47
48
-- Companies by organization
49
SELECT COALESCE(array_agg(id), '{}')
50
INTO v_companies
51
FROM companies
52
WHERE organization_id = ANY(v_orgs);
53
54
IF v_companies IS NULL OR array_length(v_companies, 1) IS NULL THEN
55
RAISE NOTICE 'No companies resolved for inventory purge. Orgs: %', v_orgs;
56
COMMIT;
57
RETURN;
58
END IF;
59
60
RAISE NOTICE 'Inventory purge targets - Organizations: %; Companies: %', v_orgs, v_companies;
61
62
--------------------------------------------------------------------
63
-- 2) Collect IDs (COALESCE to '{}' to avoid NULL-array issues)
64
--------------------------------------------------------------------
65
-- Orders and details (time-gated)
66
SELECT COALESCE(array_agg(id), '{}')
67
INTO v_order_ids
68
FROM orders
69
WHERE company_id = ANY(v_companies)
70
AND created_on_utc < v_cutoff_24h;
71
72
SELECT COALESCE(array_agg(id), '{}')
73
INTO v_order_detail_ids
74
FROM order_details
75
WHERE order_id = ANY(v_order_ids);
76
77
-- Company products and categories
78
SELECT COALESCE(array_agg(id), '{}')
79
INTO v_company_product_ids
80
FROM company_products
81
WHERE company_id = ANY(v_companies);
82
83
SELECT COALESCE(array_agg(id), '{}')
84
INTO v_company_category_ids
85
FROM company_categories
86
WHERE company_id = ANY(v_companies);
87
88
-- Product tax categories (company-scoped) and their details
89
SELECT COALESCE(array_agg(id), '{}')
90
INTO v_ptc_ids
91
FROM product_tax_categories
92
WHERE company_id = ANY(v_companies);
93
94
SELECT COALESCE(array_agg(id), '{}')
95
INTO v_ptcd_ids
96
FROM product_tax_category_details
97
WHERE producttaxcategory_id = ANY(v_ptc_ids);
98
99
-- Units (company-scoped)
100
SELECT COALESCE(array_agg(id), '{}')
101
INTO v_unit_ids
102
FROM units
103
WHERE company_id = ANY(v_companies);
104
105
-- Product warehouse quantities for products linked to these companies
106
SELECT COALESCE(array_agg(pwq.id), '{}')
107
INTO v_pwq_ids
108
FROM product_warehouse_quantities pwq
109
WHERE pwq.product_id IN (
110
SELECT DISTINCT cp.product_id
111
FROM company_products cp
112
WHERE cp.company_id = ANY(v_companies)
113
);
114
115
--------------------------------------------------------------------
116
-- 3) Purge in strict child → parent order (avoid FK violations)
117
--------------------------------------------------------------------
118
-- Orders: details → headers
119
DELETE FROM order_details
120
WHERE id = ANY(v_order_detail_ids);
121
122
DELETE FROM orders
123
WHERE id = ANY(v_order_ids);
124
125
-- Inventory quantities tied to purged company-product assignments
126
DELETE FROM product_warehouse_quantities
127
WHERE id = ANY(v_pwq_ids);
128
129
-- Product tax category details → categories
130
DELETE FROM product_tax_category_details
131
WHERE id = ANY(v_ptcd_ids);
132
133
DELETE FROM product_tax_categories
134
WHERE id = ANY(v_ptc_ids);
135
136
-- Company-scoped assignments
137
DELETE FROM company_products
138
WHERE id = ANY(v_company_product_ids);
139
140
DELETE FROM company_categories
141
WHERE id = ANY(v_company_category_ids);
142
143
-- Company-scoped units
144
DELETE FROM units
145
WHERE id = ANY(v_unit_ids);
146
147
RAISE NOTICE 'Inventory purge complete for companies: % (orgs: %).', v_companies, v_orgs;
148
149
COMMIT;
150
151
EXCEPTION
152
WHEN OTHERS THEN
153
ROLLBACK;
154
RAISE NOTICE 'purge_inventory_organization_data failed: %', SQLERRM;
155
-- Optionally rethrow
156
-- RAISE;
157
END;
158
$procedure$
|
-- Table: customer_products
Exists in source, missing in target
-- Table: product_group
Exists in source, missing in target
-- Table: orders
Exists in source, missing in target
-- Table: roles
Exists in source, missing in target
-- Table: product_tax_category_details
Exists in source, missing in target
-- Table: schema_versions
Exists in source, missing in target
-- Table: products
Exists in source, missing in target
-- Table: companies
Exists in source, missing in target
-- Table: company_categories
Exists in source, missing in target
-- Table: order_details
Exists in source, missing in target
-- Table: units
Exists in source, missing in target
-- Table: organizations
Exists in source, missing in target
-- Table: product_tax_categories
Exists in source, missing in target
-- Table: product_warehouse_quantities
Exists in source, missing in target
-- Table: company_products
Exists in source, missing in target
-- Table: gate_pass_logs
Exists in source, missing in target
-- Table: __EFMigrationsHistory
Exists in source, missing in target
-- Table: users
Exists in source, missing in target
-- Table: categories
Exists in source, missing in target
-- Table: default_apartment_products
Exists in source, missing in target
-- Table: vendor_products
Exists in source, missing in target
-- Table: stock_adjustment_headers
Exists in source, missing in target
-- Table: stock_adjustment_details
Exists in source, missing in target
-- Table: stock_adjustment_statuses
Exists in source, missing in target
-- Table: stock_ledgers
Exists in source, missing in target
-- Table: stock_movement_types
Exists in source, missing in target
-- Table: stock_reservation_statuses
Exists in source, missing in target
-- Table: stock_reservations
Exists in source, missing in target
-- Table: stock_transfer_details
Exists in source, missing in target
-- Table: stock_transfer_headers
Exists in source, missing in target
-- Table: stock_transfer_statuses
Exists in source, missing in target
-- 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: pg_get_tabledef
CREATE OR REPLACE FUNCTION public.pg_get_tabledef(p_table_name text)
RETURNS text
LANGUAGE plpgsql
AS $function$
DECLARE
col RECORD;
col_defs TEXT := '';
pk_cols TEXT := '';
result TEXT;
BEGIN
FOR col IN
SELECT
column_name,
data_type,
character_maximum_length,
numeric_precision,
numeric_scale,
is_nullable,
column_default
FROM information_schema.columns
WHERE table_schema = 'public' AND table_name = p_table_name
ORDER BY ordinal_position
LOOP
col_defs := col_defs ||
format('"%s" %s%s%s%s, ',
col.column_name,
CASE
WHEN col.data_type = 'character varying' THEN format('varchar(%s)', col.character_maximum_length)
WHEN col.data_type = 'numeric' THEN format('numeric(%s,%s)', col.numeric_precision, col.numeric_scale)
ELSE col.data_type
END,
CASE WHEN col.column_default IS NOT NULL THEN ' DEFAULT ' || col.column_default ELSE '' END,
CASE WHEN col.is_nullable = 'NO' THEN ' NOT NULL' ELSE '' END,
''
);
END LOOP;
-- Get primary key columns
SELECT string_agg(format('"%s"', kcu.column_name), ', ')
INTO pk_cols
FROM information_schema.table_constraints tc
JOIN information_schema.key_column_usage kcu
ON tc.constraint_name = kcu.constraint_name
WHERE tc.table_schema = 'public'
AND tc.table_name = p_table_name
AND tc.constraint_type = 'PRIMARY KEY';
IF pk_cols IS NOT NULL THEN
col_defs := col_defs || format('PRIMARY KEY (%s), ', pk_cols);
END IF;
col_defs := left(col_defs, length(col_defs) - 2);
result := format('CREATE TABLE "%s" (%s);', p_table_name, col_defs);
RETURN result;
END;
$function$
-- Function: get_all_products
CREATE OR REPLACE FUNCTION public.get_all_products(p_company_id uuid, p_product_group_id integer)
RETURNS TABLE(product_id uuid, product_group_id integer, product_group_name text, product_name text, unit_id uuid, unit_name text, category_id uuid, category_name text, description text, hsn_code text, sac text, purchase_price numeric, selling_price numeric, opening_stock numeric, minimum_stock numeric, vendor_id uuid, vendor_name text, tax_category_id integer, tax_category_name text, maximum_stock numeric, cgst_rate numeric, sgst_rate numeric, igst_rate numeric)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT
p.id AS product_id,
p.product_group_id,
pg.name::TEXT AS product_group_name,
p.product_name::TEXT,
p.unit_id,
COALESCE(u.unit_name, '')::TEXT AS unit_name,
p.category_id,
COALESCE(c.category_name, '')::TEXT AS category_name,
p.description::TEXT,
p.hsn_code::TEXT,
p.sac::TEXT,
p.purchase_price,
p.selling_price,
p.opening_stock,
p.minimum_stock,
p.vendor_id,
NULL::TEXT AS vendor_name, -- Replace with actual vendor join if needed
p.tax_category_id,
COALESCE(ptc.name, '')::TEXT AS tax_category_name,
p.maximum_stock,
ptcd.c_gst_rate,
ptcd.s_gst_rate,
ptcd.i_gst_rate
FROM products p
INNER JOIN company_products cp ON p.id = cp.product_id
LEFT JOIN categories c ON p.category_id = c.id
LEFT JOIN product_tax_categories ptc ON p.tax_category_id = ptc.id
LEFT JOIN product_tax_category_details ptcd ON ptc.id = ptcd.producttaxcategory_id
INNER JOIN product_group pg ON p.product_group_id = pg.id
LEFT JOIN units u ON p.unit_id = u.id
WHERE cp.company_id = p_company_id
AND p.product_group_id = p_product_group_id
AND (cp.is_deleted IS FALSE OR cp.is_deleted IS NULL)
AND (p.is_deleted IS FALSE OR p.is_deleted IS NULL);
END;
$function$
-- Function: get_products_by_company_and_group
CREATE OR REPLACE FUNCTION public.get_products_by_company_and_group(in_company_id uuid, in_product_group_id integer)
RETURNS TABLE(id uuid, product_group_id integer, product_name text, unit_id uuid, unit_name text, category_id uuid, category_name text, description text, hsn_code text, sac text, purchase_price numeric, selling_price numeric, opening_stock numeric, minimum_stock numeric, vendor_id uuid, tax_category_id uuid, maximum_stock numeric, cgst_rate numeric, sgst_rate numeric, igst_rate numeric)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT
p.id,
p.product_group_id,
p.product_name::TEXT,
p.unit_id,
u.unit_name::TEXT,
p.category_id,
c.category_name::TEXT,
p.description::TEXT,
p.hsn_code::TEXT,
p.sac::TEXT,
p.purchase_price,
p.selling_price,
p.opening_stock,
p.minimum_stock,
p.vendor_id,
p.tax_category_id,
p.maximum_stock,
ptcd.c_gst_rate,
ptcd.s_gst_rate,
ptcd.i_gst_rate
FROM products p
INNER JOIN company_products cp ON p.id = cp.product_id
LEFT JOIN categories c ON p.category_id = c.id
LEFT JOIN product_tax_categories ptc ON p.tax_category_id = ptc.id
LEFT JOIN product_tax_category_details ptcd ON ptc.id = ptcd.producttaxcategory_id
INNER JOIN product_group pg ON p.product_group_id = pg.id
LEFT JOIN units u ON u.id = p.unit_id
WHERE cp.company_id = in_company_id
AND p.product_group_id = in_product_group_id;
END;
$function$
-- Function: get_all_stock_adjustments_from_span
CREATE OR REPLACE FUNCTION public.get_all_stock_adjustments_from_span(p_company_id uuid, p_user_id uuid, p_warehouse_id uuid DEFAULT NULL::uuid, p_start_date timestamp without time zone DEFAULT NULL::timestamp without time zone, p_end_date timestamp without time zone DEFAULT NULL::timestamp without time zone)
RETURNS TABLE(id uuid, organization_id uuid, company_id uuid, warehouse_id uuid, stock_adjustment_status text, stock_adjustment_status_id integer, reason_code text, stock_adjustment_number text, remarks text, adjustment_date date, created_by uuid, created_on_utc timestamp without time zone, modified_by uuid, modified_on_utc timestamp without time zone, created_by_name text, modified_by_name text, line_count integer, total_difference numeric)
LANGUAGE plpgsql
AS $function$
DECLARE
v_start_date date := COALESCE(p_start_date::date, '1970-01-01'::date);
v_end_date date := COALESCE(p_end_date::date, '9999-12-31'::date);
BEGIN
RETURN QUERY
WITH headers AS (
SELECT h.*
FROM public.stock_adjustment_headers h
WHERE h.company_id = p_company_id
AND h.is_deleted = false
AND (p_warehouse_id IS NULL OR p_warehouse_id = '00000000-0000-0000-0000-000000000000'::uuid OR h.warehouse_id = p_warehouse_id)
AND (h.adjustment_date_utc::date BETWEEN v_start_date AND v_end_date)
), line_agg AS (
SELECT
sd.stock_adjustment_header_id,
COUNT(*)::int AS line_count,
SUM(sd.difference_quantity)::numeric AS total_difference
FROM public.stock_adjustment_details sd
WHERE sd.is_deleted = false
GROUP BY sd.stock_adjustment_header_id
)
SELECT
h.id,
h.organization_id,
h.company_id,
h.warehouse_id,
COALESCE(s.name, '') AS stock_adjustment_status,
h.stock_adjustment_status_id,
h.reason_code,
h.stock_adjustment_number,
h.remarks,
h.adjustment_date_utc::date AS adjustment_date,
h.created_by,
h.created_on_utc,
h.modified_by,
h.modified_on_utc,
CONCAT(cu.first_name, ' ', cu.last_name)::text AS created_by_name,
CONCAT(mu.first_name, ' ', mu.last_name)::text AS modified_by_name,
COALESCE(la.line_count, 0) AS line_count,
COALESCE(la.total_difference, 0.0) AS total_difference
FROM headers h
LEFT JOIN public.stock_adjustment_statuses s ON s.id = h.stock_adjustment_status_id
LEFT JOIN public.users cu ON cu.id = h.created_by
LEFT JOIN public.users mu ON mu.id = h.modified_by
LEFT JOIN line_agg la ON la.stock_adjustment_header_id = h.id;
END;
$function$
-- Procedure: hard_delete_inventory_data
CREATE OR REPLACE PROCEDURE public.hard_delete_inventory_data(IN p_company_id uuid)
LANGUAGE plpgsql
AS $procedure$
BEGIN
-- Delete from dependent tables first (if foreign key constraints exist)
-- Orders (assuming these are transactional)
DELETE FROM orders WHERE company_id = p_company_id;
-- Inventory master data
DELETE FROM company_products WHERE company_id = p_company_id;
DELETE FROM company_categories WHERE company_id = p_company_id;
DELETE FROM product_tax_categories WHERE company_id = p_company_id;
-- Units and Users
DELETE FROM units WHERE company_id = p_company_id;
DELETE FROM users WHERE company_id = p_company_id;
RAISE NOTICE 'Inventory data for company_id % has been hard deleted.', p_company_id;
END;
$procedure$
-- Procedure: hard_delete_org_inventory
CREATE OR REPLACE PROCEDURE public.hard_delete_org_inventory(IN p_organization_id uuid)
LANGUAGE plpgsql
AS $procedure$
DECLARE
v_company_id uuid;
BEGIN
FOR v_company_id IN SELECT id FROM companies WHERE organization_id = p_organization_id LOOP
DELETE FROM orders WHERE company_id = v_company_id;
DELETE FROM company_products WHERE company_id = v_company_id;
DELETE FROM company_categories WHERE company_id = v_company_id;
DELETE FROM product_tax_categories WHERE company_id = v_company_id;
DELETE FROM units WHERE company_id = v_company_id;
DELETE FROM users WHERE company_id = v_company_id;
DELETE FROM companies WHERE id = v_company_id;
END LOOP;
DELETE FROM organizations WHERE id = p_organization_id;
RAISE NOTICE 'Deleted inventory data for organization_id: %', p_organization_id;
END;
$procedure$
-- Procedure: initialize_company
CREATE OR REPLACE PROCEDURE public.initialize_company(IN p_company_id uuid, IN p_organization_id uuid, IN p_is_apartment boolean, IN p_name text, IN p_created_by uuid, IN p_default_company_id uuid)
LANGUAGE plpgsql
AS $procedure$
DECLARE
v_company_exists boolean;
v_is_apartment boolean;
v_products_exist boolean;
v_categories_exist boolean;
BEGIN
-- Check if company already exists
SELECT EXISTS (
SELECT 1 FROM public.companies
WHERE id = p_company_id
) INTO v_company_exists;
IF NOT v_company_exists THEN
-- Insert into companies table
INSERT INTO public.companies (
id,
organization_id,
is_apartment,
name,
created_on_utc,
created_by
) VALUES (
p_company_id,
p_organization_id,
p_is_apartment,
p_name,
NOW(),
p_created_by
);
RAISE NOTICE 'Created company % (ID: %) for organization %',
p_name, p_company_id, p_organization_id;
ELSE
RAISE NOTICE 'Company % (ID: %) already exists for organization %. Skipping initialization.',
p_name, p_company_id, p_organization_id;
END IF;
-- Check if the company is an apartment
IF p_is_apartment THEN
-- Check if products already exist for this company
SELECT EXISTS (
SELECT 1 FROM public.company_products
WHERE company_id = p_company_id
) INTO v_products_exist;
IF NOT v_products_exist THEN
-- Insert into company_products
INSERT INTO public.company_products(
id,
company_id,
product_id,
created_on_utc,
created_by
)
SELECT
gen_random_uuid(),
p_company_id,
product_id,
NOW(),
p_created_by
FROM public.default_apartment_products;
RAISE NOTICE 'Added default products for apartment company %', p_name;
ELSE
RAISE NOTICE 'Products already exist for company %. Skipping product initialization.', p_name;
END IF;
END IF;
-- Check if categories already exist for this company
SELECT EXISTS (
SELECT 1 FROM public.company_categories
WHERE company_id = p_company_id
) INTO v_categories_exist;
IF NOT v_categories_exist THEN
-- Insert into company_categories
INSERT INTO public.company_categories (
id,
company_id,
category_id,
created_on_utc,
created_by
)
SELECT
nextval('company_categories_seq'),
p_company_id,
category_id,
NOW(),
p_created_by
FROM public.company_categories cc
WHERE cc.company_id = p_default_company_id;
RAISE NOTICE 'Copied categories from default company % to new company %',
p_default_company_id, p_company_id;
ELSE
RAISE NOTICE 'Categories already exist for company %. Skipping category initialization.', p_name;
END IF;
END;
$procedure$
-- Procedure: initialize_company_products
CREATE OR REPLACE PROCEDURE public.initialize_company_products(IN old_company_id uuid, IN new_company_id uuid)
LANGUAGE plpgsql
AS $procedure$
BEGIN
-- Insert new records for the new company
INSERT INTO company_products (
id,
company_id,
product_id,
created_on_utc,
created_by
)
SELECT
gen_random_uuid(), -- Generate a new UUID for the id column
new_company_id, -- Assign the new company ID
product_id,
created_on_utc,
created_by
FROM company_products
WHERE company_id = old_company_id;
-- Optional: Log the operation
RAISE NOTICE 'Company products have been copied from company % to company %.', old_company_id, new_company_id;
END;
$procedure$
-- Procedure: hard_delete_organization
CREATE OR REPLACE PROCEDURE public.hard_delete_organization(IN p_organization_id uuid)
LANGUAGE plpgsql
AS $procedure$
DECLARE
p_company_id UUID; -- Variable to hold company IDs associated with the organization
BEGIN
-- Get the associated company IDs for the organization
FOR p_company_id IN
SELECT c.id FROM public.companies c WHERE c.organization_id = p_organization_id
LOOP
-- Delete company-specific products
DELETE FROM public.company_products
WHERE company_id = p_company_id;
-- Delete company-specific categories
DELETE FROM public.company_categories
WHERE company_id = p_company_id;
-- Delete users associated with the company
DELETE FROM public.users
WHERE users.company_id = p_company_id;
-- Delete from companies
DELETE FROM public.companies
WHERE id = p_company_id;
END LOOP;
-- Delete the organization itself
DELETE FROM public.organizations
WHERE public.organizations.id = p_organization_id;
-- Log the operation
RAISE NOTICE 'Organization with ID % and all related data have been hard deleted.', p_organization_id;
END;
$procedure$
-- Procedure: initialize_organization
CREATE OR REPLACE PROCEDURE public.initialize_organization(IN p_id uuid, IN p_name text, IN p_company_ids text, IN p_company_names text, IN p_user_id uuid, IN p_user_first_name character varying, IN p_user_last_name character varying, IN p_phone_number character varying, IN p_email character varying, IN p_created_by uuid, IN p_default_company_id uuid)
LANGUAGE plpgsql
AS $procedure$
DECLARE
v_company_id uuid;
v_company_name text;
v_company_ids uuid[];
v_company_names text[];
i integer;
v_organization_exists boolean;
v_user_exists boolean;
v_company_exists boolean;
v_existing_user_id uuid;
v_update_count integer;
BEGIN
SELECT EXISTS (
SELECT 1 FROM public.organizations
WHERE id = p_id
) INTO v_organization_exists;
IF v_organization_exists THEN
RAISE NOTICE 'Organization with ID % already exists. Skipping organization creation.', p_id;
ELSE
-- Insert organization if it doesn't exist
INSERT INTO public.organizations (
id, name, created_on_utc, created_by, is_deleted
) VALUES (
p_id, p_name, NOW(), p_created_by, false );
RAISE NOTICE 'Initialized organization: % with ID: %', p_name, p_id;
END IF;
-- Parse company IDs and names
v_company_ids := string_to_array(p_company_ids, ',');
v_company_names := string_to_array(p_company_names, ',');
-- Initialize companies with duplicate checks
FOR i IN 1..array_length(v_company_ids, 1) LOOP
v_company_id := v_company_ids[i];
v_company_name := v_company_names[i];
CALL public.initialize_company(
v_company_id,
p_id,
true, -- Indicates it's an apartment
v_company_name,
p_created_by,
p_default_company_id
);
END LOOP;
-- Assign the first company ID from the array
v_company_id := v_company_ids[1];
-- Check if user already exists (by both ID and email)
SELECT id INTO v_existing_user_id FROM public.users
WHERE id = p_user_id OR email = p_email
LIMIT 1;
v_user_exists := (v_existing_user_id IS NOT NULL);
IF NOT v_user_exists THEN
INSERT INTO public.users (
id, first_name, last_name, email,
created_on_utc, created_by, password_hash,
phone_number, company_id
) VALUES (
p_user_id, p_user_first_name, p_user_last_name, p_email,
NOW(), p_created_by, '',
p_phone_number, v_company_id
);
RAISE NOTICE 'New user created with ID: % and email: %', p_user_id, p_email;
ELSE
UPDATE public.users
SET
company_id = v_company_id,
first_name = COALESCE(p_user_first_name, first_name),
last_name = COALESCE(p_user_last_name, last_name),
phone_number = COALESCE(p_phone_number, phone_number),
email = COALESCE(p_email, email)
WHERE id = v_existing_user_id;
GET DIAGNOSTICS v_update_count = ROW_COUNT;
IF v_update_count > 0 THEN
RAISE NOTICE 'Updated existing user % (ID: %) with new company association %',
p_email, v_existing_user_id, v_company_id;
ELSE
RAISE NOTICE 'User % (ID: %) already associated with company %. No changes made.',
p_email, v_existing_user_id, v_company_id;
END IF;
END IF;
RAISE NOTICE 'Organization initialization process completed for % (ID: %)', p_name, p_id;
END;
$procedure$
-- Procedure: purge_inventory_organization_data
CREATE OR REPLACE PROCEDURE public.purge_inventory_organization_data(IN p_organization_ids uuid[] DEFAULT NULL::uuid[])
LANGUAGE plpgsql
AS $procedure$
DECLARE
-- Retention policy
c_retention_hours integer := 24;
v_cutoff_24h timestamp := now() - make_interval(hours => GREATEST(c_retention_hours, 1));
-- Targets
v_orgs uuid[];
v_companies uuid[] := '{}';
-- Orders
v_order_ids uuid[] := '{}';
v_order_detail_ids uuid[] := '{}';
-- Company-scoped master/config
v_company_product_ids uuid[] := '{}';
v_company_category_ids int[] := '{}';
v_ptc_ids int[] := '{}'; -- product_tax_categories.id
v_ptcd_ids int[] := '{}'; -- product_tax_category_details.id
v_unit_ids uuid[] := '{}';
-- Inventory quantities tied to company products
v_pwq_ids int[] := '{}'; -- product_warehouse_quantities.id
BEGIN
START TRANSACTION;
--------------------------------------------------------------------
-- 1) Resolve target organizations and companies (uniform block)
--------------------------------------------------------------------
IF p_organization_ids IS NULL OR array_length(p_organization_ids, 1) IS NULL THEN
SELECT array_agg(id)
INTO v_orgs
FROM organizations
WHERE created_on_utc > '2025-05-23'
AND created_on_utc < (NOW() - interval '24 hours');
ELSE
v_orgs := p_organization_ids;
END IF;
IF v_orgs IS NULL OR array_length(v_orgs, 1) IS NULL THEN
RAISE NOTICE 'No organizations found for cleanup.';
COMMIT;
RETURN;
END IF;
-- Companies by organization
SELECT COALESCE(array_agg(id), '{}')
INTO v_companies
FROM companies
WHERE organization_id = ANY(v_orgs);
IF v_companies IS NULL OR array_length(v_companies, 1) IS NULL THEN
RAISE NOTICE 'No companies resolved for inventory purge. Orgs: %', v_orgs;
COMMIT;
RETURN;
END IF;
RAISE NOTICE 'Inventory purge targets - Organizations: %; Companies: %', v_orgs, v_companies;
--------------------------------------------------------------------
-- 2) Collect IDs (COALESCE to '{}' to avoid NULL-array issues)
--------------------------------------------------------------------
-- Orders and details (time-gated)
SELECT COALESCE(array_agg(id), '{}')
INTO v_order_ids
FROM orders
WHERE company_id = ANY(v_companies)
AND created_on_utc < v_cutoff_24h;
SELECT COALESCE(array_agg(id), '{}')
INTO v_order_detail_ids
FROM order_details
WHERE order_id = ANY(v_order_ids);
-- Company products and categories
SELECT COALESCE(array_agg(id), '{}')
INTO v_company_product_ids
FROM company_products
WHERE company_id = ANY(v_companies);
SELECT COALESCE(array_agg(id), '{}')
INTO v_company_category_ids
FROM company_categories
WHERE company_id = ANY(v_companies);
-- Product tax categories (company-scoped) and their details
SELECT COALESCE(array_agg(id), '{}')
INTO v_ptc_ids
FROM product_tax_categories
WHERE company_id = ANY(v_companies);
SELECT COALESCE(array_agg(id), '{}')
INTO v_ptcd_ids
FROM product_tax_category_details
WHERE producttaxcategory_id = ANY(v_ptc_ids);
-- Units (company-scoped)
SELECT COALESCE(array_agg(id), '{}')
INTO v_unit_ids
FROM units
WHERE company_id = ANY(v_companies);
-- Product warehouse quantities for products linked to these companies
SELECT COALESCE(array_agg(pwq.id), '{}')
INTO v_pwq_ids
FROM product_warehouse_quantities pwq
WHERE pwq.product_id IN (
SELECT DISTINCT cp.product_id
FROM company_products cp
WHERE cp.company_id = ANY(v_companies)
);
--------------------------------------------------------------------
-- 3) Purge in strict child → parent order (avoid FK violations)
--------------------------------------------------------------------
-- Orders: details → headers
DELETE FROM order_details
WHERE id = ANY(v_order_detail_ids);
DELETE FROM orders
WHERE id = ANY(v_order_ids);
-- Inventory quantities tied to purged company-product assignments
DELETE FROM product_warehouse_quantities
WHERE id = ANY(v_pwq_ids);
-- Product tax category details → categories
DELETE FROM product_tax_category_details
WHERE id = ANY(v_ptcd_ids);
DELETE FROM product_tax_categories
WHERE id = ANY(v_ptc_ids);
-- Company-scoped assignments
DELETE FROM company_products
WHERE id = ANY(v_company_product_ids);
DELETE FROM company_categories
WHERE id = ANY(v_company_category_ids);
-- Company-scoped units
DELETE FROM units
WHERE id = ANY(v_unit_ids);
RAISE NOTICE 'Inventory purge complete for companies: % (orgs: %).', v_companies, v_orgs;
COMMIT;
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
RAISE NOTICE 'purge_inventory_organization_data failed: %', SQLERRM;
-- Optionally rethrow
-- RAISE;
END;
$procedure$