| Type | Name | Status | PK | FK | Columns | Index | Script | Diff Script |
|---|---|---|---|---|---|---|---|---|
| Table | bill_account_approval_levels | Match | ||||||
| Table | bill_workflow | Match | ||||||
| Table | temp_bill_next_status | Match | ||||||
| Table | vendor_advance_refunds | Missing in Target |
|
|||||
| Table | vendor_advance_settlement_ids | Missing in Target |
|
|||||
| Table | vendor_advance_settlements | Missing in Target |
|
|||||
| Table | v_organization_id | Match | ||||||
| Table | user_roles | Match | ||||||
| Table | schema_versions | Match | ||||||
| Table | vendor_note_statuses | Match | ||||||
| Table | vendor_categories | Match | ||||||
| Table | vendor_note_work_flows | Match | ||||||
| Table | bill_header_ids | Match | ||||||
| Table | bill_status_company_configs | Match | ||||||
| Table | vendor_advances | Missing in Target |
|
|||||
| Table | vendor_note_details | Match | ||||||
| Table | bill_payment_details | Match | ||||||
| Table | cities | Match | ||||||
| Table | draft_bill_details | Match | ||||||
| Table | companies | Match | ||||||
| Table | countries | Mismatch |
Source Script
Target Script
1
CREATE TABLE "countries" ("id" uuid NOT NULL, "name" text NOT NULL, "iso_alpha_code" 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, PRIMARY KEY ("id"));
1
CREATE TABLE "countries" ("id" uuid NOT NULL, "name" text NOT NULL, "iso_alpha_code" text NOT NULL, "created_by" uuid NOT NULL, "modified_by" uuid, "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, PRIMARY KEY ("id"));
|
|||||
| Table | gate_pass_details | Match | ||||||
| Table | organizations | Match | ||||||
| Table | schedule_execution_logs | Missing in Target |
|
|||||
| Table | roles | Mismatch |
Source Script
Target Script
1
CREATE TABLE "roles" ("name" varchar(50) NOT NULL, "description" varchar(200), "permission_template_id" integer, PRIMARY KEY ("name"));
1
CREATE TABLE "roles" ("name" varchar(50) NOT NULL, "description" varchar(200), PRIMARY KEY ("name"));
|
|||||
| Table | vendor_contacts | Match | ||||||
| Table | vendor_bank_accounts | Match | ||||||
| Table | banks | Match | ||||||
| Table | bill_details | Match | ||||||
| Table | bill_approval_user_company | Mismatch |
|
|||||
| Table | bill_approval_logs | Match | ||||||
| Table | bill_approval_user_account | Mismatch |
|
|||||
| Table | addresses | Match | ||||||
| Table | bill_payment_headers | Mismatch |
Source Script
Target Script
1
CREATE TABLE "bill_payment_headers" ("id" uuid NOT NULL, "vendor_id" uuid NOT NULL, "company_id" uuid DEFAULT '00000000-0000-0000-0000-000000000000'::uuid NOT NULL, "general_ledger_id" uuid DEFAULT '00000000-0000-0000-0000-000000000000'::uuid, "credit_account_id" uuid DEFAULT '00000000-0000-0000-0000-000000000000'::uuid NOT NULL, "debit_account_id" uuid DEFAULT '00000000-0000-0000-0000-000000000000'::uuid NOT NULL, "paid_date" timestamp without time zone NOT NULL, "paid_amount" numeric(,) NOT NULL, "grand_total_amount" numeric(,) NOT NULL, "advance_amount" numeric(,) NOT NULL, "description" text NOT NULL, "mode_of_payment" text DEFAULT ''::text NOT NULL, "reference" text DEFAULT ''::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, "payment_number" text DEFAULT ''::text NOT NULL, "tds_amount" numeric(,) DEFAULT 0.0 NOT NULL, "is_posted" boolean DEFAULT false NOT NULL, "transaction_id" bigint, PRIMARY KEY ("id"));
1
CREATE TABLE "bill_payment_headers" ("id" uuid NOT NULL, "vendor_id" uuid NOT NULL, "paid_date" timestamp without time zone NOT NULL, "paid_amount" numeric(,) NOT NULL, "grand_total_amount" numeric(,) NOT NULL, "advance_amount" numeric(,) NOT NULL, "description" 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, "company_id" uuid DEFAULT '00000000-0000-0000-0000-000000000000'::uuid NOT NULL, "mode_of_payment" text DEFAULT ''::text NOT NULL, "reference" text DEFAULT ''::text NOT NULL, "general_ledger_id" uuid DEFAULT '00000000-0000-0000-0000-000000000000'::uuid, "credit_account_id" uuid DEFAULT '00000000-0000-0000-0000-000000000000'::uuid NOT NULL, "debit_account_id" uuid DEFAULT '00000000-0000-0000-0000-000000000000'::uuid NOT NULL, "modified_by" uuid, "payment_number" text DEFAULT ''::text NOT NULL, "tds_amount" numeric(,) DEFAULT 0.0 NOT NULL, PRIMARY KEY ("id"));
|
|||||
| Table | bill_headers | Mismatch |
Source Script
Target Script
1
CREATE TABLE "bill_headers" ("id" uuid NOT NULL, "company_id" uuid NOT NULL, "vendor_id" uuid NOT NULL, "discount" numeric(,) NOT NULL, "bill_number" text, "bill_date" timestamp without time zone NOT NULL, "payment_term" integer, "bill_status_id" integer NOT NULL, "due_date" timestamp without time zone NOT NULL, "total_amount" numeric(,) NOT NULL, "taxable_amount" numeric(,) NOT NULL, "fees" numeric(,), "sgst_amount" numeric(,) NOT NULL, "cgst_amount" numeric(,) NOT NULL, "igst_amount" numeric(,) NOT NULL, "note" text NOT NULL, "round_off" numeric(,) NOT NULL, "debit_account_id" uuid NOT NULL, "credit_account_id" uuid NOT NULL, "currency_id" integer DEFAULT 0 NOT NULL, "po_date" timestamp without time zone DEFAULT '0001-01-01 00:00:00'::timestamp without time zone, "po_no" text, "destination_warehouse_id" uuid DEFAULT '00000000-0000-0000-0000-000000000000'::uuid, "source_warehouse_id" uuid DEFAULT '00000000-0000-0000-0000-000000000000'::uuid, "bill_type_id" integer DEFAULT 0 NOT NULL, "created_on_utc" timestamp without time zone NOT NULL, "created_by" uuid NOT NULL, "modified_by" uuid, "modified_on_utc" timestamp without time zone, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "bill_voucher" text NOT NULL, "is_editable" boolean, "is_gate_pass_created" boolean DEFAULT false NOT NULL, "tds_amount" numeric(,), "current_approval_level" integer DEFAULT 0 NOT NULL, "vendor_paid_amount" numeric(18,2) DEFAULT 0, "tds_payment_number" text, "tds_paid_amount" numeric(18,2) DEFAULT 0.0 NOT NULL, "is_tds_paid" boolean NOT NULL, "is_closed" boolean NOT NULL, "is_vendor_paid" boolean NOT NULL, "total_paid_amount" numeric(18,2) NOT NULL, "vendor_net_amount" numeric(18,2) NOT NULL, "tds_status_id" integer DEFAULT 1 NOT NULL, "payment_status_id" integer DEFAULT 1 NOT NULL, "round_off_tds" numeric(18,2) DEFAULT 0.0 NOT NULL, "is_posted" boolean DEFAULT false NOT NULL, "transaction_id" bigint, PRIMARY KEY ("id"));
1
CREATE TABLE "bill_headers" ("id" uuid NOT NULL, "company_id" uuid NOT NULL, "vendor_id" uuid NOT NULL, "discount" numeric(,) NOT NULL, "bill_voucher" text NOT NULL, "bill_date" timestamp without time zone NOT NULL, "payment_term" integer, "bill_status_id" integer NOT NULL, "due_date" timestamp without time zone NOT NULL, "total_amount" numeric(,) NOT NULL, "taxable_amount" numeric(,) NOT NULL, "fees" numeric(,), "sgst_amount" numeric(,) NOT NULL, "cgst_amount" numeric(,) NOT NULL, "igst_amount" numeric(,) NOT NULL, "note" text NOT NULL, "round_off" numeric(,) NOT NULL, "setteled_amount" numeric(,) DEFAULT 0, "debit_account_id" uuid NOT NULL, "credit_account_id" uuid NOT NULL, "created_on_utc" timestamp without time zone NOT NULL, "created_by" uuid NOT NULL, "modified_by" uuid, "modified_on_utc" timestamp without time zone, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "currency_id" integer DEFAULT 0 NOT NULL, "po_date" timestamp without time zone DEFAULT '0001-01-01 00:00:00'::timestamp without time zone, "po_no" text, "destination_warehouse_id" uuid DEFAULT '00000000-0000-0000-0000-000000000000'::uuid, "source_warehouse_id" uuid DEFAULT '00000000-0000-0000-0000-000000000000'::uuid, "bill_type_id" integer DEFAULT 0 NOT NULL, "is_editable" boolean, "bill_number" text, "is_gate_pass_created" boolean DEFAULT false NOT NULL, "tds_amount" numeric(,), "current_approval_level" integer DEFAULT 0 NOT NULL, PRIMARY KEY ("id"));
|
|||||
| Table | bill_approval_issue_logs | Mismatch |
|
|||||
| Table | __EFMigrationsHistory | Mismatch |
Source Script
Target Script
1
CREATE TABLE "__EFMigrationsHistory" ("migration_id" varchar(150) NOT NULL, "product_version" varchar(32) NOT NULL);
1
CREATE TABLE "__EFMigrationsHistory" ("migration_id" varchar(150) NOT NULL, "product_version" varchar(32) NOT NULL, PRIMARY KEY ("migration_id"));
|
|||||
| Table | bank_accounts | Match | ||||||
| Table | bill_payment_header_ids | Match | ||||||
| Table | bill_statuses | Match | ||||||
| Table | draft_bill_headers | Mismatch |
Source Script
Target Script
1
CREATE TABLE "draft_bill_headers" ("id" uuid NOT NULL, "company_id" uuid NOT NULL, "vendor_id" uuid NOT NULL, "discount" numeric(,) NOT NULL, "bill_number" text, "currency_id" integer NOT NULL, "bill_date" timestamp without time zone NOT NULL, "payment_term" integer, "bill_status_id" integer NOT NULL, "due_date" timestamp without time zone NOT NULL, "total_amount" numeric(18,2) NOT NULL, "taxable_amount" numeric(18,2) NOT NULL, "fees" numeric(18,2), "sgst_amount" numeric(18,2) NOT NULL, "cgst_amount" numeric(18,2) NOT NULL, "igst_amount" numeric(18,2) NOT NULL, "note" text NOT NULL, "round_off" numeric(18,2) NOT NULL, "debit_account_id" uuid NOT NULL, "credit_account_id" uuid NOT NULL, "po_no" text, "po_date" timestamp without time zone, "source_warehouse_id" uuid, "destination_warehouse_id" uuid, "bill_type_id" integer NOT NULL, "created_on_utc" timestamp without time zone NOT NULL, "created_by" uuid NOT NULL, "modified_by" uuid, "modified_on_utc" timestamp without time zone, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "is_editable" boolean, "bill_voucher" text NOT NULL, "is_gate_pass_created" boolean DEFAULT false NOT NULL, "tds_amount" numeric(18,2), "current_approval_level" integer DEFAULT 0 NOT NULL, "vendor_paid_amount" numeric(18,2) DEFAULT 0, "tds_paid_amount" numeric(18,2) DEFAULT 0.0 NOT NULL, "is_closed" boolean NOT NULL, "is_tds_paid" boolean NOT NULL, "is_vendor_paid" boolean NOT NULL, "total_paid_amount" numeric(18,2) NOT NULL, "vendor_net_amount" numeric(18,2) NOT NULL, "payment_status_id" integer DEFAULT 1 NOT NULL, "tds_status_id" integer DEFAULT 1 NOT NULL, "round_off_tds" numeric(18,2) DEFAULT 0.0 NOT NULL, PRIMARY KEY ("id"));
1
CREATE TABLE "draft_bill_headers" ("id" uuid NOT NULL, "company_id" uuid NOT NULL, "vendor_id" uuid NOT NULL, "discount" numeric(,) NOT NULL, "bill_voucher" text NOT NULL, "currency_id" integer NOT NULL, "bill_date" timestamp without time zone NOT NULL, "payment_term" integer, "bill_status_id" integer NOT NULL, "due_date" timestamp without time zone NOT NULL, "total_amount" numeric(,) NOT NULL, "taxable_amount" numeric(,) NOT NULL, "fees" numeric(,), "sgst_amount" numeric(,) NOT NULL, "cgst_amount" numeric(,) NOT NULL, "igst_amount" numeric(,) NOT NULL, "note" text NOT NULL, "round_off" numeric(,) NOT NULL, "setteled_amount" numeric(,) DEFAULT 0, "debit_account_id" uuid NOT NULL, "credit_account_id" uuid NOT NULL, "po_no" text, "po_date" timestamp without time zone, "source_warehouse_id" uuid, "destination_warehouse_id" uuid, "bill_type_id" integer NOT NULL, "created_on_utc" timestamp without time zone NOT NULL, "created_by" uuid NOT NULL, "modified_by" uuid, "modified_on_utc" timestamp without time zone, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "is_editable" boolean, "bill_number" text, "is_gate_pass_created" boolean DEFAULT false NOT NULL, "tds_amount" numeric(,), "current_approval_level" integer DEFAULT 0 NOT NULL, PRIMARY KEY ("id"));
|
|||||
| Table | gate_pass_statuses | Match | ||||||
| Table | draft_bill_header_ids | Match | ||||||
| Table | contacts | Match | ||||||
| Table | bill_types | Match | ||||||
| Table | payment_orders | Match | ||||||
| Table | warehouses | Match | ||||||
| Table | gate_pass_headers | Match | ||||||
| Table | payment_statuses | Missing in Target |
|
|||||
| Table | vendor_advance_ids | Missing in Target |
|
|||||
| Table | tds_statuses | Missing in Target |
|
|||||
| Table | upis | Match | ||||||
| Table | recurrence_bill_schedule_details | Match | ||||||
| Table | procedure_logs | Match | ||||||
| Table | states | Match | ||||||
| Table | users | Mismatch |
Source Script
Target Script
1
CREATE TABLE "users" ("id" uuid NOT NULL, "first_name" varchar(100) NOT NULL, "last_name" varchar(100) NOT NULL, "email" varchar(256) NOT NULL, "phone_number" varchar(15) DEFAULT ''::character varying NOT NULL, "password_hash" 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, "company_id" uuid DEFAULT '00000000-0000-0000-0000-000000000000'::uuid NOT NULL, PRIMARY KEY ("id"));
1
CREATE TABLE "users" ("id" uuid NOT NULL, "first_name" varchar(100) NOT NULL, "last_name" varchar(100) NOT NULL, "email" varchar(256) 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, "password_hash" text, "phone_number" varchar(15) DEFAULT ''::character varying NOT NULL, "company_id" uuid DEFAULT '00000000-0000-0000-0000-000000000000'::uuid NOT NULL, PRIMARY KEY ("id"));
|
|||||
| Table | vendor_default_accounts | Match | ||||||
| Table | vendor_note_headers | Match | ||||||
| Table | vendor_upis | Match | ||||||
| Table | vendors | Mismatch |
Source Script
Target Script
1
CREATE TABLE "vendors" ("id" uuid NOT NULL, "name" varchar(100) NOT NULL, "company_id" uuid DEFAULT '00000000-0000-0000-0000-000000000000'::uuid NOT NULL, "billing_address_id" uuid DEFAULT '00000000-0000-0000-0000-000000000000'::uuid, "shipping_address_id" uuid DEFAULT '00000000-0000-0000-0000-000000000000'::uuid, "gstin" text DEFAULT ''::text NOT NULL, "pan" text, "tan" text, "short_name" text, "proprietor_name" text, "interest_percentage" numeric(,) DEFAULT 0.0 NOT NULL, "is_non_work" boolean DEFAULT false NOT NULL, "outstanding_limit" numeric(,) DEFAULT 0.0 NOT NULL, "has_gstin" boolean DEFAULT false, "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, "is_direct_payment_activated" boolean DEFAULT false NOT NULL, "category_id" integer, "description" text, "default_account_id" uuid DEFAULT '00000000-0000-0000-0000-000000000000'::uuid, "is_tds_vendor" boolean DEFAULT false NOT NULL, PRIMARY KEY ("id"));
1
CREATE TABLE "vendors" ("id" uuid NOT NULL, "name" varchar(100) 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, "shipping_address_id" uuid DEFAULT '00000000-0000-0000-0000-000000000000'::uuid, "gstin" text DEFAULT ''::text NOT NULL, "interest_percentage" numeric(,) DEFAULT 0.0 NOT NULL, "is_non_work" boolean DEFAULT false NOT NULL, "outstanding_limit" numeric(,) DEFAULT 0.0 NOT NULL, "pan" text, "proprietor_name" text, "short_name" text, "tan" text, "billing_address_id" uuid DEFAULT '00000000-0000-0000-0000-000000000000'::uuid, "has_gstin" boolean DEFAULT false, "is_direct_payment_activated" boolean DEFAULT false NOT NULL, "category_id" integer, "description" text, "default_account_id" uuid DEFAULT '00000000-0000-0000-0000-000000000000'::uuid, PRIMARY KEY ("id"));
|
|||||
| Table | recurring_bill_schedules | Mismatch |
Source Script
Target Script
1
CREATE TABLE "recurring_bill_schedules" ("id" uuid NOT NULL, "company_id" uuid NOT NULL, "frequency_cycle" text NOT NULL, "schedule_status" text NOT NULL, "starts_from" timestamp without time zone NOT NULL, "end_date" timestamp without time zone NOT NULL, "created_on_utc" timestamp without time zone NOT NULL, "created_by" uuid NOT NULL, "modified_by" uuid, "modified_on_utc" timestamp without time zone, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "bill_header_id" uuid, "draft_bill_header_id" uuid, PRIMARY KEY ("id"));
1
CREATE TABLE "recurring_bill_schedules" ("id" uuid NOT NULL, "bill_header_id" uuid NOT NULL, "company_id" uuid NOT NULL, "frequency_cycle" text NOT NULL, "schedule_status" text NOT NULL, "starts_from" timestamp without time zone NOT NULL, "end_date" timestamp without time zone NOT NULL, "created_on_utc" timestamp without time zone NOT NULL, "created_by" uuid NOT NULL, "modified_by" uuid, "modified_on_utc" timestamp without time zone, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, PRIMARY KEY ("id"));
|
|||||
| Function | checkin_service_provider | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.checkin_service_provider(p_apartment_id uuid, p_pin text, p_created_by uuid)
2
RETURNS integer
3
LANGUAGE plpgsql
4
AS $function$
5
DECLARE
6
v_service_provider_id int;
7
v_log_id int;
8
BEGIN
9
-- Step 1: Validate service provider by apartmentId and pin
10
SELECT id INTO v_service_provider_id
11
FROM public.service_providers
12
WHERE apartment_id = p_apartment_id
13
AND pin = p_pin
14
AND is_deleted = false
15
LIMIT 1;
16
17
IF v_service_provider_id IS NULL THEN
18
RAISE EXCEPTION 'Service provider not found for apartment % with pin %', p_apartment_id, p_pin
19
USING ERRCODE = 'no_data_found';
20
END IF;
21
22
-- Step 2: Insert service provider log (CheckedIn)
23
INSERT INTO public.service_provider_logs (
24
service_provider_id,
25
current_status_id,
26
entry_time,
27
created_on_utc,
28
created_by
29
)
30
VALUES (
31
v_service_provider_id,
32
1, -- CheckedIn
33
now(),
34
now(),
35
p_created_by
36
)
37
RETURNING id INTO v_log_id;
38
39
RETURN v_log_id;
40
END;
41
$function$
|
|||||
| Function | bulk_delete_vendors_with_flag | Match | ||||||
| Function | create_vendor_advance_settlements | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.create_vendor_advance_settlements(p_company_id uuid, p_vendor_id uuid, p_bill_payment_header_id uuid, p_created_by uuid, p_settlements jsonb)
2
RETURNS TABLE(id uuid, advance_settlement_number text)
3
LANGUAGE plpgsql
4
AS $function$
5
DECLARE
6
v_settlement_number text;
7
v_row jsonb;
8
v_id uuid;
9
BEGIN
10
FOR v_row IN SELECT * FROM jsonb_array_elements(p_settlements)
11
LOOP
12
v_settlement_number := public.get_new_vendor_advance_settlement_number(
13
p_company_id,
14
COALESCE((v_row->>'settled_date')::date, CURRENT_DATE)
15
);
16
v_id := (v_row->>'id')::uuid;
17
18
INSERT INTO vendor_advance_settlements
19
(
20
id,
21
company_id,
22
vendor_id,
23
advance_id,
24
bill_id,
25
apply_amount,
26
applied_in_payment_id,
27
settled_date,
28
note,
29
created_on_utc,
30
created_by,
31
is_deleted,
32
advance_settlement_number
33
)
34
VALUES
35
(
36
v_id,
37
p_company_id,
38
p_vendor_id,
39
(v_row->>'advance_id')::uuid,
40
(v_row->>'bill_id')::uuid,
41
(v_row->>'apply_amount')::numeric,
42
p_bill_payment_header_id,
43
COALESCE((v_row->>'settled_date')::timestamp, now()),
44
NULLIF(v_row->>'note',''),
45
now(),
46
p_created_by,
47
false,
48
v_settlement_number
49
);
50
51
UPDATE vendor_advances va
52
SET utilized_amount = utilized_amount + (v_row->>'apply_amount')::numeric,
53
modified_by = p_created_by,
54
modified_on_utc = now()
55
WHERE va.id = (v_row->>'advance_id')::uuid
56
AND va.company_id = p_company_id
57
AND va.vendor_id = p_vendor_id
58
AND va.is_deleted = false;
59
60
-- Assign to output variables, then RETURN NEXT;
61
id := v_id;
62
advance_settlement_number := v_settlement_number;
63
RETURN NEXT;
64
END LOOP;
65
END;
66
$function$
|
|||||
| Function | create_user | Match | ||||||
| Function | get_all_bill_company_approvals | Match | ||||||
| Function | get_all_unit_names_by_apartment_id | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.get_all_unit_names_by_apartment_id(p_apartment_id uuid)
2
RETURNS TABLE(id integer, unit_id integer, unit_name text, building_name text, floor_name text)
3
LANGUAGE plpgsql
4
AS $function$
5
BEGIN
6
RETURN QUERY
7
SELECT
8
CAST(ROW_NUMBER() OVER (ORDER BY u.id) AS INT) AS id,
9
u.id AS unit_id,
10
u."name" AS unit_name,
11
b."name" AS building_name,
12
f."name" AS floor_name
13
FROM units u
14
INNER JOIN buildings b ON u.building_id = b.id
15
INNER JOIN floors f ON u.floor_id = f.id
16
WHERE u.apartment_id = p_apartment_id
17
AND u.is_deleted = false
18
AND b.is_deleted = false
19
AND f.is_deleted = false;
20
END;
21
$function$
|
|||||
| Function | get_all_schedule_bills | Match | ||||||
| Function | get_all_bills_from_span | Mismatch |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.get_all_bills_from_span(p_company_id uuid, p_fin_year_id integer, p_user_id uuid, p_bill_type_id integer DEFAULT NULL::integer, 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)
1
CREATE OR REPLACE FUNCTION public.get_all_bills_from_span(p_company_id uuid, p_fin_year_id integer, p_user_id uuid, p_bill_type_id integer DEFAULT NULL::integer, 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, bill_voucher text, bill_number text, vendor_name character varying, vendor_id uuid, due_date date, bill_date date, payment_term integer, total_amount numeric, total_paid_amount numeric, currency_id integer, bill_status character varying, bill_status_id integer, payment_status character varying, payment_status_id integer, tds_status character varying, tds_status_id integer, created_by uuid, created_on_utc timestamp without time zone, modified_by uuid, modified_on_utc timestamp without time zone, created_by_name character varying, modified_by_name character varying, bill_type character varying, bill_type_id integer, tds_amount numeric, has_next_status_approval boolean, next_status_id integer)
2
RETURNS TABLE(id uuid, bill_voucher text, bill_number text, vendor_name character varying, vendor_id uuid, due_date date, bill_date date, payment_term integer, total_amount numeric, currency_id integer, bill_status character varying, bill_status_id integer, created_by uuid, created_on_utc timestamp without time zone, modified_by uuid, modified_on_utc timestamp without time zone, created_by_name character varying, modified_by_name character varying, bill_type character varying, bill_type_id integer, tds_amount numeric, has_next_status_approval boolean, next_status_id integer)
3
LANGUAGE plpgsql
3
LANGUAGE plpgsql
4
AS $function$
4
AS $function$
5
DECLARE
5
DECLARE
6
v_start_date date := COALESCE(p_start_date::date, MAKE_DATE(p_fin_year_id, 4, 1));
6
v_start_date date := COALESCE(p_start_date::date, MAKE_DATE(p_fin_year_id, 4, 1));
7
v_end_date date := COALESCE(p_end_date::date, MAKE_DATE(p_fin_year_id + 1, 3, 31));
7
v_end_date date := COALESCE(p_end_date::date, MAKE_DATE(p_fin_year_id + 1, 3, 31));
8
DRAFT_STATUS CONSTANT integer := 1;
8
DRAFT_STATUS CONSTANT integer := 1;
9
APPROVED_STATUS CONSTANT integer := 3;
10
PAID_STATUS CONSTANT integer := 5;
9
BEGIN
11
BEGIN
10
RETURN QUERY
12
RETURN QUERY
11
WITH bill_data AS (
13
WITH bill_data AS (
12
SELECT
14
SELECT
13
bh.id,
15
bh.id,
14
bh.bill_voucher,
16
bh.bill_voucher,
15
bh.bill_number,
17
bh.bill_number,
16
v.name AS vendor_name,
18
v.name AS vendor_name,
17
bh.vendor_id,
19
bh.vendor_id,
18
bh.due_date,
20
bh.due_date,
19
bh.bill_date,
21
bh.bill_date,
20
bh.payment_term,
22
bh.payment_term,
21
bh.total_amount,
23
bh.total_amount,
22
bh.total_paid_amount,
23
bh.currency_id,
24
bh.currency_id,
24
bs.name AS bill_status,
25
bs.name AS bill_status,
25
bh.bill_status_id,
26
bh.bill_status_id,
26
ps.name AS payment_status, -- ✅ Added
27
bh.payment_status_id, -- ✅ Added
28
ts.name AS tds_status, -- ✅ Added
29
bh.tds_status_id, -- ✅ Added
30
bh.created_by,
27
bh.created_by,
31
bh.created_on_utc,
28
bh.created_on_utc,
32
bh.modified_by,
29
bh.modified_by,
33
bh.modified_on_utc,
30
bh.modified_on_utc,
34
CONCAT(cu.first_name, ' ', cu.last_name)::character varying AS created_by_name,
31
CONCAT(cu.first_name, ' ', cu.last_name)::character varying AS created_by_name,
35
CONCAT(mu.first_name, ' ', mu.last_name)::character varying AS modified_by_name,
32
CONCAT(mu.first_name, ' ', mu.last_name)::character varying AS modified_by_name,
36
bt.name AS bill_type,
33
bt.name AS bill_type,
37
bh.bill_type_id,
34
bh.bill_type_id,
38
bh.tds_amount,
35
bh.tds_amount,
39
bh.debit_account_id
36
bh.debit_account_id
40
FROM public.bill_headers bh
37
FROM public.bill_headers bh
41
LEFT JOIN public.vendors v ON v.id = bh.vendor_id
38
LEFT JOIN public.vendors v ON v.id = bh.vendor_id
42
LEFT JOIN public.bill_statuses bs ON bs.id = bh.bill_status_id
39
LEFT JOIN public.bill_statuses bs ON bs.id = bh.bill_status_id
43
LEFT JOIN public.payment_statuses ps ON ps.id = bh.payment_status_id
40
LEFT JOIN public.bill_types bt ON bt.id = bh.bill_type_id
44
LEFT JOIN public.tds_statuses ts ON ts.id = bh.tds_status_id -- ✅ Added
41
LEFT JOIN users cu ON cu.id = bh.created_by
45
LEFT JOIN public.bill_types bt ON bt.id = bh.bill_type_id
42
LEFT JOIN users mu ON mu.id = bh.modified_by
46
LEFT JOIN public.users cu ON cu.id = bh.created_by
47
LEFT JOIN public.users mu ON mu.id = bh.modified_by
48
WHERE bh.company_id = p_company_id
43
WHERE bh.company_id = p_company_id
49
AND bh.is_deleted = false
44
AND bh.is_deleted = false
50
AND (p_bill_type_id IS NULL OR p_bill_type_id = 0 OR bh.bill_type_id = p_bill_type_id)
45
AND (p_bill_type_id IS NULL OR p_bill_type_id = 0 OR bh.bill_type_id = p_bill_type_id)
51
AND bh.bill_date BETWEEN v_start_date AND v_end_date
46
AND bh.bill_date BETWEEN v_start_date AND v_end_date
52
47
53
UNION ALL
48
UNION ALL
54
49
55
SELECT
50
SELECT
56
dbh.id,
51
dbh.id,
57
dbh.bill_voucher,
52
dbh.bill_voucher,
58
dbh.bill_number,
53
dbh.bill_number,
59
v.name AS vendor_name,
54
v.name AS vendor_name,
60
dbh.vendor_id,
55
dbh.vendor_id,
61
dbh.due_date,
56
dbh.due_date,
62
dbh.bill_date,
57
dbh.bill_date,
63
dbh.payment_term,
58
dbh.payment_term,
64
dbh.total_amount,
59
dbh.total_amount,
65
0::numeric(18,2) AS total_paid_amount,
66
dbh.currency_id,
60
dbh.currency_id,
67
bs.name AS bill_status,
61
bs.name AS bill_status,
68
dbh.bill_status_id,
62
dbh.bill_status_id,
69
ps.name AS payment_status, -- ✅ Added
70
dbh.payment_status_id, -- ✅ Added
71
ts.name AS tds_status, -- ✅ Added
72
dbh.tds_status_id, -- ✅ Added
73
dbh.created_by,
63
dbh.created_by,
74
dbh.created_on_utc,
64
dbh.created_on_utc,
75
dbh.modified_by,
65
dbh.modified_by,
76
dbh.modified_on_utc,
66
dbh.modified_on_utc,
77
CONCAT(cu.first_name, ' ', cu.last_name)::character varying AS created_by_name,
67
CONCAT(cu.first_name, ' ', cu.last_name)::character varying AS created_by_name,
78
CONCAT(mu.first_name, ' ', mu.last_name)::character varying AS modified_by_name,
68
CONCAT(mu.first_name, ' ', mu.last_name)::character varying AS modified_by_name,
79
bt.name AS bill_type,
69
bt.name AS bill_type,
80
dbh.bill_type_id,
70
dbh.bill_type_id,
81
dbh.tds_amount,
71
dbh.tds_amount,
82
dbh.debit_account_id
72
dbh.debit_account_id
83
FROM public.draft_bill_headers dbh
73
FROM public.draft_bill_headers dbh
84
LEFT JOIN public.vendors v ON v.id = dbh.vendor_id
74
LEFT JOIN vendors v ON v.id = dbh.vendor_id
85
LEFT JOIN public.bill_statuses bs ON bs.id = dbh.bill_status_id
75
LEFT JOIN bill_statuses bs ON bs.id = dbh.bill_status_id
86
LEFT JOIN public.payment_statuses ps ON ps.id = dbh.payment_status_id
76
LEFT JOIN bill_types bt ON bt.id = dbh.bill_type_id
87
LEFT JOIN public.tds_statuses ts ON ts.id = dbh.tds_status_id -- ✅ Added
77
LEFT JOIN users cu ON cu.id = dbh.created_by
88
LEFT JOIN public.bill_types bt ON bt.id = dbh.bill_type_id
78
LEFT JOIN users mu ON mu.id = dbh.modified_by
89
LEFT JOIN public.users cu ON cu.id = dbh.created_by
90
LEFT JOIN public.users mu ON mu.id = dbh.modified_by
91
WHERE dbh.company_id = p_company_id
79
WHERE dbh.company_id = p_company_id
92
AND dbh.is_deleted = false
80
AND dbh.is_deleted = false
93
AND (p_bill_type_id IS NULL OR p_bill_type_id = 0 OR dbh.bill_type_id = p_bill_type_id)
81
AND (p_bill_type_id IS NULL OR p_bill_type_id = 0 OR dbh.bill_type_id = p_bill_type_id)
94
AND dbh.bill_date BETWEEN v_start_date AND v_end_date
82
AND dbh.bill_date BETWEEN v_start_date AND v_end_date
95
),
96
enriched AS (
97
SELECT
98
bd.*,
99
COALESCE(
100
(
101
SELECT bw.next_status
102
FROM bill_workflow bw
103
WHERE bw.company_id = p_company_id
104
AND bw.status = bd.bill_status_id
105
AND bw.is_deleted = false
106
AND (bw.is_enabled IS DISTINCT FROM FALSE)
107
ORDER BY bw.approval_level ASC NULLS LAST
108
LIMIT 1
109
),
110
0
111
) AS next_status_id
112
FROM bill_data bd
113
)
83
)
114
SELECT
84
SELECT
115
e.id,
85
bd.id,
116
e.bill_voucher,
86
bd.bill_voucher,
117
e.bill_number,
87
bd.bill_number,
118
e.vendor_name,
88
bd.vendor_name,
119
e.vendor_id,
89
bd.vendor_id,
120
e.due_date::date,
90
bd.due_date::date,
121
e.bill_date::date,
91
bd.bill_date::date,
122
e.payment_term,
92
bd.payment_term,
123
e.total_amount,
93
bd.total_amount,
124
e.total_paid_amount,
94
bd.currency_id,
125
e.currency_id,
95
bd.bill_status,
126
e.bill_status,
96
bd.bill_status_id,
127
e.bill_status_id,
97
bd.created_by,
128
e.payment_status,
98
bd.created_on_utc,
129
e.payment_status_id,
99
bd.modified_by,
130
e.tds_status,
100
bd.modified_on_utc,
131
e.tds_status_id,
101
bd.created_by_name,
132
e.created_by,
102
bd.modified_by_name,
133
e.created_on_utc,
103
bd.bill_type::character varying,
134
e.modified_by,
104
bd.bill_type_id,
135
e.modified_on_utc,
105
bd.tds_amount,
136
e.created_by_name,
137
e.modified_by_name,
138
e.bill_type::character varying,
139
e.bill_type_id,
140
e.tds_amount,
141
CASE
106
CASE
142
WHEN e.bill_status_id = DRAFT_STATUS THEN true
107
WHEN bd.bill_status_id = DRAFT_STATUS THEN true
143
WHEN e.next_status_id = 0 THEN false
144
WHEN EXISTS (
108
WHEN EXISTS (
145
SELECT 1
109
SELECT 1
146
FROM bill_approval_user_account a
110
FROM bill_approval_user_account a
147
WHERE a.account_id = e.debit_account_id
111
WHERE a.account_id = bd.debit_account_id
148
AND a.status_id = e.bill_status_id
112
AND a.status_id = bd.bill_status_id
149
AND a.user_id = p_user_id
113
AND a.user_id = p_user_id
150
AND a.is_deleted = false
114
AND a.is_deleted = false
151
) THEN true
115
) THEN true
152
WHEN EXISTS (
116
WHEN EXISTS (
153
SELECT 1
117
SELECT 1
154
FROM bill_approval_user_company c
118
FROM bill_approval_user_company c
155
WHERE c.company_id = p_company_id
119
WHERE c.company_id = p_company_id
156
AND c.status_id = e.bill_status_id
120
AND c.status_id = bd.bill_status_id
157
AND c.user_id = p_user_id
121
AND c.user_id = p_user_id
158
AND c.is_deleted = false
122
AND c.is_deleted = false
159
) THEN true
123
) THEN true
160
ELSE false
124
ELSE false
161
END AS has_next_status_approval,
125
END AS has_next_status_approval,
162
e.next_status_id
126
COALESCE(
163
FROM enriched e;
127
(SELECT bw.next_status
128
FROM bill_workflow bw
129
WHERE bw.company_id = p_company_id
130
AND bw.status = bd.bill_status_id
131
AND bw.is_deleted = false
132
LIMIT 1),
133
0
134
) AS next_status_id
135
FROM bill_data bd;
164
END;
136
END;
165
$function$
137
$function$
|
|||||
| Function | get_all_vendors | Match | ||||||
| Function | get_bill_by_id_json | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.get_bill_by_id_json(p_bill_header_id uuid)
2
RETURNS json
3
LANGUAGE plpgsql
4
AS $function$
5
DECLARE
6
v_bill_status text;
7
v_bill_json json;
8
BEGIN
9
-- Get bill status name
10
SELECT bs.name INTO v_bill_status
11
FROM bill_statuses bs
12
JOIN bill_headers bh ON bh.bill_status_id = bs.id
13
WHERE bh.id = p_bill_header_id;
14
15
-- Construct the JSON output
16
SELECT json_build_object(
17
'id', bh.id,
18
'billNumber', bh.bill_number,
19
'billDate', bh.bill_date,
20
'paymentTerm', bh.payment_term,
21
'billStatus', v_bill_status,
22
'billStatusId', bh.bill_status_id,
23
'vendor', json_build_object(
24
'id', v.id,
25
'name', v.name,
26
'email', c.email,
27
'mobileNumber', c.mobile_number,
28
'phoneNumber', c.phone_number,
29
'gstIn', v.gstin,
30
'shortName', v.short_name,
31
'pan', v.pan,
32
'tan', v.tan
33
),
34
'debitAccountId', bh.debit_account_id,
35
'currencyId', bh.currency_id,
36
'dueDate', bh.due_date,
37
'taxableAmount', bh.taxable_amount,
38
'fees', bh.fees,
39
'cgstAmount', bh.cgst_amount,
40
'sgstAmount', bh.sgst_amount,
41
'igstAmount', bh.igst_amount,
42
'totalAmount', bh.total_amount,
43
'discount', bh.discount,
44
'note', bh.note,
45
'roundOff', bh.round_off,
46
'sourceWarehouse', NULL,
47
'sourceWarehouseId', bh.source_warehouse_id,
48
'destinationWarehouseId', bh.destination_warehouse_id,
49
'poNo', bh.po_no,
50
'poDate', bh.po_date,
51
'lines', (
52
SELECT json_agg(json_build_object(
53
'id', bl.id,
54
'productId', bl.product_id,
55
'price', bl.price,
56
'quantity', bl.quantity,
57
'fees', bl.fees,
58
'discount', bl.discount,
59
'taxableAmount', bl.taxable_amount,
60
'sgstAmount', bl.sgst_amount,
61
'cgstAmount', bl.cgst_amount,
62
'igstAmount', bl.igst_amount,
63
'totalAmount', bl.total_amount,
64
'serialNumber', bl.serial_number
65
))
66
FROM bill_details bl
67
WHERE bl.bill_header_id = p_bill_header_id
68
),
69
'billTypeId', bh.bill_type_id
70
) INTO v_bill_json
71
FROM bill_headers bh
72
LEFT JOIN vendors v ON v.id = bh.vendor_id
73
LEFT JOIN vendor_contacts vc ON vc.vendor_id = v.id
74
LEFT JOIN contacts c ON c.id = vc.contact_id
75
WHERE bh.id = p_bill_header_id;
76
77
-- Return the JSON object
78
RETURN v_bill_json;
79
END;
80
$function$
|
|||||
| Function | get_new_bill_voucher | Match | ||||||
| Function | get_vendor_advances_by_vendor | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.get_vendor_advances_by_vendor(p_company_id uuid, p_vendor_id uuid, only_unutilized_advances boolean DEFAULT false)
2
RETURNS TABLE(id uuid, vendor_id uuid, vendor_name character varying, advance_number text, paid_date timestamp without time zone, amount numeric, utilized_amount numeric, mode_of_payment text, reference text, description text, currency_id integer)
3
LANGUAGE sql
4
AS $function$
5
SELECT
6
va.id,
7
va.vendor_id,
8
v.name, -- Add vendor name
9
va.advance_number,
10
va.paid_date,
11
va.amount,
12
va.utilized_amount,
13
va.mode_of_payment,
14
va.reference,
15
va.description,
16
va.currency_id
17
FROM public.vendor_advances va
18
LEFT JOIN public.vendors v ON va.vendor_id = v.id -- Join here to get the name
19
WHERE va.company_id = p_company_id
20
AND va.vendor_id = p_vendor_id
21
AND va.is_deleted = false
22
AND v.is_deleted = false
23
AND (
24
-- If only_unutilized_advances = true → filter unsettled
25
(only_unutilized_advances = true AND va.amount > va.utilized_amount)
26
-- If false or null → return all
27
OR (only_unutilized_advances IS DISTINCT FROM true)
28
);
29
$function$
|
|||||
| Function | get_payment_distributions_for_bill | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.get_payment_distributions_for_bill(p_bill_header_id uuid)
2
RETURNS TABLE(payment_detail_id uuid, bill_payment_header_id uuid, payment_number text, bill_header_id uuid, paid_amount numeric, tds_amount numeric, payment_is_deleted boolean, payment_created_by uuid, payment_created_by_name text, payment_created_on timestamp without time zone)
3
LANGUAGE plpgsql
4
AS $function$
5
BEGIN
6
RETURN QUERY
7
SELECT
8
bpd.id AS payment_detail_id,
9
bpd.bill_payment_header_id,
10
bph.payment_number,
11
bpd.bill_header_id,
12
bpd.paid_amount,
13
bpd.tds_amount,
14
bpd.is_deleted AS payment_is_deleted,
15
bpd.created_by AS payment_created_by,
16
CONCAT(u.first_name, ' ', u.last_name) AS payment_created_by_name,
17
bpd.created_on_utc AS payment_created_on
18
FROM
19
public.bill_payment_details bpd
20
JOIN public.bill_payment_headers bph ON bph.id = bpd.bill_payment_header_id
21
LEFT JOIN public.users u ON u.id = bpd.created_by
22
WHERE
23
bpd.bill_header_id = p_bill_header_id
24
AND bpd.is_deleted = false
25
ORDER BY
26
bpd.created_on_utc ASC;
27
END;
28
$function$
|
|||||
| Function | get_vendor_details | Match | ||||||
| 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 | get_vendor_default_accounts | Match | ||||||
| Function | update_bill_next_status_test | Match | ||||||
| Function | upsert_vendor_default_account | Match | ||||||
| Function | bulk_delete_vendors | Match | ||||||
| Function | get_all_bill_headers | Match | ||||||
| Function | get_new_payment_number | Match | ||||||
| Function | get_bill_workflow_config | Match | ||||||
| Function | get_warehouse_details | Match | ||||||
| Function | get_bill_status | Match | ||||||
| Function | insert_multiple_bills_by_excel | Mismatch |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.insert_multiple_bills_by_excel(p_bills jsonb)
1
CREATE OR REPLACE FUNCTION public.insert_multiple_bills_by_excel(p_bills jsonb)
2
RETURNS text
2
RETURNS text
3
LANGUAGE plpgsql
3
LANGUAGE plpgsql
4
AS $function$
4
AS $function$
5
DECLARE
5
DECLARE
6
bill RECORD;
6
bill RECORD;
7
v_created_by UUID;
7
v_created_by UUID;
8
v_status TEXT := 'success';
8
v_status TEXT := 'success';
9
BEGIN
9
BEGIN
10
-- Validate the input JSONB structure
10
-- Validate the input JSONB structure
11
BEGIN
11
BEGIN
12
PERFORM *
12
PERFORM *
13
FROM jsonb_to_recordset(p_bills) AS (
13
FROM jsonb_to_recordset(p_bills) AS (
14
bill_id UUID,
14
bill_id UUID,
15
company_id UUID,
15
company_id UUID,
16
vendor_id UUID,
16
vendor_id UUID,
17
debit_account_id UUID,
17
debit_account_id UUID,
18
credit_account_id UUID,
18
credit_account_id UUID,
19
discount NUMERIC,
19
discount NUMERIC,
20
bill_number TEXT,
20
bill_number TEXT,
21
currency_id INTEGER,
21
currency_id INTEGER,
22
bill_date TIMESTAMP,
22
bill_date TIMESTAMP,
23
payment_term INTEGER,
23
payment_term INTEGER,
24
bill_status_id INTEGER,
24
bill_status_id INTEGER,
25
due_date TIMESTAMP,
25
due_date TIMESTAMP,
26
total_amount NUMERIC,
26
total_amount NUMERIC,
27
note TEXT,
27
note TEXT,
28
taxable_amount NUMERIC,
28
taxable_amount NUMERIC,
29
fees NUMERIC,
29
fees NUMERIC,
30
sgst_amount NUMERIC,
30
sgst_amount NUMERIC,
31
cgst_amount NUMERIC,
31
cgst_amount NUMERIC,
32
igst_amount NUMERIC,
32
igst_amount NUMERIC,
33
tds_amount NUMERIC,
33
tds_amount NUMERIC,
34
round_off NUMERIC,
34
round_off NUMERIC,
35
round_off_tds NUMERIC,
36
po_no TEXT,
35
po_no TEXT,
37
po_date TIMESTAMP,
36
po_date TIMESTAMP,
38
source_warehouse_id UUID,
37
source_warehouse_id UUID,
39
destination_warehouse_id UUID,
38
destination_warehouse_id UUID,
40
bill_type_id INTEGER,
39
bill_type_id INTEGER,
41
lines JSONB,
40
lines JSONB,
42
created_by UUID
41
created_by UUID
43
);
42
);
44
EXCEPTION
43
EXCEPTION
45
WHEN OTHERS THEN
44
WHEN OTHERS THEN
46
RAISE NOTICE 'Invalid JSON input: %', SQLERRM;
45
RAISE NOTICE 'Invalid JSON input: %', SQLERRM;
47
RETURN 'error';
46
RETURN 'error';
48
END;
47
END;
49
48
50
-- Get 'System' user as created_by fallback
49
-- Get 'System' user as created_by fallback
51
SELECT id INTO v_created_by
50
SELECT id INTO v_created_by
52
FROM public.users
51
FROM public.users
53
WHERE first_name = 'System'
52
WHERE first_name = 'System'
54
LIMIT 1;
53
LIMIT 1;
55
54
56
-- Loop through each bill
55
-- Loop through each bill
57
FOR bill IN
56
FOR bill IN
58
SELECT * FROM jsonb_to_recordset(p_bills) AS (
57
SELECT * FROM jsonb_to_recordset(p_bills) AS (
59
bill_id UUID,
58
bill_id UUID,
60
company_id UUID,
59
company_id UUID,
61
vendor_id UUID,
60
vendor_id UUID,
62
debit_account_id UUID,
61
debit_account_id UUID,
63
credit_account_id UUID,
62
credit_account_id UUID,
64
discount NUMERIC,
63
discount NUMERIC,
65
bill_number TEXT,
64
bill_number TEXT,
66
currency_id INTEGER,
65
currency_id INTEGER,
67
bill_date TIMESTAMP,
66
bill_date TIMESTAMP,
68
payment_term INTEGER,
67
payment_term INTEGER,
69
bill_status_id INTEGER,
68
bill_status_id INTEGER,
70
due_date TIMESTAMP,
69
due_date TIMESTAMP,
71
total_amount NUMERIC,
70
total_amount NUMERIC,
72
note TEXT,
71
note TEXT,
73
taxable_amount NUMERIC,
72
taxable_amount NUMERIC,
74
fees NUMERIC,
73
fees NUMERIC,
75
sgst_amount NUMERIC,
74
sgst_amount NUMERIC,
76
cgst_amount NUMERIC,
75
cgst_amount NUMERIC,
77
igst_amount NUMERIC,
76
igst_amount NUMERIC,
78
tds_amount NUMERIC,
77
tds_amount NUMERIC,
79
round_off NUMERIC,
78
round_off NUMERIC,
80
round_off_tds NUMERIC,
81
po_no TEXT,
79
po_no TEXT,
82
po_date TIMESTAMP,
80
po_date TIMESTAMP,
83
source_warehouse_id UUID,
81
source_warehouse_id UUID,
84
destination_warehouse_id UUID,
82
destination_warehouse_id UUID,
85
bill_type_id INTEGER,
83
bill_type_id INTEGER,
86
lines JSONB,
84
lines JSONB,
87
created_by UUID
85
created_by UUID
88
)
86
)
89
LOOP
87
LOOP
90
BEGIN
88
BEGIN
91
-- Duplicate check
89
-- Duplicate check
92
IF EXISTS (
90
IF EXISTS (
93
SELECT 1
91
SELECT 1
94
FROM public.bill_headers
92
FROM public.bill_headers
95
WHERE bill_number = bill.bill_number
93
WHERE bill_number = bill.bill_number
96
AND company_id = bill.company_id
94
AND company_id = bill.company_id
97
) THEN
95
) THEN
98
RAISE NOTICE 'Duplicate: %, %', bill.bill_number, bill.company_id;
96
RAISE NOTICE 'Duplicate: %, %', bill.bill_number, bill.company_id;
99
v_status := 'duplicate';
97
v_status := 'duplicate';
100
CONTINUE;
98
CONTINUE;
101
END IF;
99
END IF;
102
100
103
-- Call the save routine
101
-- Call the save routine
104
CALL public.save_bill_and_details(
102
CALL public.save_bill_and_details(
105
bill.bill_id,
103
bill.bill_id,
106
bill.company_id,
104
bill.company_id,
107
bill.vendor_id,
105
bill.vendor_id,
108
bill.discount,
106
bill.discount,
109
bill.bill_date,
107
bill.bill_date,
110
bill.payment_term,
108
bill.payment_term,
111
bill.bill_status_id,
109
bill.bill_status_id,
112
bill.due_date,
110
bill.due_date,
113
bill.total_amount,
111
bill.total_amount,
114
bill.taxable_amount,
112
bill.taxable_amount,
115
bill.fees,
113
bill.fees,
116
bill.sgst_amount,
114
bill.sgst_amount,
117
bill.cgst_amount,
115
bill.cgst_amount,
118
bill.igst_amount,
116
bill.igst_amount,
119
bill.note,
117
bill.note,
120
bill.round_off,
118
bill.round_off,
121
bill.round_off_tds,
122
bill.debit_account_id,
119
bill.debit_account_id,
123
bill.credit_account_id,
120
bill.credit_account_id,
124
v_created_by,
121
v_created_by,
125
bill.currency_id,
122
bill.currency_id,
126
bill.po_date,
123
bill.po_date,
127
bill.po_no,
124
bill.po_no,
128
bill.destination_warehouse_id,
125
bill.destination_warehouse_id,
129
bill.source_warehouse_id,
126
bill.source_warehouse_id,
130
bill.bill_type_id,
127
bill.bill_type_id,
131
bill.bill_number,
128
bill.bill_number,
132
bill.tds_amount,
129
bill.tds_amount,
133
bill.lines
130
bill.lines
134
);
131
);
135
132
136
EXCEPTION
133
EXCEPTION
137
WHEN OTHERS THEN
134
WHEN OTHERS THEN
138
RAISE NOTICE 'Error bill %: %', bill.bill_id, SQLERRM;
135
RAISE NOTICE 'Error bill %: %', bill.bill_id, SQLERRM;
139
v_status := 'error';
136
v_status := 'error';
140
CONTINUE;
137
CONTINUE;
141
END;
138
END;
142
END LOOP;
139
END LOOP;
143
140
144
RETURN v_status;
141
RETURN v_status;
145
END;
142
END;
146
$function$
143
$function$
|
|||||
| Function | pg_get_tabledef | Match | ||||||
| Function | bulk_delete_bills_with_flag | Match | ||||||
| Function | get_bill_by_id | Mismatch |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.get_bill_by_id(p_bill_header_id uuid)
1
CREATE OR REPLACE FUNCTION public.get_bill_by_id(p_bill_header_id uuid)
2
RETURNS TABLE(id uuid, bill_number text, bill_voucher text, debit_account_id uuid, credit_account_id uuid, bill_date timestamp with time zone, payment_term integer, vendor_id uuid, vendor_name text, vendor_gstin text, vendor_short_name text, vendor_pan text, vendor_tan text, vendor_email text, vendor_phone_number text, vendor_mobile_number text, vendor_address_line1 text, vendor_address_line2 text, vendor_city_name text, vendor_state_name text, vendor_country_name text, vendor_zip_code text, due_date timestamp with time zone, total_amount numeric, taxable_amount numeric, fees numeric, cgst_amount numeric, sgst_amount numeric, igst_amount numeric, currency_id integer, bill_status_id integer, bill_status text, discount numeric, note text, round_off numeric, round_off_tds numeric, po_no text, po_date timestamp without time zone, destination_warehouse_id uuid, source_warehouse_id uuid, source_vendor_id uuid, source_warehouse_name text, source_address_id uuid, source_country_name text, source_country_id uuid, source_state_name text, source_state_id uuid, source_city_name text, source_city_id uuid, source_address_line1 text, source_address_line2 text, source_zip_code text, source_gstin text, bill_lines jsonb, bill_type_id integer, current_approval_level integer, tds_amount numeric, created_on timestamp with time zone, modified_on timestamp with time zone, created_by uuid, modified_by uuid)
2
RETURNS TABLE(id uuid, bill_number text, bill_voucher text, debit_account_id uuid, credit_account_id uuid, bill_date timestamp with time zone, payment_term integer, vendor_id uuid, vendor_name text, vendor_email text, vendor_mobile_number text, vendor_phone_number text, vendor_gstin text, vendor_short_name text, vendor_pan text, vendor_tan text, due_date timestamp with time zone, total_amount numeric, taxable_amount numeric, fees numeric, cgst_amount numeric, sgst_amount numeric, igst_amount numeric, currency_id integer, bill_status_id integer, bill_status text, discount numeric, note text, round_off numeric, po_no text, po_date timestamp without time zone, destination_warehouse_id uuid, source_warehouse_id uuid, source_vendor_id uuid, source_warehouse_name text, source_address_id uuid, source_country_name text, source_country_id uuid, source_state_name text, source_state_id uuid, source_city_name text, source_city_id uuid, source_address_line1 text, source_address_line2 text, source_zip_code text, source_gstin text, bill_lines jsonb, bill_type_id integer, current_approval_level integer, tds_amount numeric, created_on timestamp with time zone, modified_on timestamp with time zone, created_by uuid, modified_by uuid)
3
LANGUAGE plpgsql
3
LANGUAGE plpgsql
4
AS $function$
4
AS $function$
5
6
DECLARE
5
DECLARE
7
v_bill_status_id INTEGER;
6
v_bill_status_id INTEGER;
8
BEGIN
7
BEGIN
8
-- Get the bill status using a helper function
9
v_bill_status_id := public.get_bill_status(p_bill_header_id);
9
v_bill_status_id := public.get_bill_status(p_bill_header_id);
10
10
11
IF v_bill_status_id >= 3 THEN
11
IF v_bill_status_id >= 3 THEN
12
-- Query for Approved Bills
12
RETURN QUERY
13
RETURN QUERY
13
SELECT
14
SELECT
14
bh.id,
15
bh.id,
15
bh.bill_number::text,
16
bh.bill_number,
16
bh.bill_voucher::text,
17
bh.bill_voucher,
17
bh.debit_account_id,
18
bh.debit_account_id,
18
bh.credit_account_id,
19
bh.credit_account_id,
19
bh.bill_date::TIMESTAMP WITH TIME ZONE,
20
bh.bill_date::TIMESTAMP WITH TIME ZONE,
20
bh.payment_term,
21
bh.payment_term,
21
bh.vendor_id,
22
bh.vendor_id,
22
v.name::text AS vendor_name,
23
v.name::text AS vendor_name,
24
v.email::text AS vendor_email,
25
v.mobile_number::text AS vendor_mobile_number,
26
v.phone_number::text AS vendor_phone_number,
23
v.gstin::text AS vendor_gstin,
27
v.gstin::text AS vendor_gstin,
24
v.short_name::text AS vendor_short_name,
28
v.short_name::text AS vendor_short_name,
25
v.pan::text AS vendor_pan,
29
v.pan::text AS vendor_pan,
26
v.tan::text AS vendor_tan,
30
v.tan::text AS vendor_tan,
27
co.email::text AS vendor_email,
28
co.phone_number::text AS vendor_phone_number,
29
co.mobile_number::text AS vendor_mobile_number,
30
addr.address_line1::text AS vendor_address_line1,
31
addr.address_line2::text AS vendor_address_line2,
32
cit.name::text AS vendor_city_name,
33
st.name::text AS vendor_state_name,
34
ctry.name::text AS vendor_country_name,
35
addr.zip_code::text AS vendor_zip_code,
36
bh.due_date::TIMESTAMP WITH TIME ZONE,
31
bh.due_date::TIMESTAMP WITH TIME ZONE,
37
bh.total_amount,
32
bh.total_amount,
38
bh.taxable_amount,
33
bh.taxable_amount,
39
bh.fees,
34
bh.fees,
40
bh.cgst_amount,
35
bh.cgst_amount,
41
bh.sgst_amount,
36
bh.sgst_amount,
42
bh.igst_amount,
37
bh.igst_amount,
43
bh.currency_id,
38
bh.currency_id,
44
bh.bill_status_id,
39
bh.bill_status_id,
45
blst.name::text,
40
blst.name::text,
46
bh.discount,
41
bh.discount,
47
bh.note::text,
42
bh.note,
48
bh.round_off,
43
bh.round_off,
49
bh.round_off_tds,
44
bh.po_no,
50
bh.po_no::text,
51
bh.po_date::TIMESTAMP WITHOUT TIME ZONE,
45
bh.po_date::TIMESTAMP WITHOUT TIME ZONE,
52
bh.destination_warehouse_id,
46
bh.destination_warehouse_id,
53
wh.id AS source_warehouse_id,
47
wh.id AS source_warehouse_id,
54
wh.vendor_id AS source_vendor_id,
48
wh.vendor_id AS source_vendor_id,
55
wh.name::text AS source_warehouse_name,
49
wh.name AS source_warehouse_name,
56
wh.address_id AS source_address_id,
50
wh.address_id AS source_address_id,
57
wh.country_name::text AS source_country_name,
51
wh.country_name AS source_country_name,
58
wh.country_id AS source_country_id,
52
wh.country_id AS source_country_id,
59
wh.state_name::text AS source_state_name,
53
wh.state_name AS source_state_name,
60
wh.state_id AS source_state_id,
54
wh.state_id AS source_state_id,
61
wh.city_name::text AS source_city_name,
55
wh.city_name AS source_city_name,
62
wh.city_id AS source_city_id,
56
wh.city_id AS source_city_id,
63
wh.address_line1::text AS source_address_line1,
57
wh.address_line1 AS source_address_line1,
64
wh.address_line2::text AS source_address_line2,
58
wh.address_line2 AS source_address_line2,
65
wh.zip_code::text AS source_zip_code,
59
wh.zip_code AS source_zip_code,
66
wh.gstin::text AS source_gstin,
60
wh.gstin::text AS source_gstin,
67
jsonb_agg(jsonb_build_object(
61
jsonb_agg(jsonb_build_object(
68
'bill_line_id', bl.id,
62
'bill_line_id', bl.id,
69
'product_id', bl.product_id,
63
'product_id', bl.product_id,
70
'price', bl.price,
64
'price', bl.price,
71
'quantity', bl.quantity,
65
'quantity', bl.quantity,
72
'fees', bl.fees,
66
'fees', bl.fees,
73
'discount', bl.discount,
67
'discount', bl.discount,
74
'taxable_amount', bl.taxable_amount,
68
'taxable_amount', bl.taxable_amount,
75
'sgst_amount', bl.sgst_amount,
69
'sgst_amount', bl.sgst_amount,
76
'cgst_amount', bl.cgst_amount,
70
'cgst_amount', bl.cgst_amount,
77
'igst_amount', bl.igst_amount,
71
'igst_amount', bl.igst_amount,
78
'total_amount', bl.total_amount,
72
'total_amount', bl.total_amount,
79
'serial_number', bl.serial_number
73
'serial_number', bl.serial_number
80
) ORDER BY bl.serial_number) AS bill_lines,
74
) ORDER BY bl.serial_number) AS bill_lines,
81
bh.bill_type_id,
75
bh.bill_type_id,
82
bh.current_approval_level,
76
bh.current_approval_level,
83
bh.tds_amount,
77
bh.tds_amount,
84
bh.created_on_utc::TIMESTAMP WITH TIME ZONE,
78
bh.created_on_utc::TIMESTAMP WITH TIME ZONE,
85
bh.modified_on_utc::TIMESTAMP WITH TIME ZONE,
79
bh.modified_on_utc::TIMESTAMP WITH TIME ZONE,
86
bh.created_by,
80
bh.created_by,
87
bh.modified_by
81
bh.modified_by
88
FROM bill_headers bh
82
FROM bill_headers bh
89
LEFT JOIN public.vendors v ON bh.vendor_id = v.id
83
LEFT JOIN public.get_vendor_details(bh.vendor_id) v ON TRUE
90
LEFT JOIN public.vendor_contacts vc ON v.id = vc.vendor_id AND vc.is_deleted = false
91
LEFT JOIN public.contacts co ON vc.contact_id = co.id AND co.is_deleted = false AND co.is_primary = true
92
LEFT JOIN public.addresses addr ON v.billing_address_id = addr.id
93
LEFT JOIN public.cities cit ON addr.city_id = cit.id
94
LEFT JOIN public.states st ON addr.state_id = st.id
95
LEFT JOIN public.countries ctry ON addr.country_id = ctry.id
96
LEFT JOIN public.get_warehouse_details(bh.source_warehouse_id) wh ON TRUE
84
LEFT JOIN public.get_warehouse_details(bh.source_warehouse_id) wh ON TRUE
97
LEFT JOIN public.get_bill_lines(bh.id, bh.bill_status_id) bl ON TRUE
85
LEFT JOIN public.get_bill_lines(bh.id, bh.bill_status_id) bl ON TRUE
98
JOIN public.bill_statuses blst ON bh.bill_status_id = blst.id
86
JOIN public.bill_statuses blst ON bh.bill_status_id = blst.id
99
WHERE bh.id = p_bill_header_id
87
WHERE bh.id = p_bill_header_id
100
GROUP BY
88
GROUP BY
101
bh.id,
89
bh.id,
102
bh.bill_number,
90
bh.bill_number,
103
bh.bill_voucher,
91
bh.bill_voucher,
104
bh.bill_date,
92
bh.bill_date,
105
bh.payment_term,
93
bh.payment_term,
106
bh.vendor_id,
94
bh.vendor_id,
107
v.name,
95
v.name,
96
v.email,
97
v.mobile_number,
98
v.phone_number,
108
v.gstin,
99
v.gstin,
109
v.short_name,
100
v.short_name,
110
v.pan,
101
v.pan,
111
v.tan,
102
v.tan,
112
co.email,
113
co.phone_number,
114
co.mobile_number,
115
addr.address_line1,
116
addr.address_line2,
117
cit.name,
118
st.name,
119
ctry.name,
120
addr.zip_code,
121
bh.due_date,
103
bh.due_date,
122
bh.total_amount,
104
bh.total_amount,
123
bh.taxable_amount,
105
bh.taxable_amount,
124
bh.fees,
106
bh.fees,
125
bh.cgst_amount,
107
bh.cgst_amount,
126
bh.sgst_amount,
108
bh.sgst_amount,
127
bh.igst_amount,
109
bh.igst_amount,
128
bh.currency_id,
110
bh.currency_id,
129
bh.bill_status_id,
111
bh.bill_status_id,
130
blst.name,
112
blst.name,
131
bh.discount,
113
bh.discount,
132
bh.note,
114
bh.note,
133
bh.round_off,
115
bh.round_off,
134
bh.round_off_tds,
135
bh.po_no,
116
bh.po_no,
136
bh.po_date,
117
bh.po_date,
137
bh.destination_warehouse_id,
118
bh.destination_warehouse_id,
138
bh.source_warehouse_id,
119
bh.source_warehouse_id,
139
wh.id,
120
wh.id,
140
wh.vendor_id,
121
wh.vendor_id,
141
wh.name,
122
wh.name,
142
wh.address_id,
123
wh.address_id,
143
wh.country_name,
124
wh.country_name,
144
wh.country_id,
125
wh.country_id,
145
wh.state_name,
126
wh.state_name,
146
wh.state_id,
127
wh.state_id,
147
wh.city_name,
128
wh.city_name,
148
wh.city_id,
129
wh.city_id,
149
wh.address_line1,
130
wh.address_line1,
150
wh.address_line2,
131
wh.address_line2,
151
wh.zip_code,
132
wh.zip_code,
152
wh.gstin,
133
wh.gstin,
153
bh.bill_type_id,
134
bh.bill_type_id,
154
bh.current_approval_level,
135
bh.current_approval_level,
155
bh.tds_amount,
136
bh.tds_amount,
156
bh.created_on_utc,
137
bh.created_on_utc,
157
bh.modified_on_utc,
138
bh.modified_on_utc,
158
bh.created_by,
139
bh.created_by,
159
bh.modified_by;
140
bh.modified_by;
160
141
161
ELSE
142
ELSE
162
-- You can add the similar query for draft bill_headers here
143
-- Query for Draft Bills (similar structure as approved bills)
163
RETURN QUERY
144
RETURN QUERY
164
SELECT
145
SELECT
165
dbh.id,
146
dbh.id,
166
dbh.bill_number::text,
147
dbh.bill_number,
167
dbh.bill_voucher::text,
148
dbh.bill_voucher,
168
dbh.debit_account_id,
149
dbh.debit_account_id,
169
dbh.credit_account_id,
150
dbh.credit_account_id,
170
dbh.bill_date::TIMESTAMP WITH TIME ZONE,
151
dbh.bill_date::TIMESTAMP WITH TIME ZONE,
171
dbh.payment_term,
152
dbh.payment_term,
172
dbh.vendor_id,
153
dbh.vendor_id,
173
v.name::text AS vendor_name,
154
v.name::text AS vendor_name,
155
v.email::text AS vendor_email,
156
v.mobile_number::text AS vendor_mobile_number,
157
v.phone_number::text AS vendor_phone_number,
174
v.gstin::text AS vendor_gstin,
158
v.gstin::text AS vendor_gstin,
175
v.short_name::text AS vendor_short_name,
159
v.short_name AS vendor_short_name,
176
v.pan::text AS vendor_pan,
160
v.pan::text AS vendor_pan,
177
v.tan::text AS vendor_tan,
161
v.tan::text AS vendor_tan,
178
co.email::text AS vendor_email,
179
co.phone_number::text AS vendor_phone_number,
180
co.mobile_number::text AS vendor_mobile_number,
181
addr.address_line1::text,
182
addr.address_line2::text,
183
cit.name::text AS vendor_city_name,
184
st.name::text AS vendor_state_name,
185
ctry.name::text AS vendor_country_name,
186
addr.zip_code::text,
187
dbh.due_date::TIMESTAMP WITH TIME ZONE,
162
dbh.due_date::TIMESTAMP WITH TIME ZONE,
188
dbh.total_amount,
163
dbh.total_amount,
189
dbh.taxable_amount,
164
dbh.taxable_amount,
190
dbh.fees,
165
dbh.fees,
191
dbh.cgst_amount,
166
dbh.cgst_amount,
192
dbh.sgst_amount,
167
dbh.sgst_amount,
193
dbh.igst_amount,
168
dbh.igst_amount,
194
dbh.currency_id,
169
dbh.currency_id,
195
dbh.bill_status_id,
170
dbh.bill_status_id,
196
dblst.name::text,
171
dblst.name::text,
197
dbh.discount,
172
dbh.discount,
198
dbh.note::text,
173
dbh.note,
199
dbh.round_off,
174
dbh.round_off,
200
dbh.round_off_tds,
175
dbh.po_no,
201
dbh.po_no::text,
176
dbh.po_date,
202
dbh.po_date::TIMESTAMP WITHOUT TIME ZONE,
203
dbh.destination_warehouse_id,
177
dbh.destination_warehouse_id,
204
wh.id AS source_warehouse_id,
178
wh.id AS source_warehouse_id,
205
wh.vendor_id AS source_vendor_id,
179
wh.vendor_id AS source_vendor_id,
206
wh.name::text AS source_warehouse_name,
180
wh.name AS source_warehouse_name,
207
wh.address_id AS source_address_id,
181
wh.address_id AS source_address_id,
208
wh.country_name::text AS source_country_name,
182
wh.country_name AS source_country_name,
209
wh.country_id AS source_country_id,
183
wh.country_id AS source_country_id,
210
wh.state_name::text AS source_state_name,
184
wh.state_name AS source_state_name,
211
wh.state_id AS source_state_id,
185
wh.state_id AS source_state_id,
212
wh.city_name::text AS source_city_name,
186
wh.city_name AS source_city_name,
213
wh.city_id AS source_city_id,
187
wh.city_id AS source_city_id,
214
wh.address_line1::text AS source_address_line1,
188
wh.address_line1 AS source_address_line1,
215
wh.address_line2::text AS source_address_line2,
189
wh.address_line2 AS source_address_line2,
216
wh.zip_code::text AS source_zip_code,
190
wh.zip_code AS source_zip_code,
217
wh.gstin::text AS source_gstin,
191
wh.gstin AS source_gstin,
218
jsonb_agg(jsonb_build_object(
192
jsonb_agg(jsonb_build_object(
219
'bill_line_id', dbl.id,
193
'bill_line_id', dbl.id,
220
'product_id', dbl.product_id,
194
'product_id', dbl.product_id,
221
'price', dbl.price,
195
'price', dbl.price,
222
'quantity', dbl.quantity,
196
'quantity', dbl.quantity,
223
'fees', dbl.fees,
197
'fees', dbl.fees,
224
'discount', dbl.discount,
198
'discount', dbl.discount,
225
'taxable_amount', dbl.taxable_amount,
199
'taxable_amount', dbl.taxable_amount,
226
'sgst_amount', dbl.sgst_amount,
200
'sgst_amount', dbl.sgst_amount,
227
'cgst_amount', dbl.cgst_amount,
201
'cgst_amount', dbl.cgst_amount,
228
'igst_amount', dbl.igst_amount,
202
'igst_amount', dbl.igst_amount,
229
'total_amount', dbl.total_amount,
203
'total_amount', dbl.total_amount,
230
'serial_number', dbl.serial_number
204
'serial_number', dbl.serial_number
231
) ORDER BY dbl.serial_number) AS bill_lines,
205
) ORDER BY dbl.serial_number) AS bill_lines,
232
dbh.bill_type_id,
206
dbh.bill_type_id,
233
dbh.current_approval_level,
207
dbh.current_approval_level,
234
dbh.tds_amount,
208
dbh.tds_amount,
235
dbh.created_on_utc::TIMESTAMP WITH TIME ZONE,
209
dbh.created_on_utc::TIMESTAMP WITH TIME ZONE,
236
dbh.modified_on_utc::TIMESTAMP WITH TIME ZONE,
210
dbh.modified_on_utc::TIMESTAMP WITH TIME ZONE,
237
dbh.created_by,
211
dbh.created_by,
238
dbh.modified_by
212
dbh.modified_by
239
FROM draft_bill_headers dbh
213
FROM draft_bill_headers dbh
240
LEFT JOIN public.vendors v ON dbh.vendor_id = v.id
214
LEFT JOIN public.get_vendor_details(dbh.vendor_id) v ON TRUE
241
LEFT JOIN public.vendor_contacts vc ON v.id = vc.vendor_id AND vc.is_deleted = false
242
LEFT JOIN public.contacts co ON vc.contact_id = co.id AND co.is_deleted = false AND co.is_primary = true
243
LEFT JOIN public.addresses addr ON v.billing_address_id = addr.id
244
LEFT JOIN public.cities cit ON addr.city_id = cit.id
245
LEFT JOIN public.states st ON addr.state_id = st.id
246
LEFT JOIN public.countries ctry ON addr.country_id = ctry.id
247
LEFT JOIN public.get_warehouse_details(dbh.source_warehouse_id) wh ON TRUE
215
LEFT JOIN public.get_warehouse_details(dbh.source_warehouse_id) wh ON TRUE
248
LEFT JOIN public.get_bill_lines(dbh.id, dbh.bill_status_id) dbl ON TRUE
216
LEFT JOIN public.get_bill_lines(dbh.id, dbh.bill_status_id) dbl ON TRUE
249
JOIN public.bill_statuses dblst ON dbh.bill_status_id = dblst.id
217
JOIN public.bill_statuses dblst ON dbh.bill_status_id = dblst.id
250
WHERE dbh.id = p_bill_header_id
218
WHERE dbh.id = p_bill_header_id
251
GROUP BY
219
GROUP BY
252
dbh.id,
220
dbh.id,
253
dbh.bill_number,
221
dbh.bill_number,
254
dbh.bill_voucher,
222
dbh.bill_voucher,
255
dbh.bill_date,
223
dbh.bill_date,
256
dbh.payment_term,
224
dbh.payment_term,
257
dbh.vendor_id,
225
dbh.vendor_id,
258
v.name,
226
v.name, v.email,
227
v.mobile_number,
228
v.phone_number,
259
v.gstin,
229
v.gstin,
260
v.short_name,
230
v.short_name,
261
v.pan,
231
v.pan,
262
v.tan,
232
v.tan,
263
co.email,
264
co.phone_number,
265
co.mobile_number,
266
addr.address_line1,
267
addr.address_line2,
268
cit.name,
269
st.name,
270
ctry.name,
271
addr.zip_code,
272
dbh.due_date,
233
dbh.due_date,
273
dbh.total_amount,
234
dbh.total_amount,
274
dbh.taxable_amount,
235
dbh.taxable_amount,
275
dbh.fees,
236
dbh.fees,
276
dbh.cgst_amount,
237
dbh.cgst_amount,
277
dbh.sgst_amount,
238
dbh.sgst_amount,
278
dbh.igst_amount,
239
dbh.igst_amount,
279
dbh.currency_id,
240
dbh.currency_id,
280
dbh.bill_status_id,
241
dbh.bill_status_id,
281
dblst.name,
242
dblst.name,
282
dbh.discount,
243
dbh.discount,
283
dbh.note,
244
dbh.note,
284
dbh.round_off,
245
dbh.round_off,
285
dbh.round_off_tds,
286
dbh.destination_warehouse_id,
246
dbh.destination_warehouse_id,
287
dbh.po_no,
247
dbh.po_no,
288
dbh.po_date,
248
dbh.po_date,
289
dbh.source_warehouse_id,
249
dbh.source_warehouse_id,
290
wh.id,
250
wh.id,
291
wh.vendor_id,
251
wh.vendor_id,
292
wh.name,
252
wh.name,
293
wh.address_id,
253
wh.address_id,
294
wh.country_name,
254
wh.country_name,
295
wh.country_id,
255
wh.country_id,
296
wh.state_name,
256
wh.state_name,
297
wh.state_id,
257
wh.state_id,
298
wh.city_name,
258
wh.city_name,
299
wh.city_id,
259
wh.city_id,
300
wh.address_line1,
260
wh.address_line1,
301
wh.address_line2,
261
wh.address_line2,
302
wh.zip_code,
262
wh.zip_code,
303
wh.gstin,
263
wh.gstin,
304
dbh.bill_type_id,
264
dbh.bill_type_id,
305
dbh.current_approval_level,
265
dbh.current_approval_level,
306
dbh.tds_amount,
266
dbh.tds_amount,
307
dbh.created_on_utc,
267
dbh.created_on_utc,
308
dbh.modified_on_utc,
268
dbh.modified_on_utc,
309
dbh.created_by,
269
dbh.created_by,
310
dbh.modified_by;
270
dbh.modified_by;
311
END IF;
271
END IF;
312
END;
272
END;
313
$function$
273
$function$
|
|||||
| Function | update_bill_next_status_main | Match | ||||||
| Function | list_scheduled_bill_ids | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.list_scheduled_bill_ids(p_schedule_date timestamp without time zone DEFAULT CURRENT_TIMESTAMP)
2
RETURNS TABLE(bill_header_id uuid, draft_bill_header_id uuid, company_id uuid, organization_id uuid)
3
LANGUAGE sql
4
AS $function$
5
WITH base AS (
6
SELECT
7
s.id AS schedule_id,
8
s.bill_header_id AS bill_header_id,
9
s.draft_bill_header_id AS draft_bill_header_id,
10
s.company_id AS company_id,
11
c.organization_id AS organization_id,
12
d.frequency_cycle,
13
d.day_of_week,
14
d.day_of_month,
15
d.month_of_year,
16
s.starts_from,
17
s.end_date
18
FROM public.recurring_bill_schedules s
19
JOIN public.recurrence_bill_schedule_details d
20
ON d.schedule_id = s.id
21
AND d.is_deleted = false
22
JOIN public.companies c
23
ON c.id = s.company_id
24
WHERE s.is_deleted = false
25
AND s.schedule_status = 'active'
26
AND s.starts_from <= (p_schedule_date::date)
27
AND (s.end_date IS NULL OR s.end_date >= (p_schedule_date::date))
28
),
29
aligned AS (
30
SELECT
31
b.schedule_id,
32
b.bill_header_id,
33
b.draft_bill_header_id,
34
b.company_id,
35
b.organization_id
36
FROM base b
37
WHERE CASE lower(b.frequency_cycle)
38
WHEN 'daily' THEN TRUE
39
WHEN 'weekly' THEN b.day_of_week IS NOT NULL
40
AND b.day_of_week = EXTRACT(ISODOW FROM p_schedule_date)::int
41
WHEN 'monthly' THEN b.day_of_month IS NOT NULL
42
AND b.day_of_month = EXTRACT(DAY FROM p_schedule_date)::int
43
WHEN 'yearly' THEN b.month_of_year IS NOT NULL AND b.day_of_month IS NOT NULL
44
AND b.month_of_year = EXTRACT(MONTH FROM p_schedule_date)::int
45
AND b.day_of_month = EXTRACT(DAY FROM p_schedule_date)::int
46
ELSE FALSE
47
END
48
)
49
SELECT DISTINCT -- 👈 this was missing in your version
50
a.bill_header_id,
51
a.draft_bill_header_id,
52
a.company_id,
53
a.organization_id
54
FROM aligned a
55
WHERE NOT EXISTS (
56
SELECT 1
57
FROM public.schedule_execution_logs l
58
WHERE l.schedule_id = a.schedule_id
59
AND l.execution_date = (p_schedule_date::date)
60
AND l.is_deleted = false
61
);
62
$function$
|
|||||
| Function | create_vendor_advance_settlements_123 | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.create_vendor_advance_settlements_123(p_company_id uuid, p_vendor_id uuid, p_bill_payment_header_id uuid, p_created_by uuid, p_settlements jsonb)
2
RETURNS TABLE(id uuid, advance_settlement_number text)
3
LANGUAGE plpgsql
4
AS $function$
5
DECLARE
6
v_settlement_number text;
7
v_row jsonb;
8
v_id uuid;
9
BEGIN
10
FOR v_row IN SELECT * FROM jsonb_array_elements(p_settlements)
11
LOOP
12
v_settlement_number := public.get_new_vendor_advance_settlement_number(
13
p_company_id,
14
COALESCE((v_row->>'settled_date')::date, CURRENT_DATE)
15
);
16
v_id := (v_row->>'id')::uuid;
17
18
INSERT INTO vendor_advance_settlements
19
(
20
id,
21
company_id,
22
vendor_id,
23
advance_id,
24
bill_id,
25
apply_amount,
26
applied_in_payment_id,
27
settled_date,
28
note,
29
created_on_utc,
30
created_by,
31
is_deleted,
32
advance_settlement_number
33
)
34
VALUES
35
(
36
v_id,
37
p_company_id,
38
p_vendor_id,
39
(v_row->>'advance_id')::uuid,
40
(v_row->>'bill_id')::uuid,
41
(v_row->>'apply_amount')::numeric,
42
p_bill_payment_header_id,
43
COALESCE((v_row->>'settled_date')::timestamp, now()),
44
NULLIF(v_row->>'note',''),
45
now(),
46
p_created_by,
47
false,
48
v_settlement_number
49
);
50
51
UPDATE vendor_advances va
52
SET utilized_amount = utilized_amount + (v_row->>'apply_amount')::numeric,
53
modified_by = p_created_by,
54
modified_on_utc = now()
55
WHERE va.id = (v_row->>'advance_id')::uuid
56
AND va.company_id = p_company_id
57
AND va.vendor_id = p_vendor_id
58
AND va.is_deleted = false;
59
60
-- Assign to output variables, then RETURN NEXT;
61
id := v_id;
62
advance_settlement_number := v_settlement_number;
63
RETURN NEXT;
64
END LOOP;
65
END;
66
$function$
|
|||||
| Function | get_vendor_by_id | Match | ||||||
| Function | get_new_draft_bill_voucher | Match | ||||||
| Function | get_bill_payments_by_company | Match | ||||||
| Function | get_all_bill_account_approvals | Match | ||||||
| Function | upsert_bill_status_company_config | Match | ||||||
| Function | get_all_vendors | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.get_all_vendors(p_company_id uuid, p_fin_year_id integer, p_is_get_all boolean)
2
RETURNS TABLE(id uuid, name character varying, contact_name text, gst_in text, mobile_number text, email text, created_by uuid, created_by_name text, modified_by uuid, modified_by_name text, created_on_utc timestamp without time zone, modified_on_utc timestamp without time zone, billing_address jsonb, contact_count integer, total_billed_amount numeric, total_vendor_paid_amount numeric, total_tds_paid_amount numeric)
3
LANGUAGE plpgsql
4
AS $function$
5
DECLARE
6
v_organization_id uuid;
7
v_start_date date := MAKE_DATE(p_fin_year_id, 4, 1);
8
v_end_date date := MAKE_DATE(p_fin_year_id + 1, 3, 31);
9
BEGIN
10
SELECT com.organization_id INTO v_organization_id
11
FROM public.companies com
12
WHERE com.id = p_company_id;
13
14
RETURN QUERY
15
SELECT
16
v.id,
17
v.name,
18
con.first_name || ' ' || con.last_name AS contact_name,
19
v.gstin AS gst_in,
20
con.mobile_number,
21
con.email,
22
v.created_by,
23
u_created.first_name || ' ' || u_created.last_name AS created_by_name,
24
v.modified_by,
25
u_modified.first_name || ' ' || u_modified.last_name AS modified_by_name,
26
v.created_on_utc,
27
v.modified_on_utc,
28
CASE
29
WHEN ba.id IS NOT NULL THEN jsonb_build_object(
30
'AddressLine1', ba.address_line1,
31
'AddressLine2', ba.address_line2,
32
'ZipCode', ba.zip_code,
33
'CountryId', ba.country_id,
34
'StateId', ba.state_id,
35
'CityId', ba.city_id
36
)
37
ELSE NULL
38
END AS billing_address,
39
(SELECT COUNT(*)::integer
40
FROM public.vendor_contacts vc_count
41
WHERE vc_count.vendor_id = v.id) AS contact_count,
42
COALESCE(bill_sums.total_billed_amount, 0) AS total_billed_amount,
43
COALESCE(bill_sums.total_vendor_paid_amount, 0) AS total_vendor_paid_amount,
44
COALESCE(bill_sums.total_tds_paid_amount, 0) AS total_tds_paid_amount
45
FROM public.vendors v
46
JOIN public.vendor_contacts vc ON v.id = vc.vendor_id
47
JOIN public.contacts con ON vc.contact_id = con.id
48
LEFT JOIN public.users u_created ON v.created_by = u_created.id
49
LEFT JOIN public.users u_modified ON v.modified_by = u_modified.id
50
LEFT JOIN public.addresses ba ON v.billing_address_id = ba.id
51
LEFT JOIN (
52
SELECT
53
b.vendor_id,
54
SUM(b.total_amount) AS total_billed_amount,
55
SUM(b.vendor_paid_amount) AS total_vendor_paid_amount,
56
SUM(b.tds_paid_amount) AS total_tds_paid_amount
57
FROM public.bill_headers b
58
WHERE b.company_id = p_company_id
59
AND b.is_deleted = false
60
AND b.bill_date >= v_start_date
61
AND b.bill_date <= v_end_date
62
GROUP BY b.vendor_id
63
) bill_sums ON bill_sums.vendor_id = v.id
64
WHERE v.company_id IN (
65
SELECT c.id FROM public.companies c
66
WHERE (p_is_get_all AND c.organization_id = v_organization_id)
67
OR (NOT p_is_get_all AND c.id = p_company_id)
68
)
69
AND v.is_deleted = false
70
AND con.is_primary = true;
71
END;
72
$function$
|
|||||
| Function | get_bill_analytics | Mismatch |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.get_bill_analytics(billing_company_id uuid, p_finance_year_id integer, p_bill_type_id integer DEFAULT NULL::integer, 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)
1
CREATE OR REPLACE FUNCTION public.get_bill_analytics(billing_company_id uuid, p_finance_year_id integer, p_bill_type_id integer DEFAULT NULL::integer, 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(status text, count integer, total_amount numeric, month_start_date date)
2
RETURNS TABLE(status text, count integer, total_amount numeric, month_start_date date)
3
LANGUAGE plpgsql
3
LANGUAGE plpgsql
4
AS $function$
4
AS $function$
5
DECLARE
5
DECLARE
6
v_financial_year_start DATE;
6
v_financial_year_start DATE;
7
v_financial_year_end DATE;
7
v_financial_year_end DATE;
8
v_date_start DATE;
8
v_date_start DATE;
9
v_date_end DATE;
9
v_date_end DATE;
10
BEGIN
10
BEGIN
11
-- Set default financial year range
11
-- Set default financial year range
12
v_financial_year_start := TO_DATE(p_finance_year_id || '-04-01', 'YYYY-MM-DD');
12
v_financial_year_start := TO_DATE(p_finance_year_id || '-04-01', 'YYYY-MM-DD');
13
v_financial_year_end := TO_DATE((p_finance_year_id + 1) || '-03-31', 'YYYY-MM-DD');
13
v_financial_year_end := TO_DATE((p_finance_year_id + 1) || '-03-31', 'YYYY-MM-DD');
14
14
15
-- Use date range override if provided, otherwise use financial year range
15
-- Use date range override if provided, otherwise use financial year range
16
v_date_start := COALESCE(p_start_date::DATE, v_financial_year_start);
16
v_date_start := COALESCE(p_start_date::DATE, v_financial_year_start);
17
v_date_end := COALESCE(p_end_date::DATE, v_financial_year_end);
17
v_date_end := COALESCE(p_end_date::DATE, v_financial_year_end);
18
18
19
RETURN QUERY
19
RETURN QUERY
20
20
21
-- PAID
21
-- PAID
22
SELECT 'Paid' AS status,
22
SELECT 'Paid' AS status,
23
COUNT(*)::INTEGER AS count,
23
COUNT(*)::INTEGER AS count,
24
COALESCE(SUM(b.vendor_paid_amount), 0) AS total_amount,
24
COALESCE(SUM(b.setteled_amount), 0) AS total_amount,
25
DATE_TRUNC('month', b.bill_date)::DATE AS month_start_date
25
DATE_TRUNC('month', b.bill_date)::DATE AS month_start_date
26
FROM bill_headers b
26
FROM bill_headers b
27
WHERE b.company_id = billing_company_id
27
WHERE b.company_id = billing_company_id
28
AND b.bill_status_id IN (4, 5)
28
AND b.bill_status_id IN (4, 5)
29
AND b.is_deleted = false
29
AND b.is_deleted = false
30
AND b.bill_date BETWEEN v_date_start AND v_date_end
30
AND b.bill_date BETWEEN v_date_start AND v_date_end
31
AND (p_bill_type_id IS NULL OR b.bill_type_id = p_bill_type_id)
31
AND (p_bill_type_id IS NULL OR b.bill_type_id = p_bill_type_id)
32
GROUP BY month_start_date
32
GROUP BY month_start_date
33
33
34
UNION ALL
34
UNION ALL
35
35
36
-- PENDING
36
-- PENDING
37
SELECT 'Pending' AS status,
37
SELECT 'Pending' AS status,
38
COUNT(*)::INTEGER AS count,
38
COUNT(*)::INTEGER AS count,
39
COALESCE(SUM(b.total_amount - b.vendor_paid_amount), 0) AS total_amount,
39
COALESCE(SUM(b.total_amount - b.setteled_amount), 0) AS total_amount,
40
DATE_TRUNC('month', b.bill_date)::DATE AS month_start_date
40
DATE_TRUNC('month', b.bill_date)::DATE AS month_start_date
41
FROM bill_headers b
41
FROM bill_headers b
42
WHERE b.company_id = billing_company_id
42
WHERE b.company_id = billing_company_id
43
AND b.bill_status_id IN (3, 4)
43
AND b.bill_status_id IN (3, 4)
44
AND b.is_deleted = false
44
AND b.is_deleted = false
45
AND b.bill_date BETWEEN v_date_start AND v_date_end
45
AND b.bill_date BETWEEN v_date_start AND v_date_end
46
AND (p_bill_type_id IS NULL OR b.bill_type_id = p_bill_type_id)
46
AND (p_bill_type_id IS NULL OR b.bill_type_id = p_bill_type_id)
47
GROUP BY month_start_date
47
GROUP BY month_start_date
48
48
49
UNION ALL
49
UNION ALL
50
50
51
-- OVERDUE
51
-- OVERDUE
52
SELECT 'Overdue' AS status,
52
SELECT 'Overdue' AS status,
53
COUNT(*)::INTEGER AS count,
53
COUNT(*)::INTEGER AS count,
54
COALESCE(SUM(b.total_amount - b.vendor_paid_amount), 0) AS total_amount,
54
COALESCE(SUM(b.total_amount - b.setteled_amount), 0) AS total_amount,
55
DATE_TRUNC('month', b.bill_date)::DATE AS month_start_date
55
DATE_TRUNC('month', b.bill_date)::DATE AS month_start_date
56
FROM bill_headers b
56
FROM bill_headers b
57
WHERE b.company_id = billing_company_id
57
WHERE b.company_id = billing_company_id
58
AND b.bill_status_id IN (3, 4)
58
AND b.bill_status_id IN (3, 4)
59
AND b.is_deleted = false
59
AND b.is_deleted = false
60
AND b.due_date < CURRENT_DATE
60
AND b.due_date < CURRENT_DATE
61
AND b.bill_date BETWEEN v_date_start AND v_date_end
61
AND b.bill_date BETWEEN v_date_start AND v_date_end
62
AND (p_bill_type_id IS NULL OR b.bill_type_id = p_bill_type_id)
62
AND (p_bill_type_id IS NULL OR b.bill_type_id = p_bill_type_id)
63
GROUP BY month_start_date;
63
GROUP BY month_start_date;
64
END;
64
END;
65
$function$
65
$function$
|
|||||
| Function | get_bill_approval_log | Match | ||||||
| Function | get_new_vendor_advance_number | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.get_new_vendor_advance_number(p_company_id uuid, p_date date)
2
RETURNS character varying
3
LANGUAGE plpgsql
4
AS $function$
5
DECLARE
6
v_finance_year integer;
7
new_advance_id integer;
8
p_prefix varchar;
9
advance_number varchar;
10
BEGIN
11
-- Determine the start year of the financial year based on p_date
12
IF EXTRACT(MONTH FROM p_date) >= 4 THEN
13
v_finance_year := EXTRACT(YEAR FROM p_date);
14
ELSE
15
v_finance_year := EXTRACT(YEAR FROM p_date) - 1;
16
END IF;
17
18
-- Attempt to update existing row or insert if not found
19
WITH x AS (
20
UPDATE public.vendor_advance_ids
21
SET last_advance_id = last_advance_id + 1
22
WHERE company_id = p_company_id AND fin_year = v_finance_year
23
RETURNING last_advance_id, advance_prefix
24
),
25
insert_x AS (
26
INSERT INTO public.vendor_advance_ids (
27
company_id, fin_year, advance_prefix, last_advance_id, advance_length
28
)
29
SELECT p_company_id, v_finance_year, 'VADV', 1, 8
30
WHERE NOT EXISTS (SELECT 1 FROM x)
31
RETURNING last_advance_id, advance_prefix
32
)
33
SELECT
34
COALESCE((SELECT last_advance_id FROM x LIMIT 1), (SELECT last_advance_id FROM insert_x LIMIT 1)),
35
COALESCE((SELECT advance_prefix FROM x LIMIT 1), (SELECT advance_prefix FROM insert_x LIMIT 1))
36
INTO new_advance_id, p_prefix;
37
38
-- Construct the final advance number (e.g., VADV0001)
39
advance_number := p_prefix || LPAD(new_advance_id::text, 4, '0');
40
41
RETURN advance_number;
42
END;
43
$function$
|
|||||
| Function | upsert_bill_workflow_and_config | Match | ||||||
| Function | check_bill_approval_permissions | Match | ||||||
| Function | get_all_bill_headers_by_vendor_id | Mismatch |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.get_all_bill_headers_by_vendor_id(p_vendor_id uuid, p_financial_year integer)
1
CREATE OR REPLACE FUNCTION public.get_all_bill_headers_by_vendor_id(p_vendor_id uuid, p_financial_year integer)
2
RETURNS TABLE(id uuid, bill_number text, bill_voucher text, debit_account_id uuid, bill_date timestamp without time zone, vendor_id uuid, vendor_name text, due_date timestamp without time zone, payment_term integer, note text, currency_id integer, discount numeric, bill_status_id integer, bill_status text, total_amount numeric, tds_amount numeric, vendor_paid_amount numeric, tds_paid_amount numeric, is_vendor_paid boolean, is_tds_paid boolean, is_closed boolean, total_paid_amount numeric, vendor_net_amount numeric, created_by uuid, created_on_utc timestamp without time zone, modified_by uuid, modified_on_utc timestamp without time zone, bill_type_id integer, current_approval_level integer)
2
RETURNS TABLE(id uuid, bill_number text, bill_voucher text, debit_account_id uuid, bill_date timestamp without time zone, vendor_id uuid, vendor_name text, due_date timestamp without time zone, payment_term integer, note text, currency_id integer, discount numeric, bill_status_id integer, bill_status text, total_amount numeric, tds_amount numeric, vendor_paid_amount numeric, tds_paid_amount numeric, is_vendor_paid boolean, is_tds_paid boolean, is_closed boolean, total_paid_amount numeric, vendor_net_amount numeric, created_by uuid, created_on_utc timestamp without time zone, modified_by uuid, modified_on_utc timestamp without time zone, bill_type_id integer, current_approval_level integer)
3
LANGUAGE plpgsql
3
LANGUAGE plpgsql
4
AS $function$
4
AS $function$
5
6
DECLARE
5
DECLARE
7
v_is_tds_vendor boolean;
6
v_is_tds_vendor boolean;
8
start_date date := make_date(p_financial_year, 4, 1);
7
start_date date := make_date(p_financial_year, 4, 1);
9
end_date date := make_date(p_financial_year + 1, 3, 31);
8
end_date date := make_date(p_financial_year + 1, 3, 31);
10
v_company_id uuid;
11
v_org_id uuid;
12
BEGIN
9
BEGIN
13
-- Get is_tds_vendor flag and company_id for the vendor
10
-- Get is_tds_vendor flag
14
SELECT v.is_tds_vendor,
11
SELECT vendors.is_tds_vendor INTO v_is_tds_vendor FROM public.vendors WHERE vendors.id = p_vendor_id;
15
v.company_id
16
INTO v_is_tds_vendor, v_company_id
17
FROM public.vendors v
18
WHERE v.id = p_vendor_id;
19
12
20
-- Get organization_id of the company related to the vendor
21
SELECT c.organization_id
22
INTO v_org_id
23
FROM public.companies c
24
WHERE c.id = v_company_id;
25
26
IF NOT v_is_tds_vendor THEN
13
IF NOT v_is_tds_vendor THEN
27
RETURN QUERY
14
RETURN QUERY
28
SELECT
15
SELECT
29
bh.id AS id,
16
bh.id,
30
bh.bill_number AS bill_number,
17
bh.bill_number,
31
bh.bill_voucher AS bill_voucher,
18
bh.bill_voucher,
32
bh.debit_account_id AS debit_account_id,
19
bh.debit_account_id,
33
bh.bill_date AS bill_date,
20
bh.bill_date,
34
v.id AS vendor_id,
21
v.id AS vendor_id,
35
v.name::text AS vendor_name,
22
v.name::text AS vendor_name,
36
bh.due_date AS due_date,
23
bh.due_date,
37
bh.payment_term AS payment_term,
24
bh.payment_term,
38
bh.note AS note,
25
bh.note,
39
bh.currency_id AS currency_id,
26
bh.currency_id,
40
bh.discount AS discount,
27
bh.discount,
41
bs.id AS bill_status_id,
28
bs.id AS bill_status_id,
42
bs.name::text AS bill_status,
29
bs.name::text AS bill_status,
43
bh.total_amount AS total_amount,
30
bh.total_amount,
44
COALESCE(bh.tds_amount, 0) AS tds_amount,
31
COALESCE(bh.tds_amount, 0),
45
COALESCE(bh.vendor_paid_amount, 0) AS vendor_paid_amount,
32
COALESCE(bh.vendor_paid_amount, 0),
46
COALESCE(bh.tds_paid_amount, 0) AS tds_paid_amount,
33
COALESCE(bh.tds_paid_amount, 0),
47
COALESCE(bh.is_vendor_paid, false) AS is_vendor_paid,
34
COALESCE(bh.is_vendor_paid, false),
48
COALESCE(bh.is_tds_paid, false) AS is_tds_paid,
35
COALESCE(bh.is_tds_paid, false),
49
COALESCE(bh.is_closed, false) AS is_closed,
36
COALESCE(bh.is_closed, false),
50
COALESCE(bh.total_paid_amount, 0) AS total_paid_amount,
37
COALESCE(bh.total_paid_amount, 0),
51
COALESCE(bh.vendor_net_amount, 0) AS vendor_net_amount,
38
COALESCE(bh.vendor_net_amount, 0),
52
bh.created_by AS created_by,
39
bh.created_by,
53
bh.created_on_utc AS created_on_utc,
40
bh.created_on_utc,
54
bh.modified_by AS modified_by,
41
bh.modified_by,
55
bh.modified_on_utc AS modified_on_utc,
42
bh.modified_on_utc,
56
bh.bill_type_id AS bill_type_id,
43
bh.bill_type_id,
57
bh.current_approval_level AS current_approval_level
44
bh.current_approval_level
58
FROM public.bill_headers bh
45
FROM public.bill_headers bh
59
INNER JOIN public.bill_statuses bs ON bs.id = bh.bill_status_id
46
JOIN public.bill_statuses bs ON bs.id = bh.bill_status_id
60
INNER JOIN public.vendors v ON v.id = bh.vendor_id
47
JOIN public.vendors v ON v.id = bh.vendor_id
61
WHERE bh.vendor_id = p_vendor_id
48
WHERE
62
AND bh.bill_date BETWEEN start_date AND end_date
49
bh.vendor_id = p_vendor_id
63
AND bh.bill_status_id IN (3, 4)
50
AND (bh.bill_status_id = 3 OR bh.bill_status_id = 4)
64
AND bh.is_deleted = false;
51
AND bh.is_deleted = false;
65
--AND bh.is_vendor_paid = false;
66
67
ELSE
52
ELSE
68
RETURN QUERY
53
RETURN QUERY
69
SELECT
54
SELECT
70
bh.id AS id,
55
bh.id,
71
bh.bill_number AS bill_number,
56
bh.bill_number,
72
bh.bill_voucher AS bill_voucher,
57
bh.bill_voucher,
73
bh.debit_account_id AS debit_account_id,
58
bh.debit_account_id,
74
bh.bill_date AS bill_date,
59
bh.bill_date,
75
v.id AS vendor_id,
60
v.id AS vendor_id,
76
v.name::text AS vendor_name,
61
v.name::text AS vendor_name,
77
bh.due_date AS due_date,
62
bh.due_date,
78
bh.payment_term AS payment_term,
63
bh.payment_term,
79
bh.note AS note,
64
bh.note,
80
bh.currency_id AS currency_id,
65
bh.currency_id,
81
bh.discount AS discount,
66
bh.discount,
82
bs.id AS bill_status_id,
67
bs.id AS bill_status_id,
83
bs.name::text AS bill_status,
68
bs.name::text AS bill_status,
84
bh.total_amount AS total_amount,
69
bh.total_amount,
85
COALESCE(bh.tds_amount, 0) AS tds_amount,
70
COALESCE(bh.tds_amount, 0),
86
COALESCE(bh.vendor_paid_amount, 0) AS vendor_paid_amount,
71
COALESCE(bh.vendor_paid_amount, 0),
87
COALESCE(bh.tds_paid_amount, 0) AS tds_paid_amount,
72
COALESCE(bh.tds_paid_amount, 0),
88
COALESCE(bh.is_vendor_paid, false) AS is_vendor_paid,
73
COALESCE(bh.is_vendor_paid, false),
89
COALESCE(bh.is_tds_paid, false) AS is_tds_paid,
74
COALESCE(bh.is_tds_paid, false),
90
COALESCE(bh.is_closed, false) AS is_closed,
75
COALESCE(bh.is_closed, false),
91
COALESCE(bh.total_paid_amount, 0) AS total_paid_amount,
76
COALESCE(bh.total_paid_amount, 0),
92
COALESCE(bh.vendor_net_amount, 0) AS vendor_net_amount,
77
COALESCE(bh.vendor_net_amount, 0),
93
bh.created_by AS created_by,
78
bh.created_by,
94
bh.created_on_utc AS created_on_utc,
79
bh.created_on_utc,
95
bh.modified_by AS modified_by,
80
bh.modified_by,
96
bh.modified_on_utc AS modified_on_utc,
81
bh.modified_on_utc,
97
bh.bill_type_id AS bill_type_id,
82
bh.bill_type_id,
98
bh.current_approval_level AS current_approval_level
83
bh.current_approval_level
99
FROM public.bill_headers bh
84
FROM public.bill_headers bh
100
INNER JOIN public.bill_statuses bs ON bs.id = bh.bill_status_id
85
JOIN public.bill_statuses bs ON bs.id = bh.bill_status_id
101
INNER JOIN public.vendors v ON v.id = bh.vendor_id
86
JOIN public.vendors v ON v.id = bh.vendor_id
102
WHERE bh.bill_date BETWEEN start_date AND end_date
87
WHERE
103
AND bh.bill_status_id IN (3, 4, 5)
88
bh.bill_date >= start_date
104
AND bh.is_tds_paid = false
89
AND bh.bill_date <= end_date
90
AND (bh.bill_status_id = 4 OR bh.bill_status_id = 5)
105
AND COALESCE(bh.tds_amount, 0) > 0
91
AND COALESCE(bh.tds_amount, 0) > 0
106
AND bh.is_deleted = false
92
AND bh.is_deleted = false;
107
AND bh.company_id IN (
108
SELECT c.id
109
FROM public.companies c
110
WHERE c.organization_id = v_org_id
111
);
112
END IF;
93
END IF;
113
END;
94
END;
114
$function$
95
$function$
|
|||||
| Function | get_all_pending_tds_bill_headers | Match | ||||||
| Function | get_all_tds_paid_bills | Match | ||||||
| Function | get_all_vendor_advances | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.get_all_vendor_advances(p_company_id uuid, p_finance_year_id integer, p_only_unutilized boolean DEFAULT false)
2
RETURNS TABLE(id uuid, vendor_id uuid, vendor_name character varying, advance_number text, paid_date timestamp without time zone, amount numeric, utilized_amount numeric, mode_of_payment text, reference text, description text, currency_id integer)
3
LANGUAGE plpgsql
4
AS $function$
5
DECLARE
6
v_financial_year_start date;
7
v_financial_year_end date;
8
BEGIN
9
-- Start of financial year: April 1 of the given year
10
v_financial_year_start := TO_DATE(p_finance_year_id || '-04-01', 'YYYY-MM-DD');
11
12
-- End of financial year: March 31 of the next year
13
v_financial_year_end := TO_DATE((p_finance_year_id + 1) || '-03-31', 'YYYY-MM-DD');
14
15
RETURN QUERY
16
SELECT
17
va.id,
18
va.vendor_id,
19
v.name, -- Add vendor name
20
va.advance_number,
21
va.paid_date,
22
va.amount,
23
va.utilized_amount,
24
va.mode_of_payment,
25
va.reference,
26
va.description,
27
va.currency_id
28
FROM public.vendor_advances va
29
LEFT JOIN public.vendors v ON va.vendor_id = v.id -- Join here to get the name
30
WHERE va.company_id = p_company_id
31
AND va.is_deleted = false
32
AND v.is_deleted = false
33
AND va.paid_date >= v_financial_year_start
34
AND va.paid_date <= v_financial_year_end
35
AND (
36
(p_only_unutilized = true AND va.amount > va.utilized_amount)
37
OR (p_only_unutilized IS DISTINCT FROM true)
38
);
39
END;
40
$function$
|
|||||
| Function | get_bill_lines | Match | ||||||
| Function | get_bill_payments_by_company_with_tds_check | Match | ||||||
| Function | get_new_vendor_advance_settlement_number | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.get_new_vendor_advance_settlement_number(p_company_id uuid, p_date date)
2
RETURNS character varying
3
LANGUAGE plpgsql
4
AS $function$
5
DECLARE
6
v_finance_year int;
7
new_id int;
8
v_prefix text := 'ADV'; -- 👈 default prefix
9
v_length int := 4; -- 👈 default length
10
number varchar;
11
BEGIN
12
-- derive financial year
13
IF EXTRACT(MONTH FROM p_date) >= 4 THEN
14
v_finance_year := EXTRACT(YEAR FROM p_date);
15
ELSE
16
v_finance_year := EXTRACT(YEAR FROM p_date) - 1;
17
END IF;
18
19
-- upsert into vendor_advance_settlement_ids
20
WITH upd AS (
21
UPDATE public.vendor_advance_settlement_ids
22
SET last_settlement_id = last_settlement_id + 1
23
WHERE company_id = p_company_id AND fin_year = v_finance_year
24
RETURNING last_settlement_id, settlement_prefix, settlement_length
25
), ins AS (
26
INSERT INTO public.vendor_advance_settlement_ids(
27
company_id, fin_year, settlement_prefix, settlement_length, last_settlement_id
28
)
29
SELECT p_company_id, v_finance_year, v_prefix, v_length, 1
30
WHERE NOT EXISTS (SELECT 1 FROM upd)
31
RETURNING last_settlement_id, settlement_prefix, settlement_length
32
)
33
SELECT COALESCE(u.last_settlement_id, i.last_settlement_id),
34
COALESCE(u.settlement_prefix, i.settlement_prefix),
35
COALESCE(u.settlement_length, i.settlement_length)
36
INTO new_id, v_prefix, v_length
37
FROM upd u
38
FULL JOIN ins i ON true;
39
40
-- build formatted settlement number
41
number := v_prefix || LPAD(new_id::text, v_length, '0');
42
RETURN number;
43
END;
44
$function$
|
|||||
| Function | get_tds_bills_from_span | Mismatch |
Source Script
Target Script
1
CREATE OR REPLACE FUNCTION public.get_tds_bills_from_span(p_company_id uuid, p_fin_year_id integer, p_user_id uuid, p_bill_type_id integer DEFAULT NULL::integer, 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)
1
CREATE OR REPLACE FUNCTION public.get_tds_bills_from_span(p_company_id uuid, p_fin_year_id integer, p_user_id uuid, p_bill_type_id integer DEFAULT NULL::integer, 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, bill_voucher text, bill_number text, vendor_name character varying, vendor_id uuid, due_date date, bill_date date, payment_term integer, total_amount numeric, total_paid_amount numeric, currency_id integer, bill_status character varying, bill_status_id integer, created_by uuid, created_on_utc timestamp without time zone, modified_by uuid, modified_on_utc timestamp without time zone, created_by_name character varying, modified_by_name character varying, bill_type character varying, bill_type_id integer, tds_amount numeric, has_next_status_approval boolean, next_status_id integer)
2
RETURNS TABLE(id uuid, bill_voucher text, bill_number text, vendor_name character varying, vendor_id uuid, due_date date, bill_date date, payment_term integer, total_amount numeric, currency_id integer, bill_status character varying, bill_status_id integer, created_by uuid, created_on_utc timestamp without time zone, modified_by uuid, modified_on_utc timestamp without time zone, created_by_name character varying, modified_by_name character varying, bill_type character varying, bill_type_id integer, tds_amount numeric, has_next_status_approval boolean, next_status_id integer)
3
LANGUAGE plpgsql
3
LANGUAGE plpgsql
4
AS $function$
4
AS $function$
5
DECLARE
5
DECLARE
6
v_start_date date := COALESCE(p_start_date::date, MAKE_DATE(p_fin_year_id, 4, 1));
6
v_start_date date := COALESCE(p_start_date::date, MAKE_DATE(p_fin_year_id, 4, 1));
7
v_end_date date := COALESCE(p_end_date::date, MAKE_DATE(p_fin_year_id + 1, 3, 31));
7
v_end_date date := COALESCE(p_end_date::date, MAKE_DATE(p_fin_year_id + 1, 3, 31));
8
DRAFT_STATUS CONSTANT integer := 1;
8
DRAFT_STATUS CONSTANT integer := 1;
9
BEGIN
9
BEGIN
10
RETURN QUERY
10
RETURN QUERY
11
SELECT
11
SELECT
12
bh.id,
12
bh.id,
13
bh.bill_voucher,
13
bh.bill_voucher,
14
bh.bill_number,
14
bh.bill_number,
15
v.name AS vendor_name,
15
v.name AS vendor_name,
16
bh.vendor_id,
16
bh.vendor_id,
17
bh.due_date::date,
17
bh.due_date::date,
18
bh.bill_date::date,
18
bh.bill_date::date,
19
bh.payment_term,
19
bh.payment_term,
20
bh.total_amount,
20
bh.total_amount,
21
bh.total_paid_amount,
22
bh.currency_id,
21
bh.currency_id,
23
bs.name::varchar AS bill_status,
22
bs.name::varchar AS bill_status, -- ensure varchar
24
bh.bill_status_id,
23
bh.bill_status_id,
25
bh.created_by,
24
bh.created_by,
26
bh.created_on_utc,
25
bh.created_on_utc,
27
bh.modified_by,
26
bh.modified_by,
28
bh.modified_on_utc,
27
bh.modified_on_utc,
29
CONCAT(cu.first_name, ' ', cu.last_name)::varchar AS created_by_name,
28
CONCAT(cu.first_name, ' ', cu.last_name)::varchar AS created_by_name,
30
CONCAT(mu.first_name, ' ', mu.last_name)::varchar AS modified_by_name,
29
CONCAT(mu.first_name, ' ', mu.last_name)::varchar AS modified_by_name,
31
bt.name::varchar AS bill_type,
30
bt.name::varchar AS bill_type, -- ✅ cast to varchar
32
bh.bill_type_id,
31
bh.bill_type_id,
33
bh.tds_amount,
32
bh.tds_amount,
34
CASE
33
CASE
35
WHEN bh.bill_status_id = DRAFT_STATUS THEN true
34
WHEN bh.bill_status_id = DRAFT_STATUS THEN true
36
WHEN EXISTS (
35
WHEN EXISTS (
37
SELECT 1
36
SELECT 1
38
FROM bill_approval_user_account a
37
FROM bill_approval_user_account a
39
WHERE a.account_id = bh.debit_account_id
38
WHERE a.account_id = bh.debit_account_id
40
AND a.status_id = bh.bill_status_id
39
AND a.status_id = bh.bill_status_id
41
AND a.user_id = p_user_id
40
AND a.user_id = p_user_id
42
AND a.is_deleted = false
41
AND a.is_deleted = false
43
) THEN true
42
) THEN true
44
WHEN EXISTS (
43
WHEN EXISTS (
45
SELECT 1
44
SELECT 1
46
FROM bill_approval_user_company c
45
FROM bill_approval_user_company c
47
WHERE c.company_id = p_company_id
46
WHERE c.company_id = p_company_id
48
AND c.status_id = bh.bill_status_id
47
AND c.status_id = bh.bill_status_id
49
AND c.user_id = p_user_id
48
AND c.user_id = p_user_id
50
AND c.is_deleted = false
49
AND c.is_deleted = false
51
) THEN true
50
) THEN true
52
ELSE false
51
ELSE false
53
END AS has_next_status_approval,
52
END AS has_next_status_approval,
54
COALESCE(
53
COALESCE(
55
(SELECT bw.next_status
54
(SELECT bw.next_status
56
FROM bill_workflow bw
55
FROM bill_workflow bw
57
WHERE bw.company_id = p_company_id
56
WHERE bw.company_id = p_company_id
58
AND bw.status = bh.bill_status_id
57
AND bw.status = bh.bill_status_id
59
AND bw.is_deleted = false
58
AND bw.is_deleted = false
60
AND (bw.is_enabled IS DISTINCT FROM FALSE)
61
ORDER BY bw.approval_level ASC NULLS LAST
62
LIMIT 1),
59
LIMIT 1),
63
0
60
0
64
) AS next_status_id
61
) AS next_status_id
65
FROM public.bill_headers bh
62
FROM public.bill_headers bh
66
LEFT JOIN public.vendors v ON v.id = bh.vendor_id
63
LEFT JOIN public.vendors v ON v.id = bh.vendor_id
67
LEFT JOIN public.bill_statuses bs ON bs.id = bh.bill_status_id
64
LEFT JOIN public.bill_statuses bs ON bs.id = bh.bill_status_id
68
LEFT JOIN public.bill_types bt ON bt.id = bh.bill_type_id
65
LEFT JOIN public.bill_types bt ON bt.id = bh.bill_type_id
69
LEFT JOIN users cu ON cu.id = bh.created_by
66
LEFT JOIN public.users cu ON cu.id = bh.created_by
70
LEFT JOIN users mu ON mu.id = bh.modified_by
67
LEFT JOIN public.users mu ON mu.id = bh.modified_by
71
WHERE bh.company_id = p_company_id
68
WHERE bh.company_id = p_company_id
72
AND bh.is_deleted = false
69
AND bh.is_deleted = false
73
AND bh.tds_amount IS NOT NULL
70
AND bh.tds_amount IS NOT NULL -- ✅ only bills with TDS
74
AND bh.tds_amount > 0
71
AND bh.tds_amount > 0 -- ✅ enforce positive TDS
75
AND (p_bill_type_id IS NULL OR p_bill_type_id = 0 OR bh.bill_type_id = p_bill_type_id)
72
AND (p_bill_type_id IS NULL OR p_bill_type_id = 0 OR bh.bill_type_id = p_bill_type_id)
76
AND bh.bill_date BETWEEN v_start_date AND v_end_date;
73
AND bh.bill_date BETWEEN v_start_date AND v_end_date;
77
END;
74
END;
78
$function$
75
$function$
|
|||||
| Procedure | create_bill_payment | Mismatch |
Source Script
Target Script
1
CREATE OR REPLACE PROCEDURE public.create_bill_payment(IN p_bill_payment_header_id uuid, IN p_company_id uuid, IN p_vendor_id uuid, IN p_paid_date date, IN p_mode_of_payment text, IN p_credit_account_id uuid, IN p_debit_account_id uuid, IN p_reference text, IN p_paid_amount numeric, IN p_grand_total_amount numeric, IN p_advance_amount numeric, IN p_description text, IN p_tds_amount numeric, IN p_created_by uuid, IN p_bill_payment_details jsonb)
1
CREATE OR REPLACE PROCEDURE public.create_bill_payment(IN p_bill_payment_header_id uuid, IN p_company_id uuid, IN p_vendor_id uuid, IN p_paid_date date, IN p_mode_of_payment text, IN p_credit_account_id uuid, IN p_debit_account_id uuid, IN p_reference text, IN p_paid_amount numeric, IN p_grand_total_amount numeric, IN p_advance_amount numeric, IN p_description text, IN p_tds_amount numeric, IN p_created_by uuid, IN p_bill_payment_details jsonb)
2
LANGUAGE plpgsql
2
LANGUAGE plpgsql
3
AS $procedure$
3
AS $procedure$
4
5
DECLARE
4
DECLARE
6
v_bill_status_id INT;
5
v_bill_status_id INT;
7
v_bill_header_id UUID;
6
v_bill_header_id UUID;
8
v_payment_amount NUMERIC;
7
v_payment_amount NUMERIC;
9
v_tds_amount NUMERIC;
8
v_tds_amount NUMERIC;
10
v_serial_number INT;
9
v_serial_number INT;
11
v_row JSONB;
10
v_row JSONB;
12
v_payment_number character varying;
11
v_payment_number character varying;
13
v_is_tds_vendor bool;
12
v_is_tds_vendor bool;
14
v_is_tds_paid bool;
15
v_bill_total_amount NUMERIC;
13
v_bill_total_amount NUMERIC;
16
v_bill_tds_amount NUMERIC;
14
v_bill_tds_amount NUMERIC;
17
v_vendor_paid_amount NUMERIC;
15
v_vendor_paid_amount NUMERIC;
18
v_tds_paid_amount NUMERIC;
16
v_tds_paid_amount NUMERIC;
19
v_tds_status_id INT;
20
v_payment_status_id INT;
21
v_vendor_net_amount NUMERIC;
22
BEGIN
17
BEGIN
23
-- Generate new payment number
18
-- Generate new payment number
24
v_payment_number := get_new_payment_number(p_company_id, p_paid_date);
19
v_payment_number := get_new_payment_number(p_company_id, p_paid_date);
25
20
26
-- Check if vendor is TDS vendor
21
-- Check if vendor is TDS vendor
27
SELECT is_tds_vendor
22
SELECT is_tds_vendor
28
INTO v_is_tds_vendor
23
INTO v_is_tds_vendor
29
FROM vendors
24
FROM vendors
30
WHERE id = p_vendor_id;
25
WHERE id = p_vendor_id;
31
26
32
-- Insert payment header
27
-- Insert payment header
33
INSERT INTO public.bill_payment_headers (
28
INSERT INTO public.bill_payment_headers (
34
id,
29
id,
35
company_id,
30
company_id,
36
vendor_id,
31
vendor_id,
37
paid_date,
32
paid_date,
38
mode_of_payment,
33
mode_of_payment,
39
credit_account_id,
34
credit_account_id,
40
debit_account_id,
35
debit_account_id,
41
reference,
36
reference,
42
paid_amount,
37
paid_amount,
43
description,
38
description,
44
tds_amount,
39
tds_amount,
45
advance_amount,
40
advance_amount,
46
grand_total_amount,
41
grand_total_amount,
47
payment_number,
42
payment_number,
48
created_by,
43
created_by,
49
created_on_utc,
44
created_on_utc,
50
is_deleted
45
is_deleted
51
)
46
)
52
VALUES (
47
VALUES (
53
p_bill_payment_header_id,
48
p_bill_payment_header_id,
54
p_company_id,
49
p_company_id,
55
p_vendor_id,
50
p_vendor_id,
56
p_paid_date,
51
p_paid_date,
57
p_mode_of_payment,
52
p_mode_of_payment,
58
p_credit_account_id,
53
p_credit_account_id,
59
p_debit_account_id,
54
p_debit_account_id,
60
p_reference,
55
p_reference,
61
p_paid_amount,
56
p_paid_amount,
62
p_description,
57
p_description,
63
p_tds_amount,
58
p_tds_amount,
64
p_advance_amount,
59
p_advance_amount,
65
p_grand_total_amount,
60
p_grand_total_amount,
66
v_payment_number,
61
v_payment_number,
67
p_created_by,
62
p_created_by,
68
now(),
63
now(),
69
false
64
false
70
);
65
);
71
66
72
-- Loop through each bill payment detail record
67
-- Loop through each bill payment detail record
73
FOR v_row IN SELECT * FROM jsonb_array_elements(p_bill_payment_details)
68
FOR v_row IN SELECT * FROM jsonb_array_elements(p_bill_payment_details)
74
LOOP
69
LOOP
75
v_bill_header_id := (v_row->>'header_id')::UUID;
70
v_bill_header_id := (v_row->>'header_id')::UUID;
76
v_payment_amount := COALESCE((v_row->>'payment_amount')::NUMERIC, 0);
71
v_payment_amount := COALESCE((v_row->>'payment_amount')::NUMERIC, 0);
77
v_tds_amount := COALESCE((v_row->>'tds_amount')::NUMERIC, 0);
72
v_tds_amount := COALESCE((v_row->>'tds_amount')::NUMERIC, 0);
78
v_serial_number := (v_row->>'serial_number')::INT;
73
v_serial_number := (v_row->>'serial_number')::INT;
79
v_is_tds_paid := COALESCE((v_row->>'is_tds_paid')::BOOLEAN, false);
80
74
81
-- Fetch current amounts from bill header
75
-- Fetch current paid amounts and bill totals
82
SELECT vendor_paid_amount, total_amount, tds_amount, tds_paid_amount, vendor_net_amount
76
SELECT vendor_paid_amount, total_amount, tds_amount, tds_paid_amount
83
INTO v_vendor_paid_amount, v_bill_total_amount, v_bill_tds_amount, v_tds_paid_amount, v_vendor_net_amount
77
INTO v_vendor_paid_amount, v_bill_total_amount, v_bill_tds_amount, v_tds_paid_amount
84
FROM public.bill_headers
78
FROM public.bill_headers
85
WHERE id = v_bill_header_id;
79
WHERE id = v_bill_header_id;
86
80
87
-- Insert payment details record
81
-- Insert payment details record
88
INSERT INTO public.bill_payment_details (
82
INSERT INTO public.bill_payment_details (
89
id,
83
id,
90
bill_payment_header_id,
84
bill_payment_header_id,
91
bill_header_id,
85
bill_header_id,
92
paid_amount,
86
paid_amount,
93
tds_amount,
87
tds_amount,
94
serial_number,
88
serial_number,
95
created_by,
89
created_by,
96
created_on_utc
90
created_on_utc
97
)
91
)
98
VALUES (
92
VALUES (
99
(v_row->>'id')::uuid,
93
(v_row->>'id')::uuid,
100
p_bill_payment_header_id,
94
p_bill_payment_header_id,
101
v_bill_header_id,
95
v_bill_header_id,
102
v_payment_amount,
96
v_payment_amount,
103
v_tds_amount,
97
v_tds_amount,
104
v_serial_number,
98
v_serial_number,
105
p_created_by,
99
p_created_by,
106
now()
100
now()
107
);
101
);
108
102
109
-- Update paid amounts based on payment type
103
-- Payment logic based on vendor type
110
IF v_is_tds_vendor OR v_is_tds_paid THEN
104
IF v_is_tds_vendor THEN
105
-- TDS payment to government: update tds_paid_amount only
111
v_tds_paid_amount := COALESCE(v_tds_paid_amount, 0) + v_payment_amount;
106
v_tds_paid_amount := COALESCE(v_tds_paid_amount, 0) + v_payment_amount;
112
ELSE
107
ELSE
108
-- Vendor payment: update vendor_paid_amount only
113
v_vendor_paid_amount := COALESCE(v_vendor_paid_amount, 0) + v_payment_amount;
109
v_vendor_paid_amount := COALESCE(v_vendor_paid_amount, 0) + v_payment_amount;
114
END IF;
110
END IF;
115
111
116
-- Status calculations using updated paid amounts (after applying current payment)
112
-- Bill status calculation
117
118
-- Bill payment status
119
IF (v_vendor_paid_amount >= (v_bill_total_amount - v_bill_tds_amount))
113
IF (v_vendor_paid_amount >= (v_bill_total_amount - v_bill_tds_amount))
120
AND (v_tds_paid_amount >= v_bill_tds_amount) THEN
114
AND (v_tds_paid_amount >= v_bill_tds_amount) THEN
121
v_bill_status_id := 5; -- PAID
115
v_bill_status_id := 5; -- PAID
122
ELSE
116
ELSE
123
v_bill_status_id := 4; -- PARTIALLY_PAID
117
v_bill_status_id := 4; -- PARTIALLY_PAID
124
END IF;
125
126
-- TDS status for TDS vendors
127
IF v_is_tds_vendor THEN
128
IF v_tds_paid_amount = 0 AND v_bill_tds_amount > 0 THEN
129
v_tds_status_id := 2; -- Unpaid
130
ELSIF v_tds_paid_amount > 0 AND v_tds_paid_amount = v_bill_tds_amount THEN
131
v_tds_status_id := 3; -- Deducted
132
ELSE
133
v_tds_status_id := 1; -- N/A fallback
134
END IF;
135
ELSE
136
v_tds_status_id := 1; -- N/A for non-TDS vendors
137
END IF;
118
END IF;
138
119
139
-- Payment status assignment (now covers both TDS and non-TDS vendors)
120
-- Update bill header with new paid info
140
IF NOT v_is_tds_vendor THEN
141
IF v_vendor_net_amount > 0 AND v_vendor_paid_amount = 0 THEN
142
v_payment_status_id := 1; -- Unpaid
143
ELSIF v_vendor_net_amount > 0 AND v_vendor_paid_amount > 0 AND v_vendor_paid_amount < v_vendor_net_amount THEN
144
v_payment_status_id := 2; -- PartiallyPaid
145
ELSIF v_vendor_net_amount = v_vendor_paid_amount THEN
146
v_payment_status_id := 3; -- FullyPaid
147
ELSE
148
v_payment_status_id := 1; -- Default Unpaid fallback
149
END IF;
150
ELSE
151
-- TDS vendors
152
IF v_vendor_net_amount = v_vendor_paid_amount THEN
153
v_payment_status_id := 3; -- FullyPaid
154
ELSIF v_vendor_paid_amount > 0 THEN
155
v_payment_status_id := 2; -- PartiallyPaid
156
ELSE
157
v_payment_status_id := 1; -- Unpaid
158
END IF;
159
END IF;
160
161
-- Final safeguard to avoid nulls
162
v_payment_status_id := COALESCE(v_payment_status_id, 1);
163
164
-- Update bill header with new payment and status details
165
UPDATE public.bill_headers
121
UPDATE public.bill_headers
166
SET vendor_paid_amount = v_vendor_paid_amount,
122
SET vendor_paid_amount = v_vendor_paid_amount,
167
tds_paid_amount = v_tds_paid_amount,
123
tds_paid_amount = v_tds_paid_amount,
168
bill_status_id = v_bill_status_id,
124
bill_status_id = v_bill_status_id
169
tds_status_id = CASE
170
WHEN v_is_tds_vendor THEN v_tds_status_id
171
ELSE 1 -- N/A for non-TDS vendors
172
END,
173
payment_status_id = CASE
174
WHEN v_is_tds_vendor THEN payment_status_id -- leave unchanged for TDS vendors
175
ELSE v_payment_status_id -- update for non-TDS vendors
176
END
177
WHERE id = v_bill_header_id;
125
WHERE id = v_bill_header_id;
178
179
END LOOP;
126
END LOOP;
180
127
181
RAISE NOTICE 'Bill payment processed successfully';
128
RAISE NOTICE 'Bill payment processed successfully';
182
END;
129
END;
183
$procedure$
130
$procedure$
|
|||||
| Procedure | create_draft_bill_detail | Match | ||||||
| Procedure | create_multiple_bill_payments | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE PROCEDURE public.create_multiple_bill_payments(IN p_bill_payments jsonb)
2
LANGUAGE plpgsql
3
AS $procedure$
4
DECLARE
5
bill_payment RECORD;
6
payment_statuses TEXT[] := ARRAY[]::TEXT[];
7
BEGIN
8
FOR bill_payment IN
9
SELECT * FROM jsonb_to_recordset(p_bill_payments) AS (
10
bill_payment_header_id uuid,
11
company_id uuid,
12
vendor_id uuid,
13
paid_date date,
14
paid_amount numeric,
15
grand_total_amount numeric,
16
tds_amount numeric,
17
advance_amount numeric,
18
description text,
19
debit_account_id uuid,
20
credit_account_id uuid,
21
mode_of_payment text,
22
reference text,
23
created_by uuid,
24
detail_lines jsonb
25
)
26
LOOP
27
BEGIN
28
IF NOT EXISTS (
29
SELECT 1
30
FROM public.bill_payment_headers
31
WHERE reference = bill_payment.reference
32
AND company_id = bill_payment.company_id
33
) THEN
34
CALL public.create_bill_payment(
35
bill_payment.bill_payment_header_id,
36
bill_payment.company_id,
37
bill_payment.vendor_id,
38
bill_payment.paid_date,
39
bill_payment.mode_of_payment,
40
bill_payment.credit_account_id,
41
bill_payment.debit_account_id,
42
bill_payment.reference,
43
bill_payment.paid_amount,
44
bill_payment.grand_total_amount,
45
bill_payment.advance_amount,
46
bill_payment.description,
47
bill_payment.tds_amount,
48
bill_payment.created_by,
49
bill_payment.detail_lines
50
);
51
payment_statuses := payment_statuses || format('%s:success', bill_payment.bill_payment_header_id);
52
ELSE
53
RAISE NOTICE 'Duplicate bill payment found for reference: %, company_id: %. Skipping.', bill_payment.reference, bill_payment.company_id;
54
payment_statuses := payment_statuses || format('%s:duplicate', bill_payment.bill_payment_header_id);
55
CONTINUE;
56
END IF;
57
EXCEPTION
58
WHEN OTHERS THEN
59
RAISE NOTICE 'An error occurred while processing bill payment ID: %, Error: %', bill_payment.bill_payment_header_id, SQLERRM;
60
payment_statuses := payment_statuses || format('%s:failed', bill_payment.bill_payment_header_id);
61
END;
62
END LOOP;
63
64
RAISE NOTICE 'BILL_PAYMENT_STATUS:%', to_json(payment_statuses);
65
RAISE NOTICE 'All bill payments processed.';
66
END;
67
$procedure$
|
|||||
| Procedure | create_vendor_advance | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE PROCEDURE public.create_vendor_advance(IN p_vendor_advance_id uuid, IN p_company_id uuid, IN p_vendor_id uuid, IN p_paid_date date, IN p_mode_of_payment text, IN p_through_account_id uuid, IN p_reference text, IN p_amount numeric, IN p_description text, IN p_currency_id integer, IN p_created_by uuid)
2
LANGUAGE plpgsql
3
AS $procedure$
4
DECLARE
5
v_advance_number varchar;
6
BEGIN
7
-- Generate a new vendor advance number for this company & date
8
v_advance_number := public.get_new_vendor_advance_number(p_company_id, p_paid_date);
9
10
-- Insert into vendor_advances
11
INSERT INTO vendor_advances
12
(
13
id,
14
company_id,
15
vendor_id,
16
advance_number,
17
paid_date,
18
amount,
19
mode_of_payment,
20
through_account_id,
21
reference,
22
description,
23
currency_id,
24
utilized_amount,
25
created_on_utc,
26
created_by
27
)
28
VALUES
29
(
30
p_vendor_advance_id,
31
p_company_id,
32
p_vendor_id,
33
v_advance_number,
34
p_paid_date,
35
p_amount,
36
p_mode_of_payment,
37
p_through_account_id,
38
NULLIF(p_reference, ''),
39
NULLIF(p_description, ''),
40
p_currency_id,
41
0.0, -- initialize utilized_amount
42
now(),
43
p_created_by
44
);
45
END;
46
$procedure$
|
|||||
| Procedure | delete_bill_data_by_company_id | Match | ||||||
| Procedure | delete_vendor_notes_by_company | Match | ||||||
| Procedure | initialize_bill_header_ids | Match | ||||||
| Procedure | initialize_bill_payment_header_ids | Match | ||||||
| Procedure | initialize_bill_workflow | Match | ||||||
| Procedure | initialize_draft_bill_header_ids | Match | ||||||
| Procedure | initialize_organization | Match | ||||||
| Procedure | update_bill_previous_status | Match | ||||||
| Procedure | save_company_prefix_for_bill_and_payment | Match | ||||||
| Procedure | update_account_level_approval_configuration | Mismatch |
Source Script
Target Script
1
CREATE OR REPLACE PROCEDURE public.update_account_level_approval_configuration(IN p_company_id uuid, IN p_account_id uuid, IN p_status_id integer, IN p_required_approval_levels integer, IN p_approvals jsonb, IN p_modified_by uuid)
1
CREATE OR REPLACE PROCEDURE public.update_account_level_approval_configuration(IN p_company_id uuid, IN p_account_id uuid, IN p_status_id integer, IN p_required_approval_levels integer, IN p_approvals jsonb, IN p_modified_by uuid)
2
LANGUAGE plpgsql
2
LANGUAGE plpgsql
3
AS $procedure$
3
AS $procedure$
4
4
5
DECLARE
5
DECLARE
6
approval_item JSONB;
6
approval_item JSONB;
7
existing_id INT;
7
existing_id INT;
8
v_status_id INT;
9
BEGIN
8
BEGIN
10
-- 1️⃣ Soft delete old records not present in incoming approvals:
9
-- 1️⃣ Soft delete old records not present in incoming approvals:
11
UPDATE bill_approval_user_account
10
UPDATE bill_approval_user_account
12
SET is_deleted = TRUE,
11
SET is_deleted = TRUE,
13
deleted_on_utc = NOW(),
12
deleted_on_utc = NOW(),
14
modified_on_utc = NOW(),
13
modified_on_utc = NOW(),
15
modified_by = p_modified_by
14
modified_by = p_modified_by
16
WHERE company_id = p_company_id
15
WHERE company_id = p_company_id
17
AND account_id = p_account_id
16
AND account_id = p_account_id
18
AND NOT EXISTS (
17
AND NOT EXISTS (
19
SELECT 1
18
SELECT 1
20
FROM jsonb_array_elements(p_approvals) AS elem
19
FROM jsonb_array_elements(p_approvals) AS elem
21
WHERE (elem->>'userId')::UUID = bill_approval_user_account.user_id
20
WHERE (elem->>'userId')::UUID = bill_approval_user_account.user_id
22
AND (elem->>'approvalLevel')::INT = bill_approval_user_account.approval_level
21
AND (elem->>'approvalLevel')::INT = bill_approval_user_account.approval_level
23
AND (elem->>'statusId')::INT = bill_approval_user_account.status_id
24
);
22
);
25
23
26
-- 2️⃣ Upsert incoming approvals:
24
-- 2️⃣ Upsert incoming approvals:
27
FOR approval_item IN SELECT * FROM jsonb_array_elements(p_approvals)
25
FOR approval_item IN SELECT * FROM jsonb_array_elements(p_approvals)
28
LOOP
26
LOOP
29
SELECT id INTO existing_id
27
SELECT id INTO existing_id
30
FROM bill_approval_user_account
28
FROM bill_approval_user_account
31
WHERE company_id = p_company_id
29
WHERE company_id = p_company_id
32
AND account_id = p_account_id
30
AND account_id = p_account_id
33
AND user_id = (approval_item->>'userId')::UUID
31
AND user_id = (approval_item->>'userId')::UUID
34
AND approval_level = (approval_item->>'approvalLevel')::INT
32
AND approval_level = (approval_item->>'approvalLevel')::INT
35
AND status_id = (approval_item->>'statusId')::INT
36
AND is_deleted = FALSE
33
AND is_deleted = FALSE
37
LIMIT 1;
34
LIMIT 1;
38
35
39
IF existing_id IS NOT NULL THEN
36
IF existing_id IS NOT NULL THEN
40
-- -- UPDATE bill_approval_user_account
37
UPDATE bill_approval_user_account
41
-- SET status_id = (approval_item->>'statusId')::INT,
38
SET status_id = (approval_item->>'statusId')::INT,
42
-- modified_on_utc = NOW(),
39
modified_on_utc = NOW(),
43
-- modified_by = p_modified_by
40
modified_by = p_modified_by
44
-- WHERE id = existing_id;
41
WHERE id = existing_id;
45
RAISE NOTICE 'existing IDs: %', existing_id;
46
ELSE
42
ELSE
47
INSERT INTO bill_approval_user_account (
43
INSERT INTO bill_approval_user_account (
48
company_id,
44
company_id,
49
account_id,
45
account_id,
50
status_id,
46
status_id,
51
user_id,
47
user_id,
52
approval_level,
48
approval_level,
53
is_deleted,
49
is_deleted,
54
created_on_utc,
50
created_on_utc,
55
created_by
51
created_by
56
)
52
)
57
VALUES (
53
VALUES (
58
p_company_id,
54
p_company_id,
59
p_account_id,
55
p_account_id,
60
(approval_item->>'statusId')::INT,
56
(approval_item->>'statusId')::INT,
61
(approval_item->>'userId')::UUID,
57
(approval_item->>'userId')::UUID,
62
(approval_item->>'approvalLevel')::INT,
58
(approval_item->>'approvalLevel')::INT,
63
FALSE,
59
FALSE,
64
NOW(),
60
NOW(),
65
p_modified_by
61
p_modified_by
66
);
62
);
67
END IF;
63
END IF;
68
END LOOP;
64
END LOOP;
69
65
70
-- 3️⃣ Upsert bill_account_approval_levels:
66
-- 3️⃣ Upsert bill_account_approval_levels:
71
IF EXISTS (
67
IF EXISTS (
72
SELECT 1
68
SELECT 1
73
FROM bill_account_approval_levels
69
FROM bill_account_approval_levels
74
WHERE company_id = p_company_id
70
WHERE company_id = p_company_id
75
AND account_id = p_account_id
71
AND account_id = p_account_id
76
AND status_id = 2 -- temparary updating only for status 2
72
AND status_id = 2 -- temparary updating only for status 2
77
) THEN
73
) THEN
78
UPDATE bill_account_approval_levels
74
UPDATE bill_account_approval_levels
79
SET approval_level_required = p_required_approval_levels,
75
SET approval_level_required = p_required_approval_levels,
80
status_id = 2,
76
status_id = 2,
81
modified_on_utc = NOW(),
77
modified_on_utc = NOW(),
82
modified_by = p_modified_by
78
modified_by = p_modified_by
83
WHERE company_id = p_company_id
79
WHERE company_id = p_company_id
84
AND account_id = p_account_id
80
AND account_id = p_account_id;
85
AND status_id = 2; -- temparary checking for only status 2
81
-- AND status_id = p_status_id; temparary checking for only status 2
86
ELSE
82
ELSE
87
INSERT INTO bill_account_approval_levels (
83
INSERT INTO bill_account_approval_levels (
88
company_id,
84
company_id,
89
account_id,
85
account_id,
90
status_id,
86
status_id,
91
approval_level_required,
87
approval_level_required,
92
created_on_utc,
88
created_on_utc,
93
created_by
89
created_by
94
)
90
)
95
VALUES (
91
VALUES (
96
p_company_id,
92
p_company_id,
97
p_account_id,
93
p_account_id,
98
2,
94
2,
99
p_required_approval_levels,
95
p_required_approval_levels,
100
NOW(),
96
NOW(),
101
p_modified_by
97
p_modified_by
102
);
98
);
103
END IF;
99
END IF;
104
100
105
END;
101
END;
106
$procedure$
102
$procedure$
|
|||||
| Procedure | transfer_draft_to_bill | Mismatch |
Source Script
Target Script
1
CREATE OR REPLACE PROCEDURE public.transfer_draft_to_bill(IN p_draft_bill_id uuid, IN p_modified_by uuid)
1
CREATE OR REPLACE PROCEDURE public.transfer_draft_to_bill(IN p_draft_bill_id uuid, IN p_modified_by uuid)
2
LANGUAGE plpgsql
2
LANGUAGE plpgsql
3
AS $procedure$
3
AS $procedure$
4
DECLARE
4
DECLARE
5
draft_bill RECORD;
5
draft_bill RECORD;
6
draft_bill_details RECORD;
6
draft_bill_details RECORD;
7
7
8
BEGIN
8
BEGIN
9
-- Fetch the draft bill header
9
-- Fetch the draft bill header
10
SELECT * INTO draft_bill
10
SELECT * INTO draft_bill
11
FROM public.draft_bill_headers
11
FROM public.draft_bill_headers
12
WHERE id = p_draft_bill_id;
12
WHERE id = p_draft_bill_id;
13
13
14
IF draft_bill IS NULL THEN
14
IF draft_bill IS NULL THEN
15
RAISE EXCEPTION 'Draft bill not found: %', p_draft_bill_id;
15
RAISE EXCEPTION 'Draft bill not found: %', p_draft_bill_id;
16
END IF;
16
END IF;
17
17
18
-- Call to create bill header using the procedure
18
-- Call to create bill header using the procedure
19
CALL public.create_bill_header(
19
CALL public.create_bill_header(
20
draft_bill.id,
20
draft_bill.id,
21
draft_bill.company_id,
21
draft_bill.company_id,
22
draft_bill.vendor_id,
22
draft_bill.vendor_id,
23
draft_bill.credit_account_id,
23
draft_bill.credit_account_id,
24
draft_bill.debit_account_id,
24
draft_bill.debit_account_id,
25
draft_bill.bill_date::date,
25
draft_bill.bill_date::date,
26
draft_bill.due_date::date,
26
draft_bill.due_date::date,
27
draft_bill.payment_term,
27
draft_bill.payment_term,
28
draft_bill.taxable_amount,
28
draft_bill.taxable_amount,
29
draft_bill.cgst_amount,
29
draft_bill.cgst_amount,
30
draft_bill.sgst_amount,
30
draft_bill.sgst_amount,
31
draft_bill.igst_amount,
31
draft_bill.igst_amount,
32
draft_bill.tds_amount,
32
draft_bill.tds_amount,
33
draft_bill.total_amount,
33
draft_bill.total_amount,
34
draft_bill.discount,
34
draft_bill.discount,
35
draft_bill.fees,
35
draft_bill.fees,
36
draft_bill.round_off,
36
draft_bill.round_off,
37
draft_bill.round_off_tds,
38
draft_bill.currency_id,
37
draft_bill.currency_id,
39
draft_bill.note,
38
draft_bill.note,
40
draft_bill.bill_status_id,
39
draft_bill.bill_status_id,
41
draft_bill.created_by,
40
draft_bill.created_by,
42
draft_bill.source_warehouse_id,
41
draft_bill.source_warehouse_id,
43
draft_bill.destination_warehouse_id,
42
draft_bill.destination_warehouse_id,
44
draft_bill.bill_type_id,
43
draft_bill.bill_type_id,
45
draft_bill.bill_number,
44
draft_bill.bill_number,
46
draft_bill.po_no,
45
draft_bill.po_no,
47
draft_bill.po_date::date
46
draft_bill.po_date::date
48
);
47
);
49
48
50
-- Raise a notice to indicate success
49
-- Raise a notice to indicate success
51
RAISE NOTICE 'Bill header inserted with ID: %', draft_bill.id;
50
RAISE NOTICE 'Bill header inserted with ID: %', draft_bill.id;
52
51
53
-- Fetch and insert bill details
52
-- Fetch and insert bill details
54
FOR draft_bill_details IN
53
FOR draft_bill_details IN
55
SELECT * FROM public.draft_bill_details
54
SELECT * FROM public.draft_bill_details
56
WHERE bill_header_id = p_draft_bill_id
55
WHERE bill_header_id = p_draft_bill_id
57
LOOP
56
LOOP
58
INSERT INTO public.bill_details(
57
INSERT INTO public.bill_details(
59
id, bill_header_id, product_id, price, quantity, fees, discount,
58
id, bill_header_id, product_id, price, quantity, fees, discount,
60
taxable_amount, sgst_amount, cgst_amount, igst_amount, total_amount, serial_number)
59
taxable_amount, sgst_amount, cgst_amount, igst_amount, total_amount, serial_number)
61
VALUES(
60
VALUES(
62
draft_bill_details.id, draft_bill_details.bill_header_id,
61
draft_bill_details.id, draft_bill_details.bill_header_id,
63
draft_bill_details.product_id, draft_bill_details.price,
62
draft_bill_details.product_id, draft_bill_details.price,
64
draft_bill_details.quantity, draft_bill_details.fees,
63
draft_bill_details.quantity, draft_bill_details.fees,
65
draft_bill_details.discount, draft_bill_details.taxable_amount,
64
draft_bill_details.discount, draft_bill_details.taxable_amount,
66
draft_bill_details.sgst_amount, draft_bill_details.cgst_amount,
65
draft_bill_details.sgst_amount, draft_bill_details.cgst_amount,
67
draft_bill_details.igst_amount, draft_bill_details.total_amount,
66
draft_bill_details.igst_amount, draft_bill_details.total_amount,
68
draft_bill_details.serial_number
67
draft_bill_details.serial_number
69
);
68
);
70
69
71
UPDATE public.draft_bill_details
70
UPDATE public.draft_bill_details
72
SET
71
SET
73
is_deleted = TRUE,
72
is_deleted = TRUE,
74
deleted_on_utc = now()
73
deleted_on_utc = now()
75
WHERE bill_header_id = p_draft_bill_id;
74
WHERE bill_header_id = p_draft_bill_id;
76
75
77
END LOOP;
76
END LOOP;
78
77
79
-- Mark the draft bill as deleted
78
-- Mark the draft bill as deleted
80
UPDATE public.draft_bill_headers
79
UPDATE public.draft_bill_headers
81
SET
80
SET
82
is_deleted = TRUE,
81
is_deleted = TRUE,
83
deleted_on_utc = now()
82
deleted_on_utc = now()
84
WHERE id = p_draft_bill_id;
83
WHERE id = p_draft_bill_id;
85
84
86
85
87
END;
86
END;
88
$procedure$
87
$procedure$
|
|||||
| Procedure | update_bill_next_status | Match | ||||||
| Procedure | update_bill_next_status_for_bill_header | Match | ||||||
| Procedure | upsert_bill_status_company_config_json | Match | ||||||
| Procedure | clean_up_org_purchase | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE PROCEDURE public.clean_up_org_purchase(IN p_organization_id uuid)
2
LANGUAGE plpgsql
3
AS $procedure$
4
DECLARE
5
v_company_id uuid;
6
v_vendor_id uuid;
7
v_bill_header_id uuid;
8
v_bill_payment_header_id uuid;
9
v_vendor_note_header_id uuid;
10
v_gate_pass_header_id uuid;
11
v_user_id uuid;
12
BEGIN
13
FOR v_company_id IN SELECT id FROM companies WHERE organization_id = p_organization_id LOOP
14
-- Vendors and their children
15
FOR v_vendor_id IN SELECT id FROM vendors WHERE company_id = v_company_id LOOP
16
DELETE FROM vendor_contacts WHERE vendor_id = v_vendor_id;
17
DELETE FROM vendor_bank_accounts WHERE vendor_id = v_vendor_id;
18
DELETE FROM vendor_upis WHERE vendor_id = v_vendor_id;
19
DELETE FROM vendor_default_accounts WHERE vendor_id = v_vendor_id;
20
FOR v_vendor_note_header_id IN SELECT id FROM vendor_note_headers WHERE vendor_id = v_vendor_id LOOP
21
DELETE FROM vendor_note_details WHERE vendor_note_header_id = v_vendor_note_header_id;
22
END LOOP;
23
DELETE FROM vendor_note_headers WHERE vendor_id = v_vendor_id;
24
-- If you want to delete warehouses for this vendor:
25
-- DELETE FROM warehouses WHERE vendor_id = v_vendor_id;
26
END LOOP;
27
DELETE FROM vendors WHERE company_id = v_company_id;
28
DELETE FROM vendor_note_work_flows WHERE company_id = v_company_id;
29
DELETE FROM vendor_note_statuses WHERE company_id = v_company_id;
30
31
-- Bills and their children
32
FOR v_bill_header_id IN SELECT id FROM bill_headers WHERE company_id = v_company_id LOOP
33
DELETE FROM bill_details WHERE bill_header_id = v_bill_header_id;
34
DELETE FROM bill_approval_logs WHERE bill_id = v_bill_header_id;
35
DELETE FROM bill_approval_issue_logs WHERE bill_id = v_bill_header_id;
36
END LOOP;
37
DELETE FROM bill_headers WHERE company_id = v_company_id;
38
DELETE FROM bill_header_ids WHERE company_id = v_company_id;
39
DELETE FROM bill_workflow WHERE company_id = v_company_id;
40
DELETE FROM bill_status_company_configs WHERE company_id = v_company_id;
41
DELETE FROM bill_account_approval_levels WHERE company_id = v_company_id;
42
DELETE FROM bill_approval_user_account WHERE company_id = v_company_id;
43
DELETE FROM bill_approval_user_company WHERE company_id = v_company_id;
44
45
-- Bill payments
46
FOR v_bill_payment_header_id IN SELECT id FROM bill_payment_headers WHERE company_id = v_company_id LOOP
47
DELETE FROM bill_payment_details WHERE bill_payment_header_id = v_bill_payment_header_id;
48
END LOOP;
49
DELETE FROM bill_payment_headers WHERE company_id = v_company_id;
50
DELETE FROM bill_payment_header_ids WHERE company_id = v_company_id;
51
52
-- Draft bills
53
FOR v_bill_header_id IN SELECT id FROM draft_bill_headers WHERE company_id = v_company_id LOOP
54
DELETE FROM draft_bill_details WHERE bill_header_id = v_bill_header_id;
55
END LOOP;
56
DELETE FROM draft_bill_headers WHERE company_id = v_company_id;
57
DELETE FROM draft_bill_header_ids WHERE company_id = v_company_id;
58
59
-- Recurring bills
60
DELETE FROM recurring_bill_schedules WHERE company_id = v_company_id;
61
62
-- Gate pass and details
63
FOR v_gate_pass_header_id IN SELECT id FROM gate_pass_headers WHERE company_id = v_company_id LOOP
64
DELETE FROM gate_pass_details WHERE gate_pass_header_id = v_gate_pass_header_id;
65
END LOOP;
66
DELETE FROM gate_pass_headers WHERE company_id = v_company_id;
67
68
-- Users & their roles
69
FOR v_user_id IN SELECT id FROM users WHERE company_id = v_company_id LOOP
70
DELETE FROM user_roles WHERE user_id = v_user_id;
71
END LOOP;
72
DELETE FROM users WHERE company_id = v_company_id;
73
74
-- Finally, company itself
75
DELETE FROM companies WHERE id = v_company_id;
76
END LOOP;
77
78
-- Organization record itself
79
DELETE FROM organizations WHERE id = p_organization_id;
80
81
RAISE NOTICE 'Organization % and all related purchase data deleted.', p_organization_id;
82
END;
83
$procedure$
|
|||||
| Procedure | copy_bills | Match | ||||||
| Procedure | hard_delete_org_purchase | Match | ||||||
| Procedure | hard_delete_organization | Match | ||||||
| Procedure | initialize_company | Match | ||||||
| Procedure | log_bill_approval | Match | ||||||
| Procedure | update_bill_account_approval_user | Match | ||||||
| Procedure | update_company_level_approval_configuration | Mismatch |
Source Script
Target Script
1
CREATE OR REPLACE PROCEDURE public.update_company_level_approval_configuration(IN p_company_id uuid, IN p_status_id integer, IN p_required_approval_levels integer, IN p_approvals jsonb, IN p_modified_by uuid)
1
CREATE OR REPLACE PROCEDURE public.update_company_level_approval_configuration(IN p_company_id uuid, IN p_status_id integer, IN p_required_approval_levels integer, IN p_approvals jsonb, IN p_modified_by uuid)
2
LANGUAGE plpgsql
2
LANGUAGE plpgsql
3
AS $procedure$
3
AS $procedure$
4
4
5
DECLARE
5
DECLARE
6
approval_item JSONB;
6
approval_item JSONB;
7
existing_id INT;
7
existing_id INT;
8
upsert_count INT := 0;
9
delete_count INT := 0;
10
BEGIN
8
BEGIN
11
RAISE NOTICE 'Starting update_company_level_approval_configuration for company %, status %', p_company_id, p_status_id;
12
RAISE NOTICE 'Required approval levels: %', p_required_approval_levels;
13
RAISE NOTICE 'Approvals payload: %', p_approvals;
14
15
-- 1️⃣ Soft delete old records not present in incoming approvals:
9
-- 1️⃣ Soft delete old records not present in incoming approvals:
16
UPDATE bill_approval_user_company
10
UPDATE bill_approval_user_company
17
SET is_deleted = TRUE,
11
SET is_deleted = TRUE,
18
deleted_on_utc = NOW(),
12
deleted_on_utc = NOW(),
19
modified_on_utc = NOW(),
13
modified_on_utc = NOW(),
20
modified_by = p_modified_by
14
modified_by = p_modified_by
21
WHERE company_id = p_company_id
15
WHERE company_id = p_company_id
22
AND is_deleted = FALSE
23
AND NOT EXISTS (
16
AND NOT EXISTS (
24
SELECT 1
17
SELECT 1
25
FROM jsonb_array_elements(p_approvals) AS elem
18
FROM jsonb_array_elements(p_approvals) AS elem
26
WHERE (elem->>'userId')::UUID = bill_approval_user_company.user_id
19
WHERE (elem->>'userId')::UUID = bill_approval_user_company.user_id
27
AND (elem->>'statusId')::INT = bill_approval_user_company.status_id
28
AND (elem->>'approvalLevel')::INT = bill_approval_user_company.approval_level
20
AND (elem->>'approvalLevel')::INT = bill_approval_user_company.approval_level
29
);
21
);
30
22
31
GET DIAGNOSTICS delete_count = ROW_COUNT;
32
RAISE NOTICE 'Soft deleted % records not present in payload', delete_count;
33
34
-- 2️⃣ Upsert incoming approvals:
23
-- 2️⃣ Upsert incoming approvals:
35
FOR approval_item IN SELECT * FROM jsonb_array_elements(p_approvals)
24
FOR approval_item IN SELECT * FROM jsonb_array_elements(p_approvals)
36
LOOP
25
LOOP
37
SELECT id INTO existing_id
26
SELECT id INTO existing_id
38
FROM bill_approval_user_company
27
FROM bill_approval_user_company
39
WHERE company_id = p_company_id
28
WHERE company_id = p_company_id
40
AND user_id = (approval_item->>'userId')::UUID
29
AND user_id = (approval_item->>'userId')::UUID
41
AND status_id = (approval_item->>'statusId')::INT
42
AND approval_level = (approval_item->>'approvalLevel')::INT
30
AND approval_level = (approval_item->>'approvalLevel')::INT
43
AND is_deleted = FALSE
31
AND is_deleted = FALSE
44
LIMIT 1;
32
LIMIT 1;
45
33
46
IF existing_id IS NOT NULL THEN
34
IF existing_id IS NOT NULL THEN
47
UPDATE bill_approval_user_company
35
UPDATE bill_approval_user_company
48
SET status_id = (approval_item->>'statusId')::INT,
36
SET status_id = (approval_item->>'statusId')::INT,
49
modified_on_utc = NOW(),
37
modified_on_utc = NOW(),
50
modified_by = p_modified_by
38
modified_by = p_modified_by
51
WHERE id = existing_id;
39
WHERE id = existing_id;
52
RAISE NOTICE 'Updated approval for user %, level %, status %', (approval_item->>'userId'), (approval_item->>'approvalLevel'), (approval_item->>'statusId');
53
ELSE
40
ELSE
54
INSERT INTO bill_approval_user_company (
41
INSERT INTO bill_approval_user_company (
55
company_id,
42
company_id,
56
status_id,
43
status_id,
57
user_id,
44
user_id,
58
approval_level,
45
approval_level,
59
is_deleted,
46
is_deleted,
60
created_on_utc,
47
created_on_utc,
61
created_by
48
created_by
62
)
49
)
63
VALUES (
50
VALUES (
64
p_company_id,
51
p_company_id,
65
(approval_item->>'statusId')::INT,
52
(approval_item->>'statusId')::INT,
66
(approval_item->>'userId')::UUID,
53
(approval_item->>'userId')::UUID,
67
(approval_item->>'approvalLevel')::INT,
54
(approval_item->>'approvalLevel')::INT,
68
FALSE,
55
FALSE,
69
NOW(),
56
NOW(),
70
p_modified_by
57
p_modified_by
71
);
58
);
72
RAISE NOTICE 'Inserted approval for user %, level %, status %', (approval_item->>'userId'), (approval_item->>'approvalLevel'), (approval_item->>'statusId');
73
END IF;
59
END IF;
74
upsert_count := upsert_count + 1;
75
END LOOP;
60
END LOOP;
76
61
77
RAISE NOTICE 'Upserted % approval records', upsert_count;
62
-- 3️⃣ Update workflow approval level:
78
79
-- 3️⃣ Update workflow approval level for the given status:
80
UPDATE bill_workflow
63
UPDATE bill_workflow
81
SET approval_level = p_required_approval_levels
64
SET approval_level = p_required_approval_levels
82
WHERE company_id = p_company_id
65
WHERE company_id = p_company_id
83
AND status = p_status_id;
66
AND status = 2;
84
67
85
RAISE NOTICE 'Updated bill_workflow approval_level to % for company % and status %', p_required_approval_levels, p_company_id, p_status_id;
86
87
RAISE NOTICE 'Completed update_company_level_approval_configuration for company %', p_company_id;
88
END;
68
END;
89
$procedure$
69
$procedure$
|
|||||
| Procedure | purge_purchase_organization_data | Match | ||||||
| Procedure | insert_draft_bill | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE PROCEDURE public.insert_draft_bill(IN p_id uuid, IN p_company_id uuid, IN p_vendor_id uuid, IN p_discount numeric, IN p_currency_id integer, IN p_bill_date timestamp without time zone, IN p_payment_term integer, IN p_bill_status_id integer, IN p_due_date timestamp without time zone, IN p_total_amount numeric, IN p_taxable_amount numeric, IN p_fees numeric, IN p_sgst_amount numeric, IN p_cgst_amount numeric, IN p_igst_amount numeric, IN p_tds_amount numeric, IN p_note text, IN p_round_off numeric, IN p_round_off_tds numeric, IN p_debit_account_id uuid, IN p_credit_account_id uuid, IN p_po_no text, IN p_po_date timestamp without time zone, IN p_source_warehouse_id uuid, IN p_destination_warehouse_id uuid, IN p_bill_type_id integer, IN p_created_by uuid, IN p_bill_details jsonb, IN p_bill_number text, IN p_setteled_amount numeric DEFAULT 0)
2
LANGUAGE plpgsql
3
AS $procedure$
4
DECLARE
5
detail RECORD; -- Declaring a record variable for the loop
6
v_source_warehouse_id uuid;
7
v_destination_warehouse_id uuid;
8
v_detail_id uuid;
9
BEGIN
10
-- Set default values for warehouses based on bill type
11
IF p_bill_type_id = 2 OR p_bill_type_id = 3 THEN
12
v_source_warehouse_id := NULL;
13
v_destination_warehouse_id := NULL;
14
ELSE
15
v_source_warehouse_id := COALESCE(p_source_warehouse_id, NULL);
16
v_destination_warehouse_id := COALESCE(p_destination_warehouse_id, NULL);
17
END IF;
18
19
RAISE NOTICE 'v_source_warehouse_id: %', v_source_warehouse_id;
20
RAISE NOTICE 'v_destination_warehouse_id: %', v_destination_warehouse_id;
21
22
-- Call to create_draft_bill_header procedure
23
CALL public.create_draft_bill_header(
24
p_id,
25
p_company_id,
26
p_vendor_id,
27
p_credit_account_id,
28
p_debit_account_id,
29
p_bill_date::date,
30
p_due_date::date,
31
p_payment_term,
32
p_taxable_amount,
33
p_cgst_amount,
34
p_sgst_amount,
35
p_igst_amount,
36
p_tds_amount,
37
p_total_amount,
38
p_discount,
39
p_fees,
40
p_round_off,
41
p_round_off_tds,
42
p_currency_id,
43
p_note ,
44
p_bill_status_id ,
45
p_created_by ,
46
v_source_warehouse_id ,
47
v_destination_warehouse_id ,
48
p_bill_type_id,
49
p_bill_number,
50
p_po_no,
51
p_po_date::date
52
);
53
54
RAISE NOTICE 'Bill header inserted with ID: %', p_id;
55
56
-- Loop through the JSONB array of bill details
57
FOR detail IN
58
SELECT * FROM jsonb_to_recordset(p_bill_details) AS (
59
id uuid, product_id uuid, price numeric, quantity numeric,
60
fees numeric, discount numeric, taxable_amount numeric,
61
sgst_amount numeric, cgst_amount numeric, igst_amount numeric,
62
total_amount numeric, serial_number integer
63
)
64
LOOP
65
-- Log each field to ensure they are being read correctly
66
--RAISE NOTICE 'Detail - product_id: %, price: %, quantity: %, taxable_amount: %', detail."productId", detail.price, detail.quantity, detail."taxableAmount";
67
68
-- Insert into draft_bill_details, using "productId" from JSON
69
INSERT INTO public.draft_bill_details (
70
id, bill_header_id, product_id, price, quantity, fees, discount,
71
taxable_amount, sgst_amount, cgst_amount, igst_amount,
72
total_amount, serial_number, is_deleted
73
) VALUES (
74
detail.id, p_id, detail.product_id, detail.price, detail.quantity,
75
detail.fees, detail.discount, detail.taxable_amount, detail.sgst_amount,
76
detail.cgst_amount, detail.igst_amount, detail.total_Amount, detail.serial_number, false
77
);
78
RAISE NOTICE 'Bill detail inserted with ID: %', detail.id;
79
END LOOP;
80
81
-- No explicit COMMIT needed
82
83
EXCEPTION
84
-- No explicit ROLLBACK needed; just raise the error
85
WHEN OTHERS THEN
86
RAISE NOTICE 'An error occurred: %, transaction rolled back', SQLERRM;
87
RAISE;
88
END;
89
$procedure$
|
|||||
| Procedure | insert_bill_by_excel | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE PROCEDURE public.insert_bill_by_excel(IN p_id uuid, IN p_company_id uuid, IN p_vendor_id uuid, IN p_discount numeric, IN p_currency_id integer, IN p_bill_date timestamp without time zone, IN p_bill_number text, IN p_payment_term integer, IN p_bill_status_id integer, IN p_due_date timestamp without time zone, IN p_total_amount numeric, IN p_taxable_amount numeric, IN p_fees numeric, IN p_sgst_amount numeric, IN p_cgst_amount numeric, IN p_igst_amount numeric, IN p_note text, IN p_round_off numeric, IN p_round_off_tds numeric, IN p_debit_account_id uuid, IN p_credit_account_id uuid, IN p_po_no text, IN p_po_date timestamp without time zone, IN p_source_warehouse_id uuid, IN p_destination_warehouse_id uuid, IN p_created_by uuid, IN p_bill_details jsonb, IN p_type integer, IN p_setteled_amount numeric DEFAULT 0)
2
LANGUAGE plpgsql
3
AS $procedure$
4
DECLARE
5
detail RECORD;
6
v_source_warehouse_id uuid;
7
v_destination_warehouse_id uuid;
8
v_bill_voucher text;
9
v_created_by uuid;
10
BEGIN
11
-- Set default values for warehouses
12
v_source_warehouse_id := COALESCE(p_source_warehouse_id, NULL);
13
v_destination_warehouse_id := COALESCE(p_destination_warehouse_id, NULL);
14
v_bill_voucher := get_new_bill_voucher(p_company_id, p_bill_date::date);
15
16
-- Check if the bill voucher was generated successfully
17
--IF v_bill_voucher IS NULL THEN
18
-- RAISE EXCEPTION 'Failed to generate bill voucher for company ID: %', p_company_id;
19
--ELSE
20
-- RAISE NOTICE 'Generated Bill Voucher: %', v_bill_voucher;
21
--END IF;
22
23
-- Fetch the system user ID from the users table
24
SELECT id INTO v_created_by FROM public.users
25
WHERE first_name = 'System'
26
LIMIT 1;
27
28
-- Raise an exception if the system user is not found
29
IF v_created_by IS NULL THEN
30
RAISE EXCEPTION 'System user not found in the users table';
31
END IF;
32
33
-- Insert into the bill header
34
INSERT INTO public.bill_headers(
35
id,
36
company_id,
37
vendor_id,
38
credit_account_id,
39
debit_account_id,
40
bill_voucher,
41
bill_number,
42
bill_date,
43
due_date,
44
payment_term,
45
taxable_amount,
46
cgst_amount,
47
sgst_amount,
48
igst_amount,
49
tds_amount,
50
total_amount,
51
discount,
52
fees,
53
round_off,
54
round_off_tds,
55
currency_id,
56
note,
57
bill_status_id,
58
created_by,
59
source_warehouse_id,
60
destination_warehouse_id,
61
bill_type_id,
62
created_on_utc,
63
po_no,
64
po_date
65
)
66
VALUES (
67
p_id,
68
p_company_id,
69
p_vendor_id,
70
p_credit_account_id,
71
p_debit_account_id,
72
v_bill_voucher,
73
p_bill_number,
74
p_bill_date,
75
p_due_date,
76
p_payment_term,
77
p_taxable_amount,
78
p_cgst_amount,
79
p_sgst_amount,
80
p_igst_amount,
81
0,
82
p_total_amount,
83
p_discount,
84
p_fees,
85
p_round_off,
86
p_round_off_tds,
87
p_currency_id,
88
p_note,
89
p_bill_status_id,
90
p_created_by,
91
p_source_warehouse_id,
92
p_destination_warehouse_id,
93
p_type,
94
(CURRENT_TIMESTAMP AT TIME ZONE 'Asia/Kolkata')::timestamp,
95
p_po_no,
96
p_po_date
97
);
98
99
RAISE NOTICE 'bill header inserted with ID: %', p_id;
100
EXCEPTION
101
-- Handle unique violation (duplicate key)
102
WHEN unique_violation THEN
103
RAISE NOTICE 'Duplicate entry: %, rolling back', SQLERRM;
104
ROLLBACK;
105
106
-- Handle foreign key violation
107
WHEN foreign_key_violation THEN
108
RAISE NOTICE 'Foreign key violation: %, rolling back', SQLERRM;
109
ROLLBACK;
110
111
-- Catch all other exceptions
112
WHEN OTHERS THEN
113
RAISE NOTICE 'An error occurred: %, transaction rolled back', SQLERRM;
114
ROLLBACK;
115
END;
116
$procedure$
|
|||||
| Procedure | insert_bill | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE PROCEDURE public.insert_bill(IN p_id uuid, IN p_company_id uuid, IN p_vendor_id uuid, IN p_discount numeric, IN p_currency_id integer, IN p_bill_date timestamp without time zone, IN p_payment_term integer, IN p_bill_status_id integer, IN p_due_date timestamp without time zone, IN p_total_amount numeric, IN p_taxable_amount numeric, IN p_fees numeric, IN p_sgst_amount numeric, IN p_cgst_amount numeric, IN p_igst_amount numeric, IN p_tds_amount numeric, IN p_note text, IN p_round_off numeric, IN p_round_off_tds numeric, IN p_debit_account_id uuid, IN p_credit_account_id uuid, IN p_po_no text, IN p_po_date timestamp without time zone, IN p_source_warehouse_id uuid, IN p_destination_warehouse_id uuid, IN p_bill_type_id integer, IN p_created_by uuid, IN p_bill_details jsonb, IN p_bill_number text, IN p_setteled_amount numeric DEFAULT 0)
2
LANGUAGE plpgsql
3
AS $procedure$
4
DECLARE
5
detail RECORD; -- Declaring a record variable for the loop
6
v_source_warehouse_id uuid;
7
v_destination_warehouse_id uuid;
8
v_detail_id uuid;
9
BEGIN
10
-- Set default values for warehouses based on bill type
11
IF p_bill_type_id = 2 OR p_bill_type_id = 3 THEN
12
v_source_warehouse_id := NULL;
13
v_destination_warehouse_id := NULL;
14
ELSE
15
v_source_warehouse_id := COALESCE(p_source_warehouse_id, NULL);
16
v_destination_warehouse_id := COALESCE(p_destination_warehouse_id, NULL);
17
END IF;
18
19
RAISE NOTICE 'v_source_warehouse_id: %', v_source_warehouse_id;
20
RAISE NOTICE 'v_destination_warehouse_id: %', v_destination_warehouse_id;
21
22
-- Call to create_bill_header procedure
23
CALL public.create_bill_header(
24
p_id,
25
p_company_id,
26
p_vendor_id,
27
p_credit_account_id,
28
p_debit_account_id,
29
p_bill_date::date,
30
p_due_date::date,
31
p_payment_term,
32
p_taxable_amount,
33
p_cgst_amount,
34
p_sgst_amount,
35
p_igst_amount,
36
p_tds_amount,
37
p_total_amount,
38
p_discount,
39
p_fees,
40
p_round_off,
41
p_round_off_tds,
42
p_currency_id,
43
p_note ,
44
p_bill_status_id ,
45
p_created_by ,
46
v_source_warehouse_id ,
47
v_destination_warehouse_id ,
48
p_bill_type_id,
49
p_bill_number
50
);
51
52
RAISE NOTICE 'Bill header inserted with ID: %', p_id;
53
54
-- Loop through the JSONB array of bill details
55
FOR detail IN
56
SELECT * FROM jsonb_to_recordset(p_bill_details) AS (
57
id uuid, product_id uuid, price numeric, quantity integer,
58
fees numeric, discount numeric, taxable_amount numeric,
59
sgst_amount numeric, cgst_amount numeric, igst_amount numeric,
60
total_amount numeric, serial_number integer
61
)
62
LOOP
63
INSERT INTO public.bill_details (
64
id, bill_header_id, product_id, price, quantity, fees, discount,
65
taxable_amount, sgst_amount, cgst_amount, igst_amount,
66
total_amount, serial_number, is_deleted
67
) VALUES (
68
detail.id, p_id, detail.product_id, detail.price, detail.quantity,
69
detail.fees, detail.discount, detail.taxable_amount, detail.sgst_amount,
70
detail.cgst_amount, detail.igst_amount, detail.total_Amount, detail.serial_number, false
71
);
72
RAISE NOTICE 'Bill detail inserted with ID: %', detail.id;
73
END LOOP;
74
75
-- No explicit COMMIT needed
76
77
EXCEPTION
78
-- No explicit ROLLBACK needed; just raise the error
79
WHEN OTHERS THEN
80
RAISE NOTICE 'An error occurred: %, transaction rolled back', SQLERRM;
81
RAISE;
82
END;
83
$procedure$
|
|||||
| Procedure | create_draft_bill_header | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE PROCEDURE public.create_draft_bill_header(IN p_id uuid, IN p_company_id uuid, IN p_vendor_id uuid, IN p_credit_account_id uuid, IN p_debit_account_id uuid, IN p_bill_date date, IN p_due_date date, IN p_payment_term integer, IN p_taxable_amount numeric, IN p_cgst_amount numeric, IN p_sgst_amount numeric, IN p_igst_amount numeric, IN p_tds_amount numeric, IN p_total_amount numeric, IN p_discount numeric, IN p_fees numeric, IN p_round_off numeric, IN p_round_off_tds numeric, IN p_currency_id integer, IN p_note character varying, IN p_bill_status_id integer, IN p_created_by uuid, IN p_source_warehouse_id uuid DEFAULT NULL::uuid, IN p_destination_warehouse_id uuid DEFAULT NULL::uuid, IN p_bill_type_id integer DEFAULT 1, IN p_bill_number character varying DEFAULT NULL::character varying, IN p_po_no text DEFAULT NULL::text, IN p_po_date date DEFAULT NULL::date)
2
LANGUAGE plpgsql
3
AS $procedure$
4
DECLARE
5
v_bill_voucher character varying;
6
source_warehouse_id uuid;
7
destination_warehouse_id uuid;
8
BEGIN
9
-- Generate bill voucher using the draft bill function
10
v_bill_voucher := get_new_draft_bill_voucher(p_company_id,p_bill_date);
11
12
-- Log the generated bill voucher
13
RAISE NOTICE 'Bill Voucher: %', v_bill_voucher;
14
15
-- Set default values for warehouses based on bill type
16
IF p_bill_type_id = 2 OR p_bill_type_id = 3 THEN
17
source_warehouse_id := NULL;
18
destination_warehouse_id := NULL;
19
ELSE
20
-- Use passed values or default to null if not provided
21
source_warehouse_id := COALESCE(p_source_warehouse_id, NULL);
22
destination_warehouse_id := COALESCE(p_destination_warehouse_id, NULL);
23
END IF;
24
25
-- Insert into draft_bill_headers table
26
INSERT INTO
27
public.draft_bill_headers(
28
id,
29
company_id,
30
vendor_id,
31
credit_account_id,
32
debit_account_id,
33
bill_voucher,
34
bill_number,
35
bill_date,
36
due_date,
37
payment_term,
38
taxable_amount,
39
cgst_amount,
40
sgst_amount,
41
igst_amount,
42
tds_amount,
43
total_amount,
44
discount,
45
fees,
46
round_off,
47
round_off_tds,
48
currency_id,
49
note,
50
bill_status_id,
51
created_by,
52
source_warehouse_id,
53
destination_warehouse_id,
54
bill_type_id,
55
created_on_utc,
56
po_no,
57
po_date
58
)
59
VALUES (
60
p_id,
61
p_company_id,
62
p_vendor_id,
63
p_credit_account_id,
64
p_debit_account_id,
65
v_bill_voucher,
66
p_bill_number,
67
p_bill_date,
68
p_due_date,
69
p_payment_term,
70
p_taxable_amount,
71
p_cgst_amount,
72
p_sgst_amount,
73
p_igst_amount,
74
p_tds_amount,
75
p_total_amount,
76
p_discount,
77
p_fees,
78
p_round_off,
79
p_round_off_tds,
80
p_currency_id,
81
p_note,
82
p_bill_status_id,
83
p_created_by,
84
p_source_warehouse_id,
85
p_destination_warehouse_id,
86
p_bill_type_id,
87
(CURRENT_TIMESTAMP AT TIME ZONE 'Asia/Kolkata')::timestamp,
88
p_po_no,
89
p_po_date
90
);
91
92
-- Log success message
93
RAISE NOTICE 'Draft Bill header inserted successfully with Bill Voucher: % ', v_bill_voucher ;
94
95
END;
96
$procedure$
|
|||||
| Procedure | create_bill_header | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE PROCEDURE public.create_bill_header(IN p_id uuid, IN p_company_id uuid, IN p_vendor_id uuid, IN p_credit_account_id uuid, IN p_debit_account_id uuid, IN p_bill_date date, IN p_due_date date, IN p_payment_term integer, IN p_taxable_amount numeric, IN p_cgst_amount numeric, IN p_sgst_amount numeric, IN p_igst_amount numeric, IN p_tds_amount numeric, IN p_total_amount numeric, IN p_discount numeric, IN p_fees numeric, IN p_round_off numeric, IN p_round_off_tds numeric, IN p_currency_id integer, IN p_note character varying, IN p_bill_status_id integer, IN p_created_by uuid, IN p_source_warehouse_id uuid DEFAULT NULL::uuid, IN p_destination_warehouse_id uuid DEFAULT NULL::uuid, IN p_bill_type_id integer DEFAULT 1, IN p_bill_number character varying DEFAULT NULL::character varying, IN p_po_no text DEFAULT NULL::text, IN p_po_date date DEFAULT NULL::date)
2
LANGUAGE plpgsql
3
AS $procedure$
4
DECLARE
5
v_bill_voucher character varying;
6
source_warehouse_id uuid;
7
destination_warehouse_id uuid;
8
BEGIN
9
-- Attempt to generate bill voucher
10
v_bill_voucher := get_new_bill_voucher(p_company_id, p_bill_date);
11
12
-- Check if the bill voucher was generated successfully
13
IF v_bill_voucher IS NULL THEN
14
RAISE EXCEPTION 'Failed to generate bill voucher for company ID: %', p_company_id;
15
ELSE
16
RAISE NOTICE 'Generated Bill Voucher: %', v_bill_voucher;
17
END IF;
18
19
-- Set default values for warehouses based on bill type
20
IF p_bill_type_id = 2 OR p_bill_type_id = 3 THEN
21
source_warehouse_id := NULL;
22
destination_warehouse_id := NULL;
23
ELSE
24
-- Use passed values or default to null if not provided
25
source_warehouse_id := COALESCE(p_source_warehouse_id, NULL);
26
destination_warehouse_id := COALESCE(p_destination_warehouse_id, NULL);
27
END IF;
28
29
-- Insert into bill_headers table
30
INSERT INTO public.bill_headers(
31
id,
32
company_id,
33
vendor_id,
34
credit_account_id,
35
debit_account_id,
36
bill_voucher,
37
bill_number,
38
bill_date,
39
due_date,
40
payment_term,
41
taxable_amount,
42
cgst_amount,
43
sgst_amount,
44
igst_amount,
45
tds_amount,
46
total_amount,
47
discount,
48
fees,
49
round_off,
50
round_off_tds,
51
currency_id,
52
note,
53
bill_status_id,
54
created_by,
55
source_warehouse_id,
56
destination_warehouse_id,
57
bill_type_id,
58
created_on_utc,
59
po_no,
60
po_date
61
)
62
VALUES (
63
p_id,
64
p_company_id,
65
p_vendor_id,
66
p_credit_account_id,
67
p_debit_account_id,
68
v_bill_voucher,
69
p_bill_number,
70
p_bill_date,
71
p_due_date,
72
p_payment_term,
73
p_taxable_amount,
74
p_cgst_amount,
75
p_sgst_amount,
76
p_igst_amount,
77
p_tds_amount,
78
p_total_amount,
79
p_discount,
80
p_fees,
81
p_round_off,
82
p_round_off_tds,
83
p_currency_id,
84
p_note,
85
p_bill_status_id,
86
p_created_by,
87
p_source_warehouse_id,
88
p_destination_warehouse_id,
89
p_bill_type_id,
90
(CURRENT_TIMESTAMP AT TIME ZONE 'Asia/Kolkata')::timestamp,
91
p_po_no,
92
p_po_date
93
);
94
95
RAISE NOTICE 'Bill header inserted successfully with Bill Voucher: %', v_bill_voucher;
96
END;
97
$procedure$
|
|||||
| Procedure | run_scheduled_bill | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE PROCEDURE public.run_scheduled_bill(IN p_bill_header_id uuid, IN p_draft_bill_header_id uuid, IN p_schedule_date timestamp without time zone DEFAULT CURRENT_TIMESTAMP)
2
LANGUAGE plpgsql
3
AS $procedure$
4
DECLARE
5
v_system_user_id uuid;
6
v_execution_log_id uuid := gen_random_uuid();
7
v_related_schedule_id uuid;
8
v_new_draft_bill_id uuid;
9
v_source_is_live boolean := false;
10
v_source_bill_id uuid; -- final chosen source id (live or draft)
11
src_header RECORD;
12
src_detail RECORD;
13
BEGIN
14
----------------------------------------------------------------
15
-- 1. Find 'System' user (same as sales side)
16
----------------------------------------------------------------
17
SELECT u.id
18
INTO v_system_user_id
19
FROM public.users AS u
20
WHERE u.is_deleted = false
21
AND lower(u.first_name) = 'system'
22
ORDER BY u.created_on_utc NULLS LAST, u.id
23
LIMIT 1;
24
25
IF v_system_user_id IS NULL THEN
26
RAISE NOTICE 'No user with first_name="System" found; proceeding with NULL triggered_by.';
27
END IF;
28
29
----------------------------------------------------------------
30
-- 2. Decide source: live bill or draft bill (same idea as invoices)
31
----------------------------------------------------------------
32
IF p_bill_header_id IS NOT NULL THEN
33
v_source_bill_id := p_bill_header_id;
34
v_source_is_live := true;
35
ELSIF p_draft_bill_header_id IS NOT NULL THEN
36
v_source_bill_id := p_draft_bill_header_id;
37
v_source_is_live := false;
38
ELSE
39
RAISE NOTICE 'Both bill_header_id and draft_bill_header_id are NULL; aborting.';
40
RETURN;
41
END IF;
42
43
----------------------------------------------------------------
44
-- 3. Get related schedule_id from recurring_bill_schedules
45
-- must match on live OR draft id, just like sales version
46
----------------------------------------------------------------
47
SELECT s.id
48
INTO v_related_schedule_id
49
FROM public.recurring_bill_schedules AS s
50
WHERE s.is_deleted = false
51
AND (
52
(v_source_is_live AND s.bill_header_id = v_source_bill_id)
53
OR (NOT v_source_is_live AND s.draft_bill_header_id = v_source_bill_id)
54
)
55
ORDER BY s.starts_from DESC
56
LIMIT 1;
57
58
----------------------------------------------------------------
59
-- 4. Fetch source header from correct table
60
----------------------------------------------------------------
61
IF v_source_is_live THEN
62
SELECT *
63
INTO src_header
64
FROM public.bill_headers
65
WHERE id = v_source_bill_id
66
AND is_deleted = false;
67
ELSE
68
SELECT *
69
INTO src_header
70
FROM public.draft_bill_headers
71
WHERE id = v_source_bill_id
72
AND is_deleted = false;
73
END IF;
74
75
-- if nothing found, log failure and stop
76
IF NOT FOUND THEN
77
INSERT INTO public.schedule_execution_logs (
78
id, schedule_id, execution_date, execution_time, status, error_message, triggered_by, is_deleted
79
) VALUES (
80
v_execution_log_id,
81
v_related_schedule_id,
82
(p_schedule_date::date),
83
NOW(),
84
'Failure',
85
'Source bill header not found in live or draft tables.',
86
v_system_user_id,
87
false
88
);
89
90
RAISE NOTICE 'Source bill header % not found; aborting for date %.',
91
v_source_bill_id, (p_schedule_date::date);
92
RETURN;
93
END IF;
94
95
----------------------------------------------------------------
96
-- 5. Create new draft bill header (clone header-level data)
97
----------------------------------------------------------------
98
v_new_draft_bill_id := gen_random_uuid();
99
100
CALL public.create_draft_bill_header(
101
/* p_id */ v_new_draft_bill_id,
102
/* p_company_id */ src_header.company_id,
103
/* p_vendor_id */ src_header.vendor_id,
104
/* p_credit_account_id */ src_header.credit_account_id,
105
/* p_debit_account_id */ src_header.debit_account_id,
106
/* p_bill_date */ (p_schedule_date::date),
107
/* p_due_date */ ((p_schedule_date::date) + COALESCE(src_header.payment_term, 10))::date,
108
/* p_payment_term */ src_header.payment_term,
109
/* p_taxable_amount */ src_header.taxable_amount,
110
/* p_cgst_amount */ src_header.cgst_amount,
111
/* p_sgst_amount */ src_header.sgst_amount,
112
/* p_igst_amount */ src_header.igst_amount,
113
/* p_tds_amount */ COALESCE(src_header.tds_amount, 0),
114
/* p_total_amount */ src_header.total_amount,
115
/* p_discount */ src_header.discount,
116
/* p_fees */ src_header.fees,
117
/* p_round_off */ src_header.round_off,
118
/* p_round_off_tds */ COALESCE(src_header.round_off_tds, 0),
119
/* p_currency_id */ src_header.currency_id,
120
/* p_note (varchar) */ COALESCE(src_header.note, '')::varchar,
121
/* p_bill_status_id */ 1,
122
/* p_created_by */ v_system_user_id,
123
/* p_source_wh_id */ src_header.source_warehouse_id,
124
/* p_destination_wh_id */ src_header.destination_warehouse_id,
125
/* p_bill_type_id */ src_header.bill_type_id,
126
/* p_bill_number */ COALESCE(src_header.bill_number, '')::varchar,
127
/* p_po_no */ src_header.po_no::text,
128
/* p_po_date */ (src_header.po_date)::date
129
);
130
131
----------------------------------------------------------------
132
-- 6. Clone all details from correct source table
133
----------------------------------------------------------------
134
IF v_source_is_live THEN
135
FOR src_detail IN
136
SELECT *
137
FROM public.bill_details
138
WHERE bill_header_id = v_source_bill_id
139
LOOP
140
CALL public.create_draft_bill_detail(
141
v_new_draft_bill_id,
142
src_detail.price,
143
src_detail.quantity,
144
src_detail.discount,
145
src_detail.fees,
146
src_detail.cgst_amount,
147
src_detail.igst_amount,
148
src_detail.sgst_amount,
149
src_detail.taxable_amount,
150
src_detail.total_amount,
151
src_detail.serial_number,
152
src_detail.product_id
153
);
154
END LOOP;
155
ELSE
156
FOR src_detail IN
157
SELECT *
158
FROM public.draft_bill_details
159
WHERE bill_header_id = v_source_bill_id
160
LOOP
161
CALL public.create_draft_bill_detail(
162
v_new_draft_bill_id,
163
src_detail.price,
164
src_detail.quantity,
165
src_detail.discount,
166
src_detail.fees,
167
src_detail.cgst_amount,
168
src_detail.igst_amount,
169
src_detail.sgst_amount,
170
src_detail.taxable_amount,
171
src_detail.total_amount,
172
src_detail.serial_number,
173
src_detail.product_id
174
);
175
END LOOP;
176
END IF;
177
178
----------------------------------------------------------------
179
-- 7. Log success
180
----------------------------------------------------------------
181
INSERT INTO public.schedule_execution_logs (
182
id, schedule_id, execution_date, execution_time, status, error_message, triggered_by, is_deleted
183
) VALUES (
184
v_execution_log_id,
185
v_related_schedule_id,
186
(p_schedule_date::date),
187
NOW(),
188
'Success',
189
'',
190
v_system_user_id,
191
false
192
);
193
194
RAISE NOTICE 'Draft bill % created from % (live=%), schedule_id %, on %.',
195
v_new_draft_bill_id, v_source_bill_id, v_source_is_live, v_related_schedule_id, (p_schedule_date::date);
196
197
EXCEPTION WHEN OTHERS THEN
198
INSERT INTO public.schedule_execution_logs (
199
id, schedule_id, execution_date, execution_time, status, error_message, triggered_by, is_deleted
200
) VALUES (
201
COALESCE(v_execution_log_id, gen_random_uuid()),
202
v_related_schedule_id,
203
(p_schedule_date::date),
204
NOW(),
205
'Failure',
206
SQLERRM,
207
v_system_user_id,
208
false
209
);
210
211
RAISE NOTICE 'Error while cloning bill % on %: %',
212
v_source_bill_id, (p_schedule_date::date), SQLERRM;
213
END;
214
$procedure$
|
|||||
| Procedure | update_draft_bill_next_status | Match | ||||||
| Procedure | save_bill_and_details | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE PROCEDURE public.save_bill_and_details(IN p_id uuid, IN p_company_id uuid, IN p_vendor_id uuid, IN p_discount numeric, IN p_bill_date timestamp without time zone, IN p_payment_term integer, IN p_bill_status_id integer, IN p_due_date timestamp without time zone, IN p_total_amount numeric, IN p_taxable_amount numeric, IN p_fees numeric, IN p_sgst_amount numeric, IN p_cgst_amount numeric, IN p_igst_amount numeric, IN p_note text, IN p_round_off numeric, IN p_round_off_tds numeric, IN p_debit_account_id uuid, IN p_credit_account_id uuid, IN p_created_by uuid, IN p_currency_id integer, IN p_po_date timestamp without time zone, IN p_po_no text, IN p_destination_warehouse_id uuid, IN p_source_warehouse_id uuid, IN p_bill_type_id integer, IN p_bill_number text, IN p_tds_amount numeric, IN p_details jsonb)
2
LANGUAGE plpgsql
3
AS $procedure$
4
DECLARE
5
v_bill_voucher text;
6
v_source_warehouse_id uuid;
7
v_destination_warehouse_id uuid;
8
BEGIN
9
-- Set default values for warehouses
10
v_source_warehouse_id := COALESCE(p_source_warehouse_id, '00000000-0000-0000-0000-000000000000'::uuid);
11
v_destination_warehouse_id := COALESCE(p_destination_warehouse_id, '00000000-0000-0000-0000-000000000000'::uuid);
12
13
-- Generate the new bill voucher number
14
v_bill_voucher := public.get_new_bill_voucher(p_company_id, p_bill_date::date);
15
RAISE NOTICE 'Generated Bill Voucher: %', v_bill_voucher;
16
17
-- Validate p_details JSONB
18
IF p_details IS NULL OR jsonb_array_length(p_details) = 0 THEN
19
RAISE EXCEPTION 'Details JSONB parameter (p_details) cannot be empty or NULL';
20
END IF;
21
22
-- Insert into bill_headers table
23
INSERT INTO public.bill_headers (
24
id, company_id, vendor_id, discount, bill_voucher, bill_date, payment_term,
25
bill_status_id, due_date, total_amount, taxable_amount, fees, sgst_amount,
26
cgst_amount, igst_amount, note, round_off, round_off_tds, debit_account_id, credit_account_id,
27
created_on_utc, created_by, currency_id, po_date, po_no, destination_warehouse_id,
28
source_warehouse_id, bill_type_id, bill_number, tds_amount
29
) VALUES (
30
p_id, p_company_id, p_vendor_id, p_discount, v_bill_voucher, p_bill_date,
31
p_payment_term, p_bill_status_id, p_due_date, p_total_amount, p_taxable_amount, p_fees,
32
p_sgst_amount, p_cgst_amount, p_igst_amount, p_note, p_round_off, p_round_off_tds, p_debit_account_id,
33
p_credit_account_id, NOW(), p_created_by, p_currency_id, p_po_date, p_po_no,
34
v_destination_warehouse_id, v_source_warehouse_id, p_bill_type_id, p_bill_number, p_tds_amount
35
);
36
37
-- Insert into bill_details table
38
INSERT INTO public.bill_details (
39
id, bill_header_id, price, quantity, cgst_amount, discount, fees, igst_amount,
40
sgst_amount, taxable_amount, total_amount, serial_number, product_id
41
)
42
SELECT
43
gen_random_uuid(), p_id, (detail->>'price')::numeric, (detail->>'quantity')::numeric,
44
(detail->>'cgst_amount')::numeric, (detail->>'discount')::numeric, (detail->>'fees')::numeric,
45
(detail->>'igst_amount')::numeric, (detail->>'sgst_amount')::numeric, (detail->>'taxable_amount')::numeric,
46
(detail->>'total_amount')::numeric, (detail->>'serial_number')::integer, (detail->>'product_id')::uuid
47
FROM jsonb_array_elements(p_details) AS detail;
48
49
EXCEPTION
50
WHEN OTHERS THEN
51
RAISE EXCEPTION 'An error occurred: %', SQLERRM;
52
END;
53
$procedure$
|
|||||
| Procedure | update_draft_bill_next_status_1 | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE PROCEDURE public.update_draft_bill_next_status_1(IN p_company_id uuid, IN p_bill_ids uuid[], IN p_modified_by uuid)
2
LANGUAGE plpgsql
3
AS $procedure$
4
DECLARE
5
v_bill RECORD;
6
v_account_id uuid;
7
v_current_approval_level integer;
8
v_approval_level_required integer;
9
v_issue TEXT;
10
APPROVED_STATUS CONSTANT INTEGER := 3;
11
v_debug_next_status TEXT;
12
v_next_temp_id int;
13
BEGIN
14
RAISE NOTICE 'START update_draft_bill_next_status_1 for company %, user %', p_company_id, p_modified_by;
15
RAISE NOTICE 'Input bill IDs: %', p_bill_ids;
16
17
FOR v_bill IN
18
SELECT id, bill_status_id, debit_account_id, current_approval_level
19
FROM public.draft_bill_headers
20
WHERE id = ANY(p_bill_ids) AND bill_status_id = 2
21
LOOP
22
RAISE NOTICE 'Processing Bill ID: %', v_bill.id;
23
24
v_account_id := v_bill.debit_account_id;
25
v_current_approval_level := v_bill.current_approval_level;
26
27
SELECT approval_level_required
28
INTO v_approval_level_required
29
FROM public.bill_approval_level_view
30
WHERE company_id = p_company_id
31
AND status_id = v_bill.bill_status_id
32
AND (account_id = v_account_id OR account_id IS NULL)
33
ORDER BY account_id ASC
34
LIMIT 1;
35
36
RAISE NOTICE 'Bill % current level: %, required: %', v_bill.id, v_current_approval_level, v_approval_level_required;
37
38
IF v_approval_level_required IS NULL THEN
39
v_issue := 'Approval level not found';
40
INSERT INTO public.bill_approval_issue_logs(bill_id, issue, created_on_utc, created_by)
41
VALUES (v_bill.id, v_issue, now(), p_modified_by);
42
SELECT COALESCE(MAX(id), 0) + 1 INTO v_next_temp_id FROM temp_bill_next_status;
43
INSERT INTO temp_bill_next_status(id, bill_id, status, error)
44
VALUES (v_next_temp_id, v_bill.id, 'Not found', v_issue);
45
RAISE NOTICE 'Bill % skipped: approval level not found', v_bill.id;
46
CONTINUE;
47
END IF;
48
49
-- 🔔 Replace this with call to `check_bill_approval_permissions`
50
PERFORM public.check_bill_approval_permissions(
51
52
p_company_id,
53
v_bill.bill_status_id,
54
p_modified_by,
55
v_account_id,
56
v_current_approval_level + 1
57
);
58
59
IF NOT FOUND THEN
60
v_issue := 'User not authorized to approve at the next level';
61
INSERT INTO public.bill_approval_issue_logs(bill_id, issue, created_on_utc, created_by)
62
VALUES (v_bill.id, v_issue, now(), p_modified_by);
63
SELECT COALESCE(MAX(id), 0) + 1 INTO v_next_temp_id FROM temp_bill_next_status;
64
INSERT INTO temp_bill_next_status(id, bill_id, status, error)
65
VALUES (v_next_temp_id, v_bill.id, 'User not authorized', v_issue);
66
RAISE NOTICE 'Bill % skipped: user not authorized', v_bill.id;
67
CONTINUE;
68
END IF;
69
70
IF v_current_approval_level + 1 < v_approval_level_required THEN
71
v_debug_next_status := CONCAT('Level ', v_current_approval_level + 1);
72
ELSE
73
v_debug_next_status := 'Approved';
74
END IF;
75
76
RAISE NOTICE 'Bill % next computed status: %', v_bill.id, v_debug_next_status;
77
78
IF v_approval_level_required > (v_current_approval_level + 1) THEN
79
UPDATE public.draft_bill_headers
80
SET current_approval_level = v_current_approval_level + 1,
81
modified_by = p_modified_by,
82
modified_on_utc = now()
83
WHERE id = v_bill.id;
84
85
INSERT INTO public.bill_approval_logs(
86
bill_id, status_id, approved_by, approved_on, "comment",
87
created_on_utc, created_by, approval_level
88
)
89
VALUES (
90
v_bill.id, v_bill.bill_status_id, p_modified_by, now(),
91
CONCAT('Approved at level ', v_current_approval_level + 1),
92
now(), p_modified_by, v_current_approval_level + 1
93
);
94
95
RAISE NOTICE 'Bill % moved to next approval level: %', v_bill.id, v_current_approval_level + 1;
96
97
SELECT COALESCE(MAX(id), 0) + 1 INTO v_next_temp_id FROM temp_bill_next_status;
98
INSERT INTO temp_bill_next_status(id, bill_id, status, error)
99
VALUES (v_next_temp_id, v_bill.id, v_debug_next_status, NULL);
100
ELSE
101
UPDATE public.draft_bill_headers
102
SET bill_status_id = APPROVED_STATUS,
103
modified_by = p_modified_by,
104
modified_on_utc = now(),
105
current_approval_level = v_current_approval_level + 1
106
WHERE id = v_bill.id;
107
108
INSERT INTO public.bill_approval_logs(
109
bill_id, status_id, approved_by, approved_on, "comment",
110
created_on_utc, created_by, approval_level
111
)
112
VALUES (
113
v_bill.id, v_bill.bill_status_id, p_modified_by, now(),
114
'Final Approval',
115
now(), p_modified_by, v_current_approval_level + 1
116
);
117
118
CALL transfer_draft_to_bill(v_bill.id, p_modified_by);
119
120
RAISE NOTICE 'Bill % approved and transferred', v_bill.id;
121
122
SELECT COALESCE(MAX(id), 0) + 1 INTO v_next_temp_id FROM temp_bill_next_status;
123
INSERT INTO temp_bill_next_status(id, bill_id, status, error)
124
VALUES (v_next_temp_id, v_bill.id, v_debug_next_status, NULL);
125
END IF;
126
127
RAISE NOTICE '--- Finished processing Bill: % ---', v_bill.id;
128
END LOOP;
129
130
RAISE NOTICE 'END update_draft_bill_next_status';
131
END
132
$procedure$
|
|||||
| Procedure | insert_multiple_bills_by_excel1 | Match | ||||||
| Procedure | insert_draft_bill | Match | ||||||
| Procedure | insert_bill_by_excel | Match | ||||||
| Procedure | insert_bill | Match | ||||||
| Procedure | save_bill_and_details | Match | ||||||
| Procedure | create_draft_bill_header | Match | ||||||
| Procedure | create_schedule_bills | Missing in Target |
Source Script
Target Script
1
CREATE OR REPLACE PROCEDURE public.create_schedule_bills(IN p_company_id uuid, IN p_frequency_cycle text, IN p_starts_from date, IN p_end_date date, IN p_created_by uuid, IN p_bill_header_id uuid DEFAULT NULL::uuid, IN p_draft_bill_header_id uuid DEFAULT NULL::uuid, IN p_month_of_year integer DEFAULT NULL::integer, IN p_day_of_month integer DEFAULT NULL::integer, IN p_day_of_week integer DEFAULT NULL::integer, IN p_time_of_day interval DEFAULT '00:00:00'::interval)
2
LANGUAGE plpgsql
3
AS $procedure$
4
DECLARE
5
v_recurring_schedule_id uuid := gen_random_uuid(); -- New ID for recurring_bill_schedules
6
v_schedule_status text;
7
8
-- Normalized copies (treat all-zero GUID as NULL)
9
v_bill_header_id uuid := NULLIF(p_bill_header_id, '00000000-0000-0000-0000-000000000000'::uuid);
10
v_draft_bill_header_id uuid := NULLIF(p_draft_bill_header_id, '00000000-0000-0000-0000-000000000000'::uuid);
11
12
v_day_of_week integer := COALESCE(p_day_of_week, 0);
13
v_day_of_month integer := COALESCE(p_day_of_month, 0);
14
v_month_of_year integer := COALESCE(p_month_of_year, 0);
15
v_time_of_day interval := COALESCE(p_time_of_day, '00:00:00'::interval);
16
BEGIN
17
----------------------------------------------------------------
18
-- Validate: exactly ONE of bill_header_id / draft_bill_header_id
19
----------------------------------------------------------------
20
IF (v_bill_header_id IS NULL AND v_draft_bill_header_id IS NULL) THEN
21
RAISE EXCEPTION 'Either bill_header_id or draft_bill_header_id must be provided.';
22
ELSIF (v_bill_header_id IS NOT NULL AND v_draft_bill_header_id IS NOT NULL) THEN
23
RAISE EXCEPTION 'Provide only one of bill_header_id or draft_bill_header_id, not both.';
24
END IF;
25
26
----------------------------------------------------------------
27
-- Determine the initial schedule status
28
----------------------------------------------------------------
29
IF p_starts_from > CURRENT_DATE THEN
30
v_schedule_status := 'inactive';
31
ELSE
32
v_schedule_status := 'active';
33
END IF;
34
35
----------------------------------------------------------------
36
-- Insert into the recurring_bill_schedules table
37
----------------------------------------------------------------
38
INSERT INTO public.recurring_bill_schedules (
39
id,
40
bill_header_id,
41
draft_bill_header_id,
42
company_id,
43
frequency_cycle,
44
schedule_status,
45
starts_from,
46
end_date,
47
created_on_utc,
48
created_by,
49
is_deleted
50
)
51
VALUES (
52
v_recurring_schedule_id,
53
v_bill_header_id,
54
v_draft_bill_header_id,
55
p_company_id,
56
p_frequency_cycle,
57
v_schedule_status,
58
p_starts_from,
59
p_end_date,
60
NOW(),
61
p_created_by,
62
false
63
);
64
65
----------------------------------------------------------------
66
-- Insert into recurrence_bill_schedule_details
67
----------------------------------------------------------------
68
IF p_frequency_cycle = 'Daily' THEN
69
INSERT INTO public.recurrence_bill_schedule_details (
70
schedule_id,
71
frequency_cycle,
72
time_of_day,
73
is_deleted
74
)
75
VALUES (
76
v_recurring_schedule_id,
77
p_frequency_cycle,
78
v_time_of_day,
79
false
80
);
81
82
ELSIF p_frequency_cycle = 'Weekly' THEN
83
INSERT INTO public.recurrence_bill_schedule_details (
84
schedule_id,
85
frequency_cycle,
86
day_of_week,
87
time_of_day,
88
is_deleted
89
)
90
VALUES (
91
v_recurring_schedule_id,
92
p_frequency_cycle,
93
v_day_of_week,
94
v_time_of_day,
95
false
96
);
97
98
ELSIF p_frequency_cycle = 'Monthly' THEN
99
INSERT INTO public.recurrence_bill_schedule_details (
100
schedule_id,
101
frequency_cycle,
102
day_of_month,
103
time_of_day,
104
is_deleted
105
)
106
VALUES (
107
v_recurring_schedule_id,
108
p_frequency_cycle,
109
v_day_of_month,
110
v_time_of_day,
111
false
112
);
113
114
ELSIF p_frequency_cycle = 'Yearly' THEN
115
INSERT INTO public.recurrence_bill_schedule_details (
116
schedule_id,
117
frequency_cycle,
118
month_of_year,
119
day_of_month,
120
time_of_day,
121
is_deleted
122
)
123
VALUES (
124
v_recurring_schedule_id,
125
p_frequency_cycle,
126
v_month_of_year,
127
v_day_of_month,
128
v_time_of_day,
129
false
130
);
131
132
ELSE
133
RAISE NOTICE 'Invalid frequency cycle: %', p_frequency_cycle;
134
END IF;
135
136
RAISE NOTICE 'Recurring bill schedule created successfully with ID: %, Status: %',
137
v_recurring_schedule_id, v_schedule_status;
138
END;
139
$procedure$
|
|||||
| View | bill_details_with_payments | Missing in Target |
Source Script
Target Script
1
SELECT bh.bill_number,
2
bh.bill_date,
3
bd.product_id,
4
bd.quantity,
5
bd.price,
6
bd.total_amount AS bill_amount,
7
COALESCE(bp.paid_amount, (0)::numeric) AS paid_amount,
8
(bd.total_amount - COALESCE(bp.paid_amount, (0)::numeric)) AS remaining_amount,
9
bd.cgst_amount,
10
bd.sgst_amount,
11
bd.igst_amount,
12
bd.taxable_amount,
13
bd.discount,
14
bd.fees
15
FROM (((bill_details bd
16
JOIN bill_headers bh ON ((bd.bill_header_id = bh.id)))
17
LEFT JOIN bill_payment_details bpd ON ((bd.bill_header_id = bpd.bill_header_id)))
18
LEFT JOIN bill_payment_headers bp ON ((bpd.bill_payment_header_id = bp.id)))
19
WHERE ((bh.is_deleted = false) AND (bd.is_deleted = false));
|
|||||
| View | bill_approval_level_view | Match | ||||||
| View | bill_with_details | Missing in Target |
Source Script
Target Script
1
SELECT bh.bill_number,
2
bh.bill_date,
3
bd.product_id,
4
bd.quantity,
5
bd.price,
6
bd.total_amount AS bill_amount,
7
COALESCE(bp.paid_amount, (0)::numeric) AS paid_amount,
8
(bd.total_amount - COALESCE(bp.paid_amount, (0)::numeric)) AS remaining_amount,
9
bd.cgst_amount,
10
bd.sgst_amount,
11
bd.igst_amount,
12
bd.taxable_amount,
13
bd.discount,
14
bd.fees
15
FROM (((bill_details bd
16
JOIN bill_headers bh ON ((bd.bill_header_id = bh.id)))
17
LEFT JOIN bill_payment_details bpd ON ((bd.bill_header_id = bpd.bill_header_id)))
18
LEFT JOIN bill_payment_headers bp ON ((bpd.bill_payment_header_id = bp.id)))
19
WHERE ((bh.is_deleted = false) AND (bd.is_deleted = false));
|
|||||
| View | vw_bill_approval_permissions | Mismatch |
Source Script
Target Script
1
SELECT company_id,
1
SELECT COALESCE(baua.company_id, bauc.company_id) AS company_id,
2
status_id,
2
COALESCE(baua.status_id, bauc.status_id) AS status_id,
3
user_id,
3
COALESCE(baua.user_id, bauc.user_id) AS user_id,
4
approval_level,
4
COALESCE(baua.approval_level, bauc.approval_level) AS approval_level,
5
account_id,
6
account_approval_level,
7
account_status_id
8
FROM ( SELECT baua.company_id,
9
baua.status_id,
10
baua.user_id,
11
baua.approval_level,
12
baua.account_id,
5
baua.account_id,
13
baua.approval_level AS account_approval_level,
6
baua.approval_level AS account_approval_level,
14
baua.status_id AS account_status_id
7
baua.status_id AS account_status_id
15
FROM bill_approval_user_account baua
16
WHERE baua.is_deleted = false
17
UNION ALL
18
SELECT bauc.company_id,
19
bauc.status_id,
20
bauc.user_id,
21
bauc.approval_level,
22
NULL::uuid AS account_id,
23
NULL::integer AS account_approval_level,
24
bauc.status_id AS account_status_id
25
FROM bill_approval_user_company bauc
8
FROM bill_approval_user_company bauc
26
WHERE bauc.is_deleted = false AND NOT (EXISTS ( SELECT 1
9
LEFT JOIN bill_approval_user_account baua ON bauc.company_id = baua.company_id AND bauc.status_id = baua.status_id
27
FROM bill_approval_user_account baua
10
WHERE baua.company_id = bauc.company_id AND baua.status_id = bauc.status_id OR baua.company_id IS NULL AND bauc.is_deleted = false AND baua.is_deleted = false;
28
WHERE baua.company_id = bauc.company_id AND baua.status_id = bauc.status_id AND baua.user_id = bauc.user_id AND baua.is_deleted = false))) sub;
|
-- Table: vendor_advance_refunds
Exists in source, missing in target
-- Table: vendor_advance_settlement_ids
Exists in source, missing in target
-- Table: vendor_advance_settlements
Exists in source, missing in target
-- Table: vendor_advances
Exists in source, missing in target
-- Table: countries
-- 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:id:uuid:False:::False|COL:iso_alpha_code: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:name:text:False:::False|PK:|FK:created_by→users.id|FK:modified_by→users.id|IDX:btree:unique: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:id:uuid:False:::False|COL:iso_alpha_code: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:name:text:False:::False|PK:|FK:created_by→users.id|FK:modified_by→users.id|IDX:btree:unique:id:
-- SOURCE SCRIPT
CREATE TABLE "countries" ("id" uuid NOT NULL, "name" text NOT NULL, "iso_alpha_code" 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, PRIMARY KEY ("id"));
-- TARGET SCRIPT
CREATE TABLE "countries" ("id" uuid NOT NULL, "name" text NOT NULL, "iso_alpha_code" text NOT NULL, "created_by" uuid NOT NULL, "modified_by" uuid, "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, PRIMARY KEY ("id"));
-- Table: schedule_execution_logs
Exists in source, missing in target
-- Table: roles
-- StructuralDiff: Mismatch
-- SOURCE SIGNATURE
COL:description:character varying:True:200::False|COL:name:character varying:False:50::False|COL:permission_template_id:integer:True:::False|PK:|IDX:btree:unique:name:
-- TARGET SIGNATURE
COL:description:character varying:True:200::False|COL:name:character varying:False:50::False|PK:|IDX:btree:unique:name:
-- SOURCE SCRIPT
CREATE TABLE "roles" ("name" varchar(50) NOT NULL, "description" varchar(200), "permission_template_id" integer, PRIMARY KEY ("name"));
-- TARGET SCRIPT
CREATE TABLE "roles" ("name" varchar(50) NOT NULL, "description" varchar(200), PRIMARY KEY ("name"));
-- Columns: MissingInTarget
ALTER TABLE "roles" ADD COLUMN "permission_template_id" integer ;
-- Table: bill_approval_user_company
-- ForeignKeys: MissingInSource
ALTER TABLE "bill_approval_user_company" ADD CONSTRAINT "fk_bill_approval_user_company_company_id" FOREIGN KEY (company_id) REFERENCES companies(id);
-- Table: bill_approval_user_account
-- ForeignKeys: MissingInSource
ALTER TABLE "bill_approval_user_account" ADD CONSTRAINT "fk_bill_approval_users_account_company_id" FOREIGN KEY (company_id) REFERENCES companies(id);
-- Table: bill_payment_headers
-- StructuralDiff: Mismatch
-- SOURCE SIGNATURE
COL:advance_amount: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:credit_account_id:uuid:False::'00000000-0000-0000-0000-000000000000'::uuid:False|COL:debit_account_id:uuid:False::'00000000-0000-0000-0000-000000000000'::uuid:False|COL:deleted_on_utc:timestamp without time zone:True:::False|COL:description:text:False:::False|COL:general_ledger_id:uuid:True::'00000000-0000-0000-0000-000000000000'::uuid:False|COL:grand_total_amount:numeric: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::''::text:False|COL:modified_by:uuid:True:::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::''::text:False|COL:tds_amount:numeric:False::0.0:False|COL:transaction_id:bigint:True:::False|COL:vendor_id:uuid:False:::False|PK:|FK:company_id→companies.id|FK:created_by→users.id|FK:modified_by→users.id|FK:vendor_id→vendors.id|IDX:btree:unique:id:
-- TARGET SIGNATURE
COL:advance_amount: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:credit_account_id:uuid:False::'00000000-0000-0000-0000-000000000000'::uuid:False|COL:debit_account_id:uuid:False::'00000000-0000-0000-0000-000000000000'::uuid:False|COL:deleted_on_utc:timestamp without time zone:True:::False|COL:description:text:False:::False|COL:general_ledger_id:uuid:True::'00000000-0000-0000-0000-000000000000'::uuid:False|COL:grand_total_amount:numeric:False:::False|COL:id:uuid:False:::False|COL:is_deleted:boolean:False::false:False|COL:mode_of_payment:text:False::''::text:False|COL:modified_by:uuid:True:::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::''::text:False|COL:tds_amount:numeric:False::0.0:False|COL:vendor_id:uuid:False:::False|PK:|FK:company_id→companies.id|FK:created_by→users.id|FK:modified_by→users.id|FK:vendor_id→vendors.id|IDX:btree:unique:id:
-- SOURCE SCRIPT
CREATE TABLE "bill_payment_headers" ("id" uuid NOT NULL, "vendor_id" uuid NOT NULL, "company_id" uuid DEFAULT '00000000-0000-0000-0000-000000000000'::uuid NOT NULL, "general_ledger_id" uuid DEFAULT '00000000-0000-0000-0000-000000000000'::uuid, "credit_account_id" uuid DEFAULT '00000000-0000-0000-0000-000000000000'::uuid NOT NULL, "debit_account_id" uuid DEFAULT '00000000-0000-0000-0000-000000000000'::uuid NOT NULL, "paid_date" timestamp without time zone NOT NULL, "paid_amount" numeric(,) NOT NULL, "grand_total_amount" numeric(,) NOT NULL, "advance_amount" numeric(,) NOT NULL, "description" text NOT NULL, "mode_of_payment" text DEFAULT ''::text NOT NULL, "reference" text DEFAULT ''::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, "payment_number" text DEFAULT ''::text NOT NULL, "tds_amount" numeric(,) DEFAULT 0.0 NOT NULL, "is_posted" boolean DEFAULT false NOT NULL, "transaction_id" bigint, PRIMARY KEY ("id"));
-- TARGET SCRIPT
CREATE TABLE "bill_payment_headers" ("id" uuid NOT NULL, "vendor_id" uuid NOT NULL, "paid_date" timestamp without time zone NOT NULL, "paid_amount" numeric(,) NOT NULL, "grand_total_amount" numeric(,) NOT NULL, "advance_amount" numeric(,) NOT NULL, "description" 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, "company_id" uuid DEFAULT '00000000-0000-0000-0000-000000000000'::uuid NOT NULL, "mode_of_payment" text DEFAULT ''::text NOT NULL, "reference" text DEFAULT ''::text NOT NULL, "general_ledger_id" uuid DEFAULT '00000000-0000-0000-0000-000000000000'::uuid, "credit_account_id" uuid DEFAULT '00000000-0000-0000-0000-000000000000'::uuid NOT NULL, "debit_account_id" uuid DEFAULT '00000000-0000-0000-0000-000000000000'::uuid NOT NULL, "modified_by" uuid, "payment_number" text DEFAULT ''::text NOT NULL, "tds_amount" numeric(,) DEFAULT 0.0 NOT NULL, PRIMARY KEY ("id"));
-- Columns: MissingInTarget
ALTER TABLE "bill_payment_headers" ADD COLUMN "is_posted" boolean NOT NULL;
-- Columns: MissingInTarget
ALTER TABLE "bill_payment_headers" ADD COLUMN "transaction_id" bigint ;
-- Table: bill_headers
-- StructuralDiff: Mismatch
-- SOURCE SIGNATURE
COL:bill_date:timestamp without time zone:False:::False|COL:bill_number:text:True:::False|COL:bill_status_id:integer:False:::False|COL:bill_type_id:integer:False::0:False|COL:bill_voucher:text:False:::False|COL:cgst_amount:numeric:False:::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:currency_id:integer:False::0:False|COL:current_approval_level:integer:False::0:False|COL:debit_account_id:uuid:False:::False|COL:deleted_on_utc:timestamp without time zone:True:::False|COL:destination_warehouse_id:uuid:True::'00000000-0000-0000-0000-000000000000'::uuid:False|COL:discount:numeric:False:::False|COL:due_date:timestamp without time zone:False:::False|COL:fees:numeric:True:::False|COL:id:uuid:False:::False|COL:igst_amount:numeric:False:::False|COL:is_closed:boolean:False:::False|COL:is_deleted:boolean:False::false:False|COL:is_editable:boolean:True:::False|COL:is_gate_pass_created:boolean:False::false:False|COL:is_posted:boolean:False::false:False|COL:is_tds_paid:boolean:False:::False|COL:is_vendor_paid:boolean:False:::False|COL:modified_by:uuid:True:::False|COL:modified_on_utc:timestamp without time zone:True:::False|COL:note:text:False:::False|COL:payment_status_id:integer:False::1:False|COL:payment_term:integer:True:::False|COL:po_date:timestamp without time zone:True::'0001-01-01 00:00:00'::timestamp without time zone:False|COL:po_no:text:True:::False|COL:round_off:numeric:False:::False|COL:round_off_tds:numeric:False::0.0:False|COL:sgst_amount:numeric:False:::False|COL:source_warehouse_id:uuid:True::'00000000-0000-0000-0000-000000000000'::uuid:False|COL:taxable_amount:numeric:False:::False|COL:tds_amount:numeric:True:::False|COL:tds_paid_amount:numeric:False::0.0:False|COL:tds_payment_number:text:True:::False|COL:tds_status_id:integer:False::1:False|COL:total_amount:numeric:False:::False|COL:total_paid_amount:numeric:False:::False|COL:transaction_id:bigint:True:::False|COL:vendor_id:uuid:False:::False|COL:vendor_net_amount:numeric:False:::False|COL:vendor_paid_amount:numeric:True::0:False|PK:|FK:bill_status_id→bill_statuses.id|FK:bill_type_id→bill_types.id|FK:company_id→companies.id|FK:created_by→users.id|FK:modified_by→users.id|FK:source_warehouse_id→warehouses.id|FK:vendor_id→vendors.id|IDX:btree:unique:id:
-- TARGET SIGNATURE
COL:bill_date:timestamp without time zone:False:::False|COL:bill_number:text:True:::False|COL:bill_status_id:integer:False:::False|COL:bill_type_id:integer:False::0:False|COL:bill_voucher:text:False:::False|COL:cgst_amount:numeric:False:::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:currency_id:integer:False::0:False|COL:current_approval_level:integer:False::0:False|COL:debit_account_id:uuid:False:::False|COL:deleted_on_utc:timestamp without time zone:True:::False|COL:destination_warehouse_id:uuid:True::'00000000-0000-0000-0000-000000000000'::uuid:False|COL:discount:numeric:False:::False|COL:due_date:timestamp without time zone:False:::False|COL:fees:numeric:True:::False|COL:id:uuid:False:::False|COL:igst_amount:numeric:False:::False|COL:is_deleted:boolean:False::false:False|COL:is_editable:boolean:True:::False|COL:is_gate_pass_created:boolean:False::false:False|COL:modified_by:uuid:True:::False|COL:modified_on_utc:timestamp without time zone:True:::False|COL:note:text:False:::False|COL:payment_term:integer:True:::False|COL:po_date:timestamp without time zone:True::'0001-01-01 00:00:00'::timestamp without time zone:False|COL:po_no:text:True:::False|COL:round_off:numeric:False:::False|COL:setteled_amount:numeric:True::0:False|COL:sgst_amount:numeric:False:::False|COL:source_warehouse_id:uuid:True::'00000000-0000-0000-0000-000000000000'::uuid:False|COL:taxable_amount:numeric:False:::False|COL:tds_amount:numeric:True:::False|COL:total_amount:numeric:False:::False|COL:vendor_id:uuid:False:::False|PK:|FK:bill_status_id→bill_statuses.id|FK:bill_type_id→bill_types.id|FK:company_id→companies.id|FK:created_by→users.id|FK:modified_by→users.id|FK:source_warehouse_id→warehouses.id|FK:vendor_id→vendors.id|IDX:btree:unique:id:
-- SOURCE SCRIPT
CREATE TABLE "bill_headers" ("id" uuid NOT NULL, "company_id" uuid NOT NULL, "vendor_id" uuid NOT NULL, "discount" numeric(,) NOT NULL, "bill_number" text, "bill_date" timestamp without time zone NOT NULL, "payment_term" integer, "bill_status_id" integer NOT NULL, "due_date" timestamp without time zone NOT NULL, "total_amount" numeric(,) NOT NULL, "taxable_amount" numeric(,) NOT NULL, "fees" numeric(,), "sgst_amount" numeric(,) NOT NULL, "cgst_amount" numeric(,) NOT NULL, "igst_amount" numeric(,) NOT NULL, "note" text NOT NULL, "round_off" numeric(,) NOT NULL, "debit_account_id" uuid NOT NULL, "credit_account_id" uuid NOT NULL, "currency_id" integer DEFAULT 0 NOT NULL, "po_date" timestamp without time zone DEFAULT '0001-01-01 00:00:00'::timestamp without time zone, "po_no" text, "destination_warehouse_id" uuid DEFAULT '00000000-0000-0000-0000-000000000000'::uuid, "source_warehouse_id" uuid DEFAULT '00000000-0000-0000-0000-000000000000'::uuid, "bill_type_id" integer DEFAULT 0 NOT NULL, "created_on_utc" timestamp without time zone NOT NULL, "created_by" uuid NOT NULL, "modified_by" uuid, "modified_on_utc" timestamp without time zone, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "bill_voucher" text NOT NULL, "is_editable" boolean, "is_gate_pass_created" boolean DEFAULT false NOT NULL, "tds_amount" numeric(,), "current_approval_level" integer DEFAULT 0 NOT NULL, "vendor_paid_amount" numeric(18,2) DEFAULT 0, "tds_payment_number" text, "tds_paid_amount" numeric(18,2) DEFAULT 0.0 NOT NULL, "is_tds_paid" boolean NOT NULL, "is_closed" boolean NOT NULL, "is_vendor_paid" boolean NOT NULL, "total_paid_amount" numeric(18,2) NOT NULL, "vendor_net_amount" numeric(18,2) NOT NULL, "tds_status_id" integer DEFAULT 1 NOT NULL, "payment_status_id" integer DEFAULT 1 NOT NULL, "round_off_tds" numeric(18,2) DEFAULT 0.0 NOT NULL, "is_posted" boolean DEFAULT false NOT NULL, "transaction_id" bigint, PRIMARY KEY ("id"));
-- TARGET SCRIPT
CREATE TABLE "bill_headers" ("id" uuid NOT NULL, "company_id" uuid NOT NULL, "vendor_id" uuid NOT NULL, "discount" numeric(,) NOT NULL, "bill_voucher" text NOT NULL, "bill_date" timestamp without time zone NOT NULL, "payment_term" integer, "bill_status_id" integer NOT NULL, "due_date" timestamp without time zone NOT NULL, "total_amount" numeric(,) NOT NULL, "taxable_amount" numeric(,) NOT NULL, "fees" numeric(,), "sgst_amount" numeric(,) NOT NULL, "cgst_amount" numeric(,) NOT NULL, "igst_amount" numeric(,) NOT NULL, "note" text NOT NULL, "round_off" numeric(,) NOT NULL, "setteled_amount" numeric(,) DEFAULT 0, "debit_account_id" uuid NOT NULL, "credit_account_id" uuid NOT NULL, "created_on_utc" timestamp without time zone NOT NULL, "created_by" uuid NOT NULL, "modified_by" uuid, "modified_on_utc" timestamp without time zone, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "currency_id" integer DEFAULT 0 NOT NULL, "po_date" timestamp without time zone DEFAULT '0001-01-01 00:00:00'::timestamp without time zone, "po_no" text, "destination_warehouse_id" uuid DEFAULT '00000000-0000-0000-0000-000000000000'::uuid, "source_warehouse_id" uuid DEFAULT '00000000-0000-0000-0000-000000000000'::uuid, "bill_type_id" integer DEFAULT 0 NOT NULL, "is_editable" boolean, "bill_number" text, "is_gate_pass_created" boolean DEFAULT false NOT NULL, "tds_amount" numeric(,), "current_approval_level" integer DEFAULT 0 NOT NULL, PRIMARY KEY ("id"));
-- Columns: MissingInTarget
ALTER TABLE "bill_headers" ADD COLUMN "vendor_paid_amount" numeric ;
-- Columns: MissingInTarget
ALTER TABLE "bill_headers" ADD COLUMN "tds_payment_number" text ;
-- Columns: MissingInTarget
ALTER TABLE "bill_headers" ADD COLUMN "tds_paid_amount" numeric NOT NULL;
-- Columns: MissingInTarget
ALTER TABLE "bill_headers" ADD COLUMN "is_tds_paid" boolean NOT NULL;
-- Columns: MissingInTarget
ALTER TABLE "bill_headers" ADD COLUMN "is_closed" boolean NOT NULL;
-- Columns: MissingInTarget
ALTER TABLE "bill_headers" ADD COLUMN "is_vendor_paid" boolean NOT NULL;
-- Columns: MissingInTarget
ALTER TABLE "bill_headers" ADD COLUMN "total_paid_amount" numeric NOT NULL;
-- Columns: MissingInTarget
ALTER TABLE "bill_headers" ADD COLUMN "vendor_net_amount" numeric NOT NULL;
-- Columns: MissingInTarget
ALTER TABLE "bill_headers" ADD COLUMN "tds_status_id" integer NOT NULL;
-- Columns: MissingInTarget
ALTER TABLE "bill_headers" ADD COLUMN "payment_status_id" integer NOT NULL;
-- Columns: MissingInTarget
ALTER TABLE "bill_headers" ADD COLUMN "round_off_tds" numeric NOT NULL;
-- Columns: MissingInTarget
ALTER TABLE "bill_headers" ADD COLUMN "is_posted" boolean NOT NULL;
-- Columns: MissingInTarget
ALTER TABLE "bill_headers" ADD COLUMN "transaction_id" bigint ;
-- Table: bill_approval_issue_logs
-- ForeignKeys: MissingInTarget
ALTER TABLE "bill_approval_issue_logs" ADD CONSTRAINT "fk_bill_approval_issue_logs_created_by" FOREIGN KEY (created_by) REFERENCES users(id);
-- Table: __EFMigrationsHistory
-- StructuralDiff: Mismatch
-- SOURCE SIGNATURE
COL:migration_id:character varying:False:150::False|COL:product_version:character varying:False:32::False
-- TARGET SIGNATURE
COL:migration_id:character varying:False:150::False|COL:product_version:character varying:False:32::False|PK:|IDX:btree:unique:migration_id:
-- SOURCE SCRIPT
CREATE TABLE "__EFMigrationsHistory" ("migration_id" varchar(150) NOT NULL, "product_version" varchar(32) NOT NULL);
-- TARGET SCRIPT
CREATE TABLE "__EFMigrationsHistory" ("migration_id" varchar(150) NOT NULL, "product_version" varchar(32) NOT NULL, PRIMARY KEY ("migration_id"));
-- Indexes: MissingInSource
-- Optionally drop: DROP INDEX IF EXISTS "pk___ef_migrations_history";
-- Table: draft_bill_headers
-- StructuralDiff: Mismatch
-- SOURCE SIGNATURE
COL:bill_date:timestamp without time zone:False:::False|COL:bill_number:text:True:::False|COL:bill_status_id:integer:False:::False|COL:bill_type_id:integer:False:::False|COL:bill_voucher:text:False:::False|COL:cgst_amount:numeric:False:::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:currency_id:integer:False:::False|COL:current_approval_level:integer:False::0:False|COL:debit_account_id:uuid:False:::False|COL:deleted_on_utc:timestamp without time zone:True:::False|COL:destination_warehouse_id:uuid:True:::False|COL:discount:numeric:False:::False|COL:due_date:timestamp without time zone:False:::False|COL:fees:numeric:True:::False|COL:id:uuid:False:::False|COL:igst_amount:numeric:False:::False|COL:is_closed:boolean:False:::False|COL:is_deleted:boolean:False::false:False|COL:is_editable:boolean:True:::False|COL:is_gate_pass_created:boolean:False::false:False|COL:is_tds_paid:boolean:False:::False|COL:is_vendor_paid:boolean:False:::False|COL:modified_by:uuid:True:::False|COL:modified_on_utc:timestamp without time zone:True:::False|COL:note:text:False:::False|COL:payment_status_id:integer:False::1:False|COL:payment_term:integer:True:::False|COL:po_date:timestamp without time zone:True:::False|COL:po_no:text:True:::False|COL:round_off:numeric:False:::False|COL:round_off_tds:numeric:False::0.0:False|COL:sgst_amount:numeric:False:::False|COL:source_warehouse_id:uuid:True:::False|COL:taxable_amount:numeric:False:::False|COL:tds_amount:numeric:True:::False|COL:tds_paid_amount:numeric:False::0.0:False|COL:tds_status_id:integer:False::1:False|COL:total_amount:numeric:False:::False|COL:total_paid_amount:numeric:False:::False|COL:vendor_id:uuid:False:::False|COL:vendor_net_amount:numeric:False:::False|COL:vendor_paid_amount:numeric:True::0:False|PK:|FK:bill_status_id→bill_statuses.id|FK:bill_type_id→bill_types.id|FK:company_id→companies.id|FK:created_by→users.id|FK:modified_by→users.id|FK:vendor_id→vendors.id|IDX:btree:unique:id:
-- TARGET SIGNATURE
COL:bill_date:timestamp without time zone:False:::False|COL:bill_number:text:True:::False|COL:bill_status_id:integer:False:::False|COL:bill_type_id:integer:False:::False|COL:bill_voucher:text:False:::False|COL:cgst_amount:numeric:False:::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:currency_id:integer:False:::False|COL:current_approval_level:integer:False::0:False|COL:debit_account_id:uuid:False:::False|COL:deleted_on_utc:timestamp without time zone:True:::False|COL:destination_warehouse_id:uuid:True:::False|COL:discount:numeric:False:::False|COL:due_date:timestamp without time zone:False:::False|COL:fees:numeric:True:::False|COL:id:uuid:False:::False|COL:igst_amount:numeric:False:::False|COL:is_deleted:boolean:False::false:False|COL:is_editable:boolean:True:::False|COL:is_gate_pass_created:boolean:False::false:False|COL:modified_by:uuid:True:::False|COL:modified_on_utc:timestamp without time zone:True:::False|COL:note:text:False:::False|COL:payment_term:integer:True:::False|COL:po_date:timestamp without time zone:True:::False|COL:po_no:text:True:::False|COL:round_off:numeric:False:::False|COL:setteled_amount:numeric:True::0:False|COL:sgst_amount:numeric:False:::False|COL:source_warehouse_id:uuid:True:::False|COL:taxable_amount:numeric:False:::False|COL:tds_amount:numeric:True:::False|COL:total_amount:numeric:False:::False|COL:vendor_id:uuid:False:::False|PK:|FK:bill_status_id→bill_statuses.id|FK:bill_type_id→bill_types.id|FK:company_id→companies.id|FK:created_by→users.id|FK:modified_by→users.id|FK:vendor_id→vendors.id|IDX:btree:unique:id:
-- SOURCE SCRIPT
CREATE TABLE "draft_bill_headers" ("id" uuid NOT NULL, "company_id" uuid NOT NULL, "vendor_id" uuid NOT NULL, "discount" numeric(,) NOT NULL, "bill_number" text, "currency_id" integer NOT NULL, "bill_date" timestamp without time zone NOT NULL, "payment_term" integer, "bill_status_id" integer NOT NULL, "due_date" timestamp without time zone NOT NULL, "total_amount" numeric(18,2) NOT NULL, "taxable_amount" numeric(18,2) NOT NULL, "fees" numeric(18,2), "sgst_amount" numeric(18,2) NOT NULL, "cgst_amount" numeric(18,2) NOT NULL, "igst_amount" numeric(18,2) NOT NULL, "note" text NOT NULL, "round_off" numeric(18,2) NOT NULL, "debit_account_id" uuid NOT NULL, "credit_account_id" uuid NOT NULL, "po_no" text, "po_date" timestamp without time zone, "source_warehouse_id" uuid, "destination_warehouse_id" uuid, "bill_type_id" integer NOT NULL, "created_on_utc" timestamp without time zone NOT NULL, "created_by" uuid NOT NULL, "modified_by" uuid, "modified_on_utc" timestamp without time zone, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "is_editable" boolean, "bill_voucher" text NOT NULL, "is_gate_pass_created" boolean DEFAULT false NOT NULL, "tds_amount" numeric(18,2), "current_approval_level" integer DEFAULT 0 NOT NULL, "vendor_paid_amount" numeric(18,2) DEFAULT 0, "tds_paid_amount" numeric(18,2) DEFAULT 0.0 NOT NULL, "is_closed" boolean NOT NULL, "is_tds_paid" boolean NOT NULL, "is_vendor_paid" boolean NOT NULL, "total_paid_amount" numeric(18,2) NOT NULL, "vendor_net_amount" numeric(18,2) NOT NULL, "payment_status_id" integer DEFAULT 1 NOT NULL, "tds_status_id" integer DEFAULT 1 NOT NULL, "round_off_tds" numeric(18,2) DEFAULT 0.0 NOT NULL, PRIMARY KEY ("id"));
-- TARGET SCRIPT
CREATE TABLE "draft_bill_headers" ("id" uuid NOT NULL, "company_id" uuid NOT NULL, "vendor_id" uuid NOT NULL, "discount" numeric(,) NOT NULL, "bill_voucher" text NOT NULL, "currency_id" integer NOT NULL, "bill_date" timestamp without time zone NOT NULL, "payment_term" integer, "bill_status_id" integer NOT NULL, "due_date" timestamp without time zone NOT NULL, "total_amount" numeric(,) NOT NULL, "taxable_amount" numeric(,) NOT NULL, "fees" numeric(,), "sgst_amount" numeric(,) NOT NULL, "cgst_amount" numeric(,) NOT NULL, "igst_amount" numeric(,) NOT NULL, "note" text NOT NULL, "round_off" numeric(,) NOT NULL, "setteled_amount" numeric(,) DEFAULT 0, "debit_account_id" uuid NOT NULL, "credit_account_id" uuid NOT NULL, "po_no" text, "po_date" timestamp without time zone, "source_warehouse_id" uuid, "destination_warehouse_id" uuid, "bill_type_id" integer NOT NULL, "created_on_utc" timestamp without time zone NOT NULL, "created_by" uuid NOT NULL, "modified_by" uuid, "modified_on_utc" timestamp without time zone, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "is_editable" boolean, "bill_number" text, "is_gate_pass_created" boolean DEFAULT false NOT NULL, "tds_amount" numeric(,), "current_approval_level" integer DEFAULT 0 NOT NULL, PRIMARY KEY ("id"));
-- Columns: MissingInTarget
ALTER TABLE "draft_bill_headers" ADD COLUMN "vendor_paid_amount" numeric ;
-- Columns: MissingInTarget
ALTER TABLE "draft_bill_headers" ADD COLUMN "tds_paid_amount" numeric NOT NULL;
-- Columns: MissingInTarget
ALTER TABLE "draft_bill_headers" ADD COLUMN "is_closed" boolean NOT NULL;
-- Columns: MissingInTarget
ALTER TABLE "draft_bill_headers" ADD COLUMN "is_tds_paid" boolean NOT NULL;
-- Columns: MissingInTarget
ALTER TABLE "draft_bill_headers" ADD COLUMN "is_vendor_paid" boolean NOT NULL;
-- Columns: MissingInTarget
ALTER TABLE "draft_bill_headers" ADD COLUMN "total_paid_amount" numeric NOT NULL;
-- Columns: MissingInTarget
ALTER TABLE "draft_bill_headers" ADD COLUMN "vendor_net_amount" numeric NOT NULL;
-- Columns: MissingInTarget
ALTER TABLE "draft_bill_headers" ADD COLUMN "payment_status_id" integer NOT NULL;
-- Columns: MissingInTarget
ALTER TABLE "draft_bill_headers" ADD COLUMN "tds_status_id" integer NOT NULL;
-- Columns: MissingInTarget
ALTER TABLE "draft_bill_headers" ADD COLUMN "round_off_tds" numeric NOT NULL;
-- Table: payment_statuses
Exists in source, missing in target
-- Table: vendor_advance_ids
Exists in source, missing in target
-- Table: tds_statuses
Exists in source, missing in target
-- Table: users
-- 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:email:character varying:False:256::False|COL:first_name:character varying:False:100::False|COL:id:uuid:False:::False|COL:is_deleted:boolean:False::false:False|COL:last_name:character varying:False:100::False|COL:modified_by:uuid:True:::False|COL:modified_on_utc:timestamp without time zone:True:::False|COL:password_hash:text:True:::False|COL:phone_number:character varying:False:15:''::character varying:False|PK:|IDX:btree:unique:email,phone_number:|IDX:btree:unique:email:|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:email:character varying:False:256::False|COL:first_name:character varying:False:100::False|COL:id:uuid:False:::False|COL:is_deleted:boolean:False::false:False|COL:last_name:character varying:False:100::False|COL:modified_by:uuid:True:::False|COL:modified_on_utc:timestamp without time zone:True:::False|COL:password_hash:text:True:::False|COL:phone_number:character varying:False:15:''::character varying:False|PK:|IDX:btree:unique:email,phone_number:|IDX:btree:unique:id:
-- SOURCE SCRIPT
CREATE TABLE "users" ("id" uuid NOT NULL, "first_name" varchar(100) NOT NULL, "last_name" varchar(100) NOT NULL, "email" varchar(256) NOT NULL, "phone_number" varchar(15) DEFAULT ''::character varying NOT NULL, "password_hash" 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, "company_id" uuid DEFAULT '00000000-0000-0000-0000-000000000000'::uuid NOT NULL, PRIMARY KEY ("id"));
-- TARGET SCRIPT
CREATE TABLE "users" ("id" uuid NOT NULL, "first_name" varchar(100) NOT NULL, "last_name" varchar(100) NOT NULL, "email" varchar(256) 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, "password_hash" text, "phone_number" varchar(15) DEFAULT ''::character varying NOT NULL, "company_id" uuid DEFAULT '00000000-0000-0000-0000-000000000000'::uuid NOT NULL, PRIMARY KEY ("id"));
-- Indexes: MissingInTarget
CREATE UNIQUE INDEX uq_users_email ON public.users USING btree (email)
-- Table: vendors
-- StructuralDiff: Mismatch
-- SOURCE SIGNATURE
COL:billing_address_id:uuid:True::'00000000-0000-0000-0000-000000000000'::uuid:False|COL:category_id:integer:True:::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:default_account_id:uuid:True::'00000000-0000-0000-0000-000000000000'::uuid:False|COL:deleted_on_utc:timestamp without time zone:True:::False|COL:description:text:True:::False|COL:gstin:text:False::''::text:False|COL:has_gstin:boolean:True::false:False|COL:id:uuid:False:::False|COL:interest_percentage:numeric:False::0.0:False|COL:is_deleted:boolean:False::false:False|COL:is_direct_payment_activated:boolean:False::false:False|COL:is_non_work:boolean:False::false:False|COL:is_tds_vendor:boolean:False::false:False|COL:modified_by:uuid:True:::False|COL:modified_on_utc:timestamp without time zone:True:::False|COL:name:character varying:False:100::False|COL:outstanding_limit:numeric:False::0.0:False|COL:pan:text:True:::False|COL:proprietor_name:text:True:::False|COL:shipping_address_id:uuid:True::'00000000-0000-0000-0000-000000000000'::uuid:False|COL:short_name:text:True:::False|COL:tan:text:True:::False|PK:|IDX:btree:unique:id:
-- TARGET SIGNATURE
COL:billing_address_id:uuid:True::'00000000-0000-0000-0000-000000000000'::uuid:False|COL:category_id:integer:True:::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:default_account_id:uuid:True::'00000000-0000-0000-0000-000000000000'::uuid:False|COL:deleted_on_utc:timestamp without time zone:True:::False|COL:description:text:True:::False|COL:gstin:text:False::''::text:False|COL:has_gstin:boolean:True::false:False|COL:id:uuid:False:::False|COL:interest_percentage:numeric:False::0.0:False|COL:is_deleted:boolean:False::false:False|COL:is_direct_payment_activated:boolean:False::false:False|COL:is_non_work:boolean:False::false:False|COL:modified_by:uuid:True:::False|COL:modified_on_utc:timestamp without time zone:True:::False|COL:name:character varying:False:100::False|COL:outstanding_limit:numeric:False::0.0:False|COL:pan:text:True:::False|COL:proprietor_name:text:True:::False|COL:shipping_address_id:uuid:True::'00000000-0000-0000-0000-000000000000'::uuid:False|COL:short_name:text:True:::False|COL:tan:text:True:::False|PK:|IDX:btree:unique:id:
-- SOURCE SCRIPT
CREATE TABLE "vendors" ("id" uuid NOT NULL, "name" varchar(100) NOT NULL, "company_id" uuid DEFAULT '00000000-0000-0000-0000-000000000000'::uuid NOT NULL, "billing_address_id" uuid DEFAULT '00000000-0000-0000-0000-000000000000'::uuid, "shipping_address_id" uuid DEFAULT '00000000-0000-0000-0000-000000000000'::uuid, "gstin" text DEFAULT ''::text NOT NULL, "pan" text, "tan" text, "short_name" text, "proprietor_name" text, "interest_percentage" numeric(,) DEFAULT 0.0 NOT NULL, "is_non_work" boolean DEFAULT false NOT NULL, "outstanding_limit" numeric(,) DEFAULT 0.0 NOT NULL, "has_gstin" boolean DEFAULT false, "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, "is_direct_payment_activated" boolean DEFAULT false NOT NULL, "category_id" integer, "description" text, "default_account_id" uuid DEFAULT '00000000-0000-0000-0000-000000000000'::uuid, "is_tds_vendor" boolean DEFAULT false NOT NULL, PRIMARY KEY ("id"));
-- TARGET SCRIPT
CREATE TABLE "vendors" ("id" uuid NOT NULL, "name" varchar(100) 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, "shipping_address_id" uuid DEFAULT '00000000-0000-0000-0000-000000000000'::uuid, "gstin" text DEFAULT ''::text NOT NULL, "interest_percentage" numeric(,) DEFAULT 0.0 NOT NULL, "is_non_work" boolean DEFAULT false NOT NULL, "outstanding_limit" numeric(,) DEFAULT 0.0 NOT NULL, "pan" text, "proprietor_name" text, "short_name" text, "tan" text, "billing_address_id" uuid DEFAULT '00000000-0000-0000-0000-000000000000'::uuid, "has_gstin" boolean DEFAULT false, "is_direct_payment_activated" boolean DEFAULT false NOT NULL, "category_id" integer, "description" text, "default_account_id" uuid DEFAULT '00000000-0000-0000-0000-000000000000'::uuid, PRIMARY KEY ("id"));
-- Columns: MissingInTarget
ALTER TABLE "vendors" ADD COLUMN "is_tds_vendor" boolean NOT NULL;
-- Table: recurring_bill_schedules
-- StructuralDiff: Mismatch
-- SOURCE SIGNATURE
COL:bill_header_id:uuid: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:deleted_on_utc:timestamp without time zone:True:::False|COL:draft_bill_header_id:uuid:True:::False|COL:end_date:timestamp without time zone:False:::False|COL:frequency_cycle:text: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:schedule_status:text:False:::False|COL:starts_from:timestamp without time zone:False:::False|PK:|IDX:btree:unique:id:
-- TARGET SIGNATURE
COL:bill_header_id:uuid:False:::False|COL:company_id:uuid: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:end_date:timestamp without time zone:False:::False|COL:frequency_cycle:text: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:schedule_status:text:False:::False|COL:starts_from:timestamp without time zone:False:::False|PK:|IDX:btree:unique:id:
-- SOURCE SCRIPT
CREATE TABLE "recurring_bill_schedules" ("id" uuid NOT NULL, "company_id" uuid NOT NULL, "frequency_cycle" text NOT NULL, "schedule_status" text NOT NULL, "starts_from" timestamp without time zone NOT NULL, "end_date" timestamp without time zone NOT NULL, "created_on_utc" timestamp without time zone NOT NULL, "created_by" uuid NOT NULL, "modified_by" uuid, "modified_on_utc" timestamp without time zone, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, "bill_header_id" uuid, "draft_bill_header_id" uuid, PRIMARY KEY ("id"));
-- TARGET SCRIPT
CREATE TABLE "recurring_bill_schedules" ("id" uuid NOT NULL, "bill_header_id" uuid NOT NULL, "company_id" uuid NOT NULL, "frequency_cycle" text NOT NULL, "schedule_status" text NOT NULL, "starts_from" timestamp without time zone NOT NULL, "end_date" timestamp without time zone NOT NULL, "created_on_utc" timestamp without time zone NOT NULL, "created_by" uuid NOT NULL, "modified_by" uuid, "modified_on_utc" timestamp without time zone, "deleted_on_utc" timestamp without time zone, "is_deleted" boolean DEFAULT false NOT NULL, PRIMARY KEY ("id"));
-- Columns: MissingInTarget
ALTER TABLE "recurring_bill_schedules" ADD COLUMN "draft_bill_header_id" uuid ;
-- Function: checkin_service_provider
CREATE OR REPLACE FUNCTION public.checkin_service_provider(p_apartment_id uuid, p_pin text, p_created_by uuid)
RETURNS integer
LANGUAGE plpgsql
AS $function$
DECLARE
v_service_provider_id int;
v_log_id int;
BEGIN
-- Step 1: Validate service provider by apartmentId and pin
SELECT id INTO v_service_provider_id
FROM public.service_providers
WHERE apartment_id = p_apartment_id
AND pin = p_pin
AND is_deleted = false
LIMIT 1;
IF v_service_provider_id IS NULL THEN
RAISE EXCEPTION 'Service provider not found for apartment % with pin %', p_apartment_id, p_pin
USING ERRCODE = 'no_data_found';
END IF;
-- Step 2: Insert service provider log (CheckedIn)
INSERT INTO public.service_provider_logs (
service_provider_id,
current_status_id,
entry_time,
created_on_utc,
created_by
)
VALUES (
v_service_provider_id,
1, -- CheckedIn
now(),
now(),
p_created_by
)
RETURNING id INTO v_log_id;
RETURN v_log_id;
END;
$function$
-- Function: create_vendor_advance_settlements
CREATE OR REPLACE FUNCTION public.create_vendor_advance_settlements(p_company_id uuid, p_vendor_id uuid, p_bill_payment_header_id uuid, p_created_by uuid, p_settlements jsonb)
RETURNS TABLE(id uuid, advance_settlement_number text)
LANGUAGE plpgsql
AS $function$
DECLARE
v_settlement_number text;
v_row jsonb;
v_id uuid;
BEGIN
FOR v_row IN SELECT * FROM jsonb_array_elements(p_settlements)
LOOP
v_settlement_number := public.get_new_vendor_advance_settlement_number(
p_company_id,
COALESCE((v_row->>'settled_date')::date, CURRENT_DATE)
);
v_id := (v_row->>'id')::uuid;
INSERT INTO vendor_advance_settlements
(
id,
company_id,
vendor_id,
advance_id,
bill_id,
apply_amount,
applied_in_payment_id,
settled_date,
note,
created_on_utc,
created_by,
is_deleted,
advance_settlement_number
)
VALUES
(
v_id,
p_company_id,
p_vendor_id,
(v_row->>'advance_id')::uuid,
(v_row->>'bill_id')::uuid,
(v_row->>'apply_amount')::numeric,
p_bill_payment_header_id,
COALESCE((v_row->>'settled_date')::timestamp, now()),
NULLIF(v_row->>'note',''),
now(),
p_created_by,
false,
v_settlement_number
);
UPDATE vendor_advances va
SET utilized_amount = utilized_amount + (v_row->>'apply_amount')::numeric,
modified_by = p_created_by,
modified_on_utc = now()
WHERE va.id = (v_row->>'advance_id')::uuid
AND va.company_id = p_company_id
AND va.vendor_id = p_vendor_id
AND va.is_deleted = false;
-- Assign to output variables, then RETURN NEXT;
id := v_id;
advance_settlement_number := v_settlement_number;
RETURN NEXT;
END LOOP;
END;
$function$
-- Function: get_all_unit_names_by_apartment_id
CREATE OR REPLACE FUNCTION public.get_all_unit_names_by_apartment_id(p_apartment_id uuid)
RETURNS TABLE(id integer, unit_id integer, unit_name text, building_name text, floor_name text)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT
CAST(ROW_NUMBER() OVER (ORDER BY u.id) AS INT) AS id,
u.id AS unit_id,
u."name" AS unit_name,
b."name" AS building_name,
f."name" AS floor_name
FROM units u
INNER JOIN buildings b ON u.building_id = b.id
INNER JOIN floors f ON u.floor_id = f.id
WHERE u.apartment_id = p_apartment_id
AND u.is_deleted = false
AND b.is_deleted = false
AND f.is_deleted = false;
END;
$function$
-- Function: get_all_bills_from_span
-- SOURCE
CREATE OR REPLACE FUNCTION public.get_all_bills_from_span(p_company_id uuid, p_fin_year_id integer, p_user_id uuid, p_bill_type_id integer DEFAULT NULL::integer, 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, bill_voucher text, bill_number text, vendor_name character varying, vendor_id uuid, due_date date, bill_date date, payment_term integer, total_amount numeric, total_paid_amount numeric, currency_id integer, bill_status character varying, bill_status_id integer, payment_status character varying, payment_status_id integer, tds_status character varying, tds_status_id integer, created_by uuid, created_on_utc timestamp without time zone, modified_by uuid, modified_on_utc timestamp without time zone, created_by_name character varying, modified_by_name character varying, bill_type character varying, bill_type_id integer, tds_amount numeric, has_next_status_approval boolean, next_status_id integer)
LANGUAGE plpgsql
AS $function$
DECLARE
v_start_date date := COALESCE(p_start_date::date, MAKE_DATE(p_fin_year_id, 4, 1));
v_end_date date := COALESCE(p_end_date::date, MAKE_DATE(p_fin_year_id + 1, 3, 31));
DRAFT_STATUS CONSTANT integer := 1;
BEGIN
RETURN QUERY
WITH bill_data AS (
SELECT
bh.id,
bh.bill_voucher,
bh.bill_number,
v.name AS vendor_name,
bh.vendor_id,
bh.due_date,
bh.bill_date,
bh.payment_term,
bh.total_amount,
bh.total_paid_amount,
bh.currency_id,
bs.name AS bill_status,
bh.bill_status_id,
ps.name AS payment_status, -- ✅ Added
bh.payment_status_id, -- ✅ Added
ts.name AS tds_status, -- ✅ Added
bh.tds_status_id, -- ✅ Added
bh.created_by,
bh.created_on_utc,
bh.modified_by,
bh.modified_on_utc,
CONCAT(cu.first_name, ' ', cu.last_name)::character varying AS created_by_name,
CONCAT(mu.first_name, ' ', mu.last_name)::character varying AS modified_by_name,
bt.name AS bill_type,
bh.bill_type_id,
bh.tds_amount,
bh.debit_account_id
FROM public.bill_headers bh
LEFT JOIN public.vendors v ON v.id = bh.vendor_id
LEFT JOIN public.bill_statuses bs ON bs.id = bh.bill_status_id
LEFT JOIN public.payment_statuses ps ON ps.id = bh.payment_status_id
LEFT JOIN public.tds_statuses ts ON ts.id = bh.tds_status_id -- ✅ Added
LEFT JOIN public.bill_types bt ON bt.id = bh.bill_type_id
LEFT JOIN public.users cu ON cu.id = bh.created_by
LEFT JOIN public.users mu ON mu.id = bh.modified_by
WHERE bh.company_id = p_company_id
AND bh.is_deleted = false
AND (p_bill_type_id IS NULL OR p_bill_type_id = 0 OR bh.bill_type_id = p_bill_type_id)
AND bh.bill_date BETWEEN v_start_date AND v_end_date
UNION ALL
SELECT
dbh.id,
dbh.bill_voucher,
dbh.bill_number,
v.name AS vendor_name,
dbh.vendor_id,
dbh.due_date,
dbh.bill_date,
dbh.payment_term,
dbh.total_amount,
0::numeric(18,2) AS total_paid_amount,
dbh.currency_id,
bs.name AS bill_status,
dbh.bill_status_id,
ps.name AS payment_status, -- ✅ Added
dbh.payment_status_id, -- ✅ Added
ts.name AS tds_status, -- ✅ Added
dbh.tds_status_id, -- ✅ Added
dbh.created_by,
dbh.created_on_utc,
dbh.modified_by,
dbh.modified_on_utc,
CONCAT(cu.first_name, ' ', cu.last_name)::character varying AS created_by_name,
CONCAT(mu.first_name, ' ', mu.last_name)::character varying AS modified_by_name,
bt.name AS bill_type,
dbh.bill_type_id,
dbh.tds_amount,
dbh.debit_account_id
FROM public.draft_bill_headers dbh
LEFT JOIN public.vendors v ON v.id = dbh.vendor_id
LEFT JOIN public.bill_statuses bs ON bs.id = dbh.bill_status_id
LEFT JOIN public.payment_statuses ps ON ps.id = dbh.payment_status_id
LEFT JOIN public.tds_statuses ts ON ts.id = dbh.tds_status_id -- ✅ Added
LEFT JOIN public.bill_types bt ON bt.id = dbh.bill_type_id
LEFT JOIN public.users cu ON cu.id = dbh.created_by
LEFT JOIN public.users mu ON mu.id = dbh.modified_by
WHERE dbh.company_id = p_company_id
AND dbh.is_deleted = false
AND (p_bill_type_id IS NULL OR p_bill_type_id = 0 OR dbh.bill_type_id = p_bill_type_id)
AND dbh.bill_date BETWEEN v_start_date AND v_end_date
),
enriched AS (
SELECT
bd.*,
COALESCE(
(
SELECT bw.next_status
FROM bill_workflow bw
WHERE bw.company_id = p_company_id
AND bw.status = bd.bill_status_id
AND bw.is_deleted = false
AND (bw.is_enabled IS DISTINCT FROM FALSE)
ORDER BY bw.approval_level ASC NULLS LAST
LIMIT 1
),
0
) AS next_status_id
FROM bill_data bd
)
SELECT
e.id,
e.bill_voucher,
e.bill_number,
e.vendor_name,
e.vendor_id,
e.due_date::date,
e.bill_date::date,
e.payment_term,
e.total_amount,
e.total_paid_amount,
e.currency_id,
e.bill_status,
e.bill_status_id,
e.payment_status,
e.payment_status_id,
e.tds_status,
e.tds_status_id,
e.created_by,
e.created_on_utc,
e.modified_by,
e.modified_on_utc,
e.created_by_name,
e.modified_by_name,
e.bill_type::character varying,
e.bill_type_id,
e.tds_amount,
CASE
WHEN e.bill_status_id = DRAFT_STATUS THEN true
WHEN e.next_status_id = 0 THEN false
WHEN EXISTS (
SELECT 1
FROM bill_approval_user_account a
WHERE a.account_id = e.debit_account_id
AND a.status_id = e.bill_status_id
AND a.user_id = p_user_id
AND a.is_deleted = false
) THEN true
WHEN EXISTS (
SELECT 1
FROM bill_approval_user_company c
WHERE c.company_id = p_company_id
AND c.status_id = e.bill_status_id
AND c.user_id = p_user_id
AND c.is_deleted = false
) THEN true
ELSE false
END AS has_next_status_approval,
e.next_status_id
FROM enriched e;
END;
$function$
-- TARGET
CREATE OR REPLACE FUNCTION public.get_all_bills_from_span(p_company_id uuid, p_fin_year_id integer, p_user_id uuid, p_bill_type_id integer DEFAULT NULL::integer, 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, bill_voucher text, bill_number text, vendor_name character varying, vendor_id uuid, due_date date, bill_date date, payment_term integer, total_amount numeric, currency_id integer, bill_status character varying, bill_status_id integer, created_by uuid, created_on_utc timestamp without time zone, modified_by uuid, modified_on_utc timestamp without time zone, created_by_name character varying, modified_by_name character varying, bill_type character varying, bill_type_id integer, tds_amount numeric, has_next_status_approval boolean, next_status_id integer)
LANGUAGE plpgsql
AS $function$
DECLARE
v_start_date date := COALESCE(p_start_date::date, MAKE_DATE(p_fin_year_id, 4, 1));
v_end_date date := COALESCE(p_end_date::date, MAKE_DATE(p_fin_year_id + 1, 3, 31));
DRAFT_STATUS CONSTANT integer := 1;
APPROVED_STATUS CONSTANT integer := 3;
PAID_STATUS CONSTANT integer := 5;
BEGIN
RETURN QUERY
WITH bill_data AS (
SELECT
bh.id,
bh.bill_voucher,
bh.bill_number,
v.name AS vendor_name,
bh.vendor_id,
bh.due_date,
bh.bill_date,
bh.payment_term,
bh.total_amount,
bh.currency_id,
bs.name AS bill_status,
bh.bill_status_id,
bh.created_by,
bh.created_on_utc,
bh.modified_by,
bh.modified_on_utc,
CONCAT(cu.first_name, ' ', cu.last_name)::character varying AS created_by_name,
CONCAT(mu.first_name, ' ', mu.last_name)::character varying AS modified_by_name,
bt.name AS bill_type,
bh.bill_type_id,
bh.tds_amount,
bh.debit_account_id
FROM public.bill_headers bh
LEFT JOIN public.vendors v ON v.id = bh.vendor_id
LEFT JOIN public.bill_statuses bs ON bs.id = bh.bill_status_id
LEFT JOIN public.bill_types bt ON bt.id = bh.bill_type_id
LEFT JOIN users cu ON cu.id = bh.created_by
LEFT JOIN users mu ON mu.id = bh.modified_by
WHERE bh.company_id = p_company_id
AND bh.is_deleted = false
AND (p_bill_type_id IS NULL OR p_bill_type_id = 0 OR bh.bill_type_id = p_bill_type_id)
AND bh.bill_date BETWEEN v_start_date AND v_end_date
UNION ALL
SELECT
dbh.id,
dbh.bill_voucher,
dbh.bill_number,
v.name AS vendor_name,
dbh.vendor_id,
dbh.due_date,
dbh.bill_date,
dbh.payment_term,
dbh.total_amount,
dbh.currency_id,
bs.name AS bill_status,
dbh.bill_status_id,
dbh.created_by,
dbh.created_on_utc,
dbh.modified_by,
dbh.modified_on_utc,
CONCAT(cu.first_name, ' ', cu.last_name)::character varying AS created_by_name,
CONCAT(mu.first_name, ' ', mu.last_name)::character varying AS modified_by_name,
bt.name AS bill_type,
dbh.bill_type_id,
dbh.tds_amount,
dbh.debit_account_id
FROM public.draft_bill_headers dbh
LEFT JOIN vendors v ON v.id = dbh.vendor_id
LEFT JOIN bill_statuses bs ON bs.id = dbh.bill_status_id
LEFT JOIN bill_types bt ON bt.id = dbh.bill_type_id
LEFT JOIN users cu ON cu.id = dbh.created_by
LEFT JOIN users mu ON mu.id = dbh.modified_by
WHERE dbh.company_id = p_company_id
AND dbh.is_deleted = false
AND (p_bill_type_id IS NULL OR p_bill_type_id = 0 OR dbh.bill_type_id = p_bill_type_id)
AND dbh.bill_date BETWEEN v_start_date AND v_end_date
)
SELECT
bd.id,
bd.bill_voucher,
bd.bill_number,
bd.vendor_name,
bd.vendor_id,
bd.due_date::date,
bd.bill_date::date,
bd.payment_term,
bd.total_amount,
bd.currency_id,
bd.bill_status,
bd.bill_status_id,
bd.created_by,
bd.created_on_utc,
bd.modified_by,
bd.modified_on_utc,
bd.created_by_name,
bd.modified_by_name,
bd.bill_type::character varying,
bd.bill_type_id,
bd.tds_amount,
CASE
WHEN bd.bill_status_id = DRAFT_STATUS THEN true
WHEN EXISTS (
SELECT 1
FROM bill_approval_user_account a
WHERE a.account_id = bd.debit_account_id
AND a.status_id = bd.bill_status_id
AND a.user_id = p_user_id
AND a.is_deleted = false
) THEN true
WHEN EXISTS (
SELECT 1
FROM bill_approval_user_company c
WHERE c.company_id = p_company_id
AND c.status_id = bd.bill_status_id
AND c.user_id = p_user_id
AND c.is_deleted = false
) THEN true
ELSE false
END AS has_next_status_approval,
COALESCE(
(SELECT bw.next_status
FROM bill_workflow bw
WHERE bw.company_id = p_company_id
AND bw.status = bd.bill_status_id
AND bw.is_deleted = false
LIMIT 1),
0
) AS next_status_id
FROM bill_data bd;
END;
$function$
-- Function: get_bill_by_id_json
CREATE OR REPLACE FUNCTION public.get_bill_by_id_json(p_bill_header_id uuid)
RETURNS json
LANGUAGE plpgsql
AS $function$
DECLARE
v_bill_status text;
v_bill_json json;
BEGIN
-- Get bill status name
SELECT bs.name INTO v_bill_status
FROM bill_statuses bs
JOIN bill_headers bh ON bh.bill_status_id = bs.id
WHERE bh.id = p_bill_header_id;
-- Construct the JSON output
SELECT json_build_object(
'id', bh.id,
'billNumber', bh.bill_number,
'billDate', bh.bill_date,
'paymentTerm', bh.payment_term,
'billStatus', v_bill_status,
'billStatusId', bh.bill_status_id,
'vendor', json_build_object(
'id', v.id,
'name', v.name,
'email', c.email,
'mobileNumber', c.mobile_number,
'phoneNumber', c.phone_number,
'gstIn', v.gstin,
'shortName', v.short_name,
'pan', v.pan,
'tan', v.tan
),
'debitAccountId', bh.debit_account_id,
'currencyId', bh.currency_id,
'dueDate', bh.due_date,
'taxableAmount', bh.taxable_amount,
'fees', bh.fees,
'cgstAmount', bh.cgst_amount,
'sgstAmount', bh.sgst_amount,
'igstAmount', bh.igst_amount,
'totalAmount', bh.total_amount,
'discount', bh.discount,
'note', bh.note,
'roundOff', bh.round_off,
'sourceWarehouse', NULL,
'sourceWarehouseId', bh.source_warehouse_id,
'destinationWarehouseId', bh.destination_warehouse_id,
'poNo', bh.po_no,
'poDate', bh.po_date,
'lines', (
SELECT json_agg(json_build_object(
'id', bl.id,
'productId', bl.product_id,
'price', bl.price,
'quantity', bl.quantity,
'fees', bl.fees,
'discount', bl.discount,
'taxableAmount', bl.taxable_amount,
'sgstAmount', bl.sgst_amount,
'cgstAmount', bl.cgst_amount,
'igstAmount', bl.igst_amount,
'totalAmount', bl.total_amount,
'serialNumber', bl.serial_number
))
FROM bill_details bl
WHERE bl.bill_header_id = p_bill_header_id
),
'billTypeId', bh.bill_type_id
) INTO v_bill_json
FROM bill_headers bh
LEFT JOIN vendors v ON v.id = bh.vendor_id
LEFT JOIN vendor_contacts vc ON vc.vendor_id = v.id
LEFT JOIN contacts c ON c.id = vc.contact_id
WHERE bh.id = p_bill_header_id;
-- Return the JSON object
RETURN v_bill_json;
END;
$function$
-- Function: get_vendor_advances_by_vendor
CREATE OR REPLACE FUNCTION public.get_vendor_advances_by_vendor(p_company_id uuid, p_vendor_id uuid, only_unutilized_advances boolean DEFAULT false)
RETURNS TABLE(id uuid, vendor_id uuid, vendor_name character varying, advance_number text, paid_date timestamp without time zone, amount numeric, utilized_amount numeric, mode_of_payment text, reference text, description text, currency_id integer)
LANGUAGE sql
AS $function$
SELECT
va.id,
va.vendor_id,
v.name, -- Add vendor name
va.advance_number,
va.paid_date,
va.amount,
va.utilized_amount,
va.mode_of_payment,
va.reference,
va.description,
va.currency_id
FROM public.vendor_advances va
LEFT JOIN public.vendors v ON va.vendor_id = v.id -- Join here to get the name
WHERE va.company_id = p_company_id
AND va.vendor_id = p_vendor_id
AND va.is_deleted = false
AND v.is_deleted = false
AND (
-- If only_unutilized_advances = true → filter unsettled
(only_unutilized_advances = true AND va.amount > va.utilized_amount)
-- If false or null → return all
OR (only_unutilized_advances IS DISTINCT FROM true)
);
$function$
-- Function: get_payment_distributions_for_bill
CREATE OR REPLACE FUNCTION public.get_payment_distributions_for_bill(p_bill_header_id uuid)
RETURNS TABLE(payment_detail_id uuid, bill_payment_header_id uuid, payment_number text, bill_header_id uuid, paid_amount numeric, tds_amount numeric, payment_is_deleted boolean, payment_created_by uuid, payment_created_by_name text, payment_created_on timestamp without time zone)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT
bpd.id AS payment_detail_id,
bpd.bill_payment_header_id,
bph.payment_number,
bpd.bill_header_id,
bpd.paid_amount,
bpd.tds_amount,
bpd.is_deleted AS payment_is_deleted,
bpd.created_by AS payment_created_by,
CONCAT(u.first_name, ' ', u.last_name) AS payment_created_by_name,
bpd.created_on_utc AS payment_created_on
FROM
public.bill_payment_details bpd
JOIN public.bill_payment_headers bph ON bph.id = bpd.bill_payment_header_id
LEFT JOIN public.users u ON u.id = bpd.created_by
WHERE
bpd.bill_header_id = p_bill_header_id
AND bpd.is_deleted = false
ORDER BY
bpd.created_on_utc ASC;
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: insert_multiple_bills_by_excel
-- SOURCE
CREATE OR REPLACE FUNCTION public.insert_multiple_bills_by_excel(p_bills jsonb)
RETURNS text
LANGUAGE plpgsql
AS $function$
DECLARE
bill RECORD;
v_created_by UUID;
v_status TEXT := 'success';
BEGIN
-- Validate the input JSONB structure
BEGIN
PERFORM *
FROM jsonb_to_recordset(p_bills) AS (
bill_id UUID,
company_id UUID,
vendor_id UUID,
debit_account_id UUID,
credit_account_id UUID,
discount NUMERIC,
bill_number TEXT,
currency_id INTEGER,
bill_date TIMESTAMP,
payment_term INTEGER,
bill_status_id INTEGER,
due_date TIMESTAMP,
total_amount NUMERIC,
note TEXT,
taxable_amount NUMERIC,
fees NUMERIC,
sgst_amount NUMERIC,
cgst_amount NUMERIC,
igst_amount NUMERIC,
tds_amount NUMERIC,
round_off NUMERIC,
round_off_tds NUMERIC,
po_no TEXT,
po_date TIMESTAMP,
source_warehouse_id UUID,
destination_warehouse_id UUID,
bill_type_id INTEGER,
lines JSONB,
created_by UUID
);
EXCEPTION
WHEN OTHERS THEN
RAISE NOTICE 'Invalid JSON input: %', SQLERRM;
RETURN 'error';
END;
-- Get 'System' user as created_by fallback
SELECT id INTO v_created_by
FROM public.users
WHERE first_name = 'System'
LIMIT 1;
-- Loop through each bill
FOR bill IN
SELECT * FROM jsonb_to_recordset(p_bills) AS (
bill_id UUID,
company_id UUID,
vendor_id UUID,
debit_account_id UUID,
credit_account_id UUID,
discount NUMERIC,
bill_number TEXT,
currency_id INTEGER,
bill_date TIMESTAMP,
payment_term INTEGER,
bill_status_id INTEGER,
due_date TIMESTAMP,
total_amount NUMERIC,
note TEXT,
taxable_amount NUMERIC,
fees NUMERIC,
sgst_amount NUMERIC,
cgst_amount NUMERIC,
igst_amount NUMERIC,
tds_amount NUMERIC,
round_off NUMERIC,
round_off_tds NUMERIC,
po_no TEXT,
po_date TIMESTAMP,
source_warehouse_id UUID,
destination_warehouse_id UUID,
bill_type_id INTEGER,
lines JSONB,
created_by UUID
)
LOOP
BEGIN
-- Duplicate check
IF EXISTS (
SELECT 1
FROM public.bill_headers
WHERE bill_number = bill.bill_number
AND company_id = bill.company_id
) THEN
RAISE NOTICE 'Duplicate: %, %', bill.bill_number, bill.company_id;
v_status := 'duplicate';
CONTINUE;
END IF;
-- Call the save routine
CALL public.save_bill_and_details(
bill.bill_id,
bill.company_id,
bill.vendor_id,
bill.discount,
bill.bill_date,
bill.payment_term,
bill.bill_status_id,
bill.due_date,
bill.total_amount,
bill.taxable_amount,
bill.fees,
bill.sgst_amount,
bill.cgst_amount,
bill.igst_amount,
bill.note,
bill.round_off,
bill.round_off_tds,
bill.debit_account_id,
bill.credit_account_id,
v_created_by,
bill.currency_id,
bill.po_date,
bill.po_no,
bill.destination_warehouse_id,
bill.source_warehouse_id,
bill.bill_type_id,
bill.bill_number,
bill.tds_amount,
bill.lines
);
EXCEPTION
WHEN OTHERS THEN
RAISE NOTICE 'Error bill %: %', bill.bill_id, SQLERRM;
v_status := 'error';
CONTINUE;
END;
END LOOP;
RETURN v_status;
END;
$function$
-- TARGET
CREATE OR REPLACE FUNCTION public.insert_multiple_bills_by_excel(p_bills jsonb)
RETURNS text
LANGUAGE plpgsql
AS $function$
DECLARE
bill RECORD;
v_created_by UUID;
v_status TEXT := 'success';
BEGIN
-- Validate the input JSONB structure
BEGIN
PERFORM *
FROM jsonb_to_recordset(p_bills) AS (
bill_id UUID,
company_id UUID,
vendor_id UUID,
debit_account_id UUID,
credit_account_id UUID,
discount NUMERIC,
bill_number TEXT,
currency_id INTEGER,
bill_date TIMESTAMP,
payment_term INTEGER,
bill_status_id INTEGER,
due_date TIMESTAMP,
total_amount NUMERIC,
note TEXT,
taxable_amount NUMERIC,
fees NUMERIC,
sgst_amount NUMERIC,
cgst_amount NUMERIC,
igst_amount NUMERIC,
tds_amount NUMERIC,
round_off NUMERIC,
po_no TEXT,
po_date TIMESTAMP,
source_warehouse_id UUID,
destination_warehouse_id UUID,
bill_type_id INTEGER,
lines JSONB,
created_by UUID
);
EXCEPTION
WHEN OTHERS THEN
RAISE NOTICE 'Invalid JSON input: %', SQLERRM;
RETURN 'error';
END;
-- Get 'System' user as created_by fallback
SELECT id INTO v_created_by
FROM public.users
WHERE first_name = 'System'
LIMIT 1;
-- Loop through each bill
FOR bill IN
SELECT * FROM jsonb_to_recordset(p_bills) AS (
bill_id UUID,
company_id UUID,
vendor_id UUID,
debit_account_id UUID,
credit_account_id UUID,
discount NUMERIC,
bill_number TEXT,
currency_id INTEGER,
bill_date TIMESTAMP,
payment_term INTEGER,
bill_status_id INTEGER,
due_date TIMESTAMP,
total_amount NUMERIC,
note TEXT,
taxable_amount NUMERIC,
fees NUMERIC,
sgst_amount NUMERIC,
cgst_amount NUMERIC,
igst_amount NUMERIC,
tds_amount NUMERIC,
round_off NUMERIC,
po_no TEXT,
po_date TIMESTAMP,
source_warehouse_id UUID,
destination_warehouse_id UUID,
bill_type_id INTEGER,
lines JSONB,
created_by UUID
)
LOOP
BEGIN
-- Duplicate check
IF EXISTS (
SELECT 1
FROM public.bill_headers
WHERE bill_number = bill.bill_number
AND company_id = bill.company_id
) THEN
RAISE NOTICE 'Duplicate: %, %', bill.bill_number, bill.company_id;
v_status := 'duplicate';
CONTINUE;
END IF;
-- Call the save routine
CALL public.save_bill_and_details(
bill.bill_id,
bill.company_id,
bill.vendor_id,
bill.discount,
bill.bill_date,
bill.payment_term,
bill.bill_status_id,
bill.due_date,
bill.total_amount,
bill.taxable_amount,
bill.fees,
bill.sgst_amount,
bill.cgst_amount,
bill.igst_amount,
bill.note,
bill.round_off,
bill.debit_account_id,
bill.credit_account_id,
v_created_by,
bill.currency_id,
bill.po_date,
bill.po_no,
bill.destination_warehouse_id,
bill.source_warehouse_id,
bill.bill_type_id,
bill.bill_number,
bill.tds_amount,
bill.lines
);
EXCEPTION
WHEN OTHERS THEN
RAISE NOTICE 'Error bill %: %', bill.bill_id, SQLERRM;
v_status := 'error';
CONTINUE;
END;
END LOOP;
RETURN v_status;
END;
$function$
-- Function: get_bill_by_id
-- SOURCE
CREATE OR REPLACE FUNCTION public.get_bill_by_id(p_bill_header_id uuid)
RETURNS TABLE(id uuid, bill_number text, bill_voucher text, debit_account_id uuid, credit_account_id uuid, bill_date timestamp with time zone, payment_term integer, vendor_id uuid, vendor_name text, vendor_gstin text, vendor_short_name text, vendor_pan text, vendor_tan text, vendor_email text, vendor_phone_number text, vendor_mobile_number text, vendor_address_line1 text, vendor_address_line2 text, vendor_city_name text, vendor_state_name text, vendor_country_name text, vendor_zip_code text, due_date timestamp with time zone, total_amount numeric, taxable_amount numeric, fees numeric, cgst_amount numeric, sgst_amount numeric, igst_amount numeric, currency_id integer, bill_status_id integer, bill_status text, discount numeric, note text, round_off numeric, round_off_tds numeric, po_no text, po_date timestamp without time zone, destination_warehouse_id uuid, source_warehouse_id uuid, source_vendor_id uuid, source_warehouse_name text, source_address_id uuid, source_country_name text, source_country_id uuid, source_state_name text, source_state_id uuid, source_city_name text, source_city_id uuid, source_address_line1 text, source_address_line2 text, source_zip_code text, source_gstin text, bill_lines jsonb, bill_type_id integer, current_approval_level integer, tds_amount numeric, created_on timestamp with time zone, modified_on timestamp with time zone, created_by uuid, modified_by uuid)
LANGUAGE plpgsql
AS $function$
DECLARE
v_bill_status_id INTEGER;
BEGIN
v_bill_status_id := public.get_bill_status(p_bill_header_id);
IF v_bill_status_id >= 3 THEN
RETURN QUERY
SELECT
bh.id,
bh.bill_number::text,
bh.bill_voucher::text,
bh.debit_account_id,
bh.credit_account_id,
bh.bill_date::TIMESTAMP WITH TIME ZONE,
bh.payment_term,
bh.vendor_id,
v.name::text AS vendor_name,
v.gstin::text AS vendor_gstin,
v.short_name::text AS vendor_short_name,
v.pan::text AS vendor_pan,
v.tan::text AS vendor_tan,
co.email::text AS vendor_email,
co.phone_number::text AS vendor_phone_number,
co.mobile_number::text AS vendor_mobile_number,
addr.address_line1::text AS vendor_address_line1,
addr.address_line2::text AS vendor_address_line2,
cit.name::text AS vendor_city_name,
st.name::text AS vendor_state_name,
ctry.name::text AS vendor_country_name,
addr.zip_code::text AS vendor_zip_code,
bh.due_date::TIMESTAMP WITH TIME ZONE,
bh.total_amount,
bh.taxable_amount,
bh.fees,
bh.cgst_amount,
bh.sgst_amount,
bh.igst_amount,
bh.currency_id,
bh.bill_status_id,
blst.name::text,
bh.discount,
bh.note::text,
bh.round_off,
bh.round_off_tds,
bh.po_no::text,
bh.po_date::TIMESTAMP WITHOUT TIME ZONE,
bh.destination_warehouse_id,
wh.id AS source_warehouse_id,
wh.vendor_id AS source_vendor_id,
wh.name::text AS source_warehouse_name,
wh.address_id AS source_address_id,
wh.country_name::text AS source_country_name,
wh.country_id AS source_country_id,
wh.state_name::text AS source_state_name,
wh.state_id AS source_state_id,
wh.city_name::text AS source_city_name,
wh.city_id AS source_city_id,
wh.address_line1::text AS source_address_line1,
wh.address_line2::text AS source_address_line2,
wh.zip_code::text AS source_zip_code,
wh.gstin::text AS source_gstin,
jsonb_agg(jsonb_build_object(
'bill_line_id', bl.id,
'product_id', bl.product_id,
'price', bl.price,
'quantity', bl.quantity,
'fees', bl.fees,
'discount', bl.discount,
'taxable_amount', bl.taxable_amount,
'sgst_amount', bl.sgst_amount,
'cgst_amount', bl.cgst_amount,
'igst_amount', bl.igst_amount,
'total_amount', bl.total_amount,
'serial_number', bl.serial_number
) ORDER BY bl.serial_number) AS bill_lines,
bh.bill_type_id,
bh.current_approval_level,
bh.tds_amount,
bh.created_on_utc::TIMESTAMP WITH TIME ZONE,
bh.modified_on_utc::TIMESTAMP WITH TIME ZONE,
bh.created_by,
bh.modified_by
FROM bill_headers bh
LEFT JOIN public.vendors v ON bh.vendor_id = v.id
LEFT JOIN public.vendor_contacts vc ON v.id = vc.vendor_id AND vc.is_deleted = false
LEFT JOIN public.contacts co ON vc.contact_id = co.id AND co.is_deleted = false AND co.is_primary = true
LEFT JOIN public.addresses addr ON v.billing_address_id = addr.id
LEFT JOIN public.cities cit ON addr.city_id = cit.id
LEFT JOIN public.states st ON addr.state_id = st.id
LEFT JOIN public.countries ctry ON addr.country_id = ctry.id
LEFT JOIN public.get_warehouse_details(bh.source_warehouse_id) wh ON TRUE
LEFT JOIN public.get_bill_lines(bh.id, bh.bill_status_id) bl ON TRUE
JOIN public.bill_statuses blst ON bh.bill_status_id = blst.id
WHERE bh.id = p_bill_header_id
GROUP BY
bh.id,
bh.bill_number,
bh.bill_voucher,
bh.bill_date,
bh.payment_term,
bh.vendor_id,
v.name,
v.gstin,
v.short_name,
v.pan,
v.tan,
co.email,
co.phone_number,
co.mobile_number,
addr.address_line1,
addr.address_line2,
cit.name,
st.name,
ctry.name,
addr.zip_code,
bh.due_date,
bh.total_amount,
bh.taxable_amount,
bh.fees,
bh.cgst_amount,
bh.sgst_amount,
bh.igst_amount,
bh.currency_id,
bh.bill_status_id,
blst.name,
bh.discount,
bh.note,
bh.round_off,
bh.round_off_tds,
bh.po_no,
bh.po_date,
bh.destination_warehouse_id,
bh.source_warehouse_id,
wh.id,
wh.vendor_id,
wh.name,
wh.address_id,
wh.country_name,
wh.country_id,
wh.state_name,
wh.state_id,
wh.city_name,
wh.city_id,
wh.address_line1,
wh.address_line2,
wh.zip_code,
wh.gstin,
bh.bill_type_id,
bh.current_approval_level,
bh.tds_amount,
bh.created_on_utc,
bh.modified_on_utc,
bh.created_by,
bh.modified_by;
ELSE
-- You can add the similar query for draft bill_headers here
RETURN QUERY
SELECT
dbh.id,
dbh.bill_number::text,
dbh.bill_voucher::text,
dbh.debit_account_id,
dbh.credit_account_id,
dbh.bill_date::TIMESTAMP WITH TIME ZONE,
dbh.payment_term,
dbh.vendor_id,
v.name::text AS vendor_name,
v.gstin::text AS vendor_gstin,
v.short_name::text AS vendor_short_name,
v.pan::text AS vendor_pan,
v.tan::text AS vendor_tan,
co.email::text AS vendor_email,
co.phone_number::text AS vendor_phone_number,
co.mobile_number::text AS vendor_mobile_number,
addr.address_line1::text,
addr.address_line2::text,
cit.name::text AS vendor_city_name,
st.name::text AS vendor_state_name,
ctry.name::text AS vendor_country_name,
addr.zip_code::text,
dbh.due_date::TIMESTAMP WITH TIME ZONE,
dbh.total_amount,
dbh.taxable_amount,
dbh.fees,
dbh.cgst_amount,
dbh.sgst_amount,
dbh.igst_amount,
dbh.currency_id,
dbh.bill_status_id,
dblst.name::text,
dbh.discount,
dbh.note::text,
dbh.round_off,
dbh.round_off_tds,
dbh.po_no::text,
dbh.po_date::TIMESTAMP WITHOUT TIME ZONE,
dbh.destination_warehouse_id,
wh.id AS source_warehouse_id,
wh.vendor_id AS source_vendor_id,
wh.name::text AS source_warehouse_name,
wh.address_id AS source_address_id,
wh.country_name::text AS source_country_name,
wh.country_id AS source_country_id,
wh.state_name::text AS source_state_name,
wh.state_id AS source_state_id,
wh.city_name::text AS source_city_name,
wh.city_id AS source_city_id,
wh.address_line1::text AS source_address_line1,
wh.address_line2::text AS source_address_line2,
wh.zip_code::text AS source_zip_code,
wh.gstin::text AS source_gstin,
jsonb_agg(jsonb_build_object(
'bill_line_id', dbl.id,
'product_id', dbl.product_id,
'price', dbl.price,
'quantity', dbl.quantity,
'fees', dbl.fees,
'discount', dbl.discount,
'taxable_amount', dbl.taxable_amount,
'sgst_amount', dbl.sgst_amount,
'cgst_amount', dbl.cgst_amount,
'igst_amount', dbl.igst_amount,
'total_amount', dbl.total_amount,
'serial_number', dbl.serial_number
) ORDER BY dbl.serial_number) AS bill_lines,
dbh.bill_type_id,
dbh.current_approval_level,
dbh.tds_amount,
dbh.created_on_utc::TIMESTAMP WITH TIME ZONE,
dbh.modified_on_utc::TIMESTAMP WITH TIME ZONE,
dbh.created_by,
dbh.modified_by
FROM draft_bill_headers dbh
LEFT JOIN public.vendors v ON dbh.vendor_id = v.id
LEFT JOIN public.vendor_contacts vc ON v.id = vc.vendor_id AND vc.is_deleted = false
LEFT JOIN public.contacts co ON vc.contact_id = co.id AND co.is_deleted = false AND co.is_primary = true
LEFT JOIN public.addresses addr ON v.billing_address_id = addr.id
LEFT JOIN public.cities cit ON addr.city_id = cit.id
LEFT JOIN public.states st ON addr.state_id = st.id
LEFT JOIN public.countries ctry ON addr.country_id = ctry.id
LEFT JOIN public.get_warehouse_details(dbh.source_warehouse_id) wh ON TRUE
LEFT JOIN public.get_bill_lines(dbh.id, dbh.bill_status_id) dbl ON TRUE
JOIN public.bill_statuses dblst ON dbh.bill_status_id = dblst.id
WHERE dbh.id = p_bill_header_id
GROUP BY
dbh.id,
dbh.bill_number,
dbh.bill_voucher,
dbh.bill_date,
dbh.payment_term,
dbh.vendor_id,
v.name,
v.gstin,
v.short_name,
v.pan,
v.tan,
co.email,
co.phone_number,
co.mobile_number,
addr.address_line1,
addr.address_line2,
cit.name,
st.name,
ctry.name,
addr.zip_code,
dbh.due_date,
dbh.total_amount,
dbh.taxable_amount,
dbh.fees,
dbh.cgst_amount,
dbh.sgst_amount,
dbh.igst_amount,
dbh.currency_id,
dbh.bill_status_id,
dblst.name,
dbh.discount,
dbh.note,
dbh.round_off,
dbh.round_off_tds,
dbh.destination_warehouse_id,
dbh.po_no,
dbh.po_date,
dbh.source_warehouse_id,
wh.id,
wh.vendor_id,
wh.name,
wh.address_id,
wh.country_name,
wh.country_id,
wh.state_name,
wh.state_id,
wh.city_name,
wh.city_id,
wh.address_line1,
wh.address_line2,
wh.zip_code,
wh.gstin,
dbh.bill_type_id,
dbh.current_approval_level,
dbh.tds_amount,
dbh.created_on_utc,
dbh.modified_on_utc,
dbh.created_by,
dbh.modified_by;
END IF;
END;
$function$
-- TARGET
CREATE OR REPLACE FUNCTION public.get_bill_by_id(p_bill_header_id uuid)
RETURNS TABLE(id uuid, bill_number text, bill_voucher text, debit_account_id uuid, credit_account_id uuid, bill_date timestamp with time zone, payment_term integer, vendor_id uuid, vendor_name text, vendor_email text, vendor_mobile_number text, vendor_phone_number text, vendor_gstin text, vendor_short_name text, vendor_pan text, vendor_tan text, due_date timestamp with time zone, total_amount numeric, taxable_amount numeric, fees numeric, cgst_amount numeric, sgst_amount numeric, igst_amount numeric, currency_id integer, bill_status_id integer, bill_status text, discount numeric, note text, round_off numeric, po_no text, po_date timestamp without time zone, destination_warehouse_id uuid, source_warehouse_id uuid, source_vendor_id uuid, source_warehouse_name text, source_address_id uuid, source_country_name text, source_country_id uuid, source_state_name text, source_state_id uuid, source_city_name text, source_city_id uuid, source_address_line1 text, source_address_line2 text, source_zip_code text, source_gstin text, bill_lines jsonb, bill_type_id integer, current_approval_level integer, tds_amount numeric, created_on timestamp with time zone, modified_on timestamp with time zone, created_by uuid, modified_by uuid)
LANGUAGE plpgsql
AS $function$
DECLARE
v_bill_status_id INTEGER;
BEGIN
-- Get the bill status using a helper function
v_bill_status_id := public.get_bill_status(p_bill_header_id);
IF v_bill_status_id >= 3 THEN
-- Query for Approved Bills
RETURN QUERY
SELECT
bh.id,
bh.bill_number,
bh.bill_voucher,
bh.debit_account_id,
bh.credit_account_id,
bh.bill_date::TIMESTAMP WITH TIME ZONE,
bh.payment_term,
bh.vendor_id,
v.name::text AS vendor_name,
v.email::text AS vendor_email,
v.mobile_number::text AS vendor_mobile_number,
v.phone_number::text AS vendor_phone_number,
v.gstin::text AS vendor_gstin,
v.short_name::text AS vendor_short_name,
v.pan::text AS vendor_pan,
v.tan::text AS vendor_tan,
bh.due_date::TIMESTAMP WITH TIME ZONE,
bh.total_amount,
bh.taxable_amount,
bh.fees,
bh.cgst_amount,
bh.sgst_amount,
bh.igst_amount,
bh.currency_id,
bh.bill_status_id,
blst.name::text,
bh.discount,
bh.note,
bh.round_off,
bh.po_no,
bh.po_date::TIMESTAMP WITHOUT TIME ZONE,
bh.destination_warehouse_id,
wh.id AS source_warehouse_id,
wh.vendor_id AS source_vendor_id,
wh.name AS source_warehouse_name,
wh.address_id AS source_address_id,
wh.country_name AS source_country_name,
wh.country_id AS source_country_id,
wh.state_name AS source_state_name,
wh.state_id AS source_state_id,
wh.city_name AS source_city_name,
wh.city_id AS source_city_id,
wh.address_line1 AS source_address_line1,
wh.address_line2 AS source_address_line2,
wh.zip_code AS source_zip_code,
wh.gstin::text AS source_gstin,
jsonb_agg(jsonb_build_object(
'bill_line_id', bl.id,
'product_id', bl.product_id,
'price', bl.price,
'quantity', bl.quantity,
'fees', bl.fees,
'discount', bl.discount,
'taxable_amount', bl.taxable_amount,
'sgst_amount', bl.sgst_amount,
'cgst_amount', bl.cgst_amount,
'igst_amount', bl.igst_amount,
'total_amount', bl.total_amount,
'serial_number', bl.serial_number
) ORDER BY bl.serial_number) AS bill_lines,
bh.bill_type_id,
bh.current_approval_level,
bh.tds_amount,
bh.created_on_utc::TIMESTAMP WITH TIME ZONE,
bh.modified_on_utc::TIMESTAMP WITH TIME ZONE,
bh.created_by,
bh.modified_by
FROM bill_headers bh
LEFT JOIN public.get_vendor_details(bh.vendor_id) v ON TRUE
LEFT JOIN public.get_warehouse_details(bh.source_warehouse_id) wh ON TRUE
LEFT JOIN public.get_bill_lines(bh.id, bh.bill_status_id) bl ON TRUE
JOIN public.bill_statuses blst ON bh.bill_status_id = blst.id
WHERE bh.id = p_bill_header_id
GROUP BY
bh.id,
bh.bill_number,
bh.bill_voucher,
bh.bill_date,
bh.payment_term,
bh.vendor_id,
v.name,
v.email,
v.mobile_number,
v.phone_number,
v.gstin,
v.short_name,
v.pan,
v.tan,
bh.due_date,
bh.total_amount,
bh.taxable_amount,
bh.fees,
bh.cgst_amount,
bh.sgst_amount,
bh.igst_amount,
bh.currency_id,
bh.bill_status_id,
blst.name,
bh.discount,
bh.note,
bh.round_off,
bh.po_no,
bh.po_date,
bh.destination_warehouse_id,
bh.source_warehouse_id,
wh.id,
wh.vendor_id,
wh.name,
wh.address_id,
wh.country_name,
wh.country_id,
wh.state_name,
wh.state_id,
wh.city_name,
wh.city_id,
wh.address_line1,
wh.address_line2,
wh.zip_code,
wh.gstin,
bh.bill_type_id,
bh.current_approval_level,
bh.tds_amount,
bh.created_on_utc,
bh.modified_on_utc,
bh.created_by,
bh.modified_by;
ELSE
-- Query for Draft Bills (similar structure as approved bills)
RETURN QUERY
SELECT
dbh.id,
dbh.bill_number,
dbh.bill_voucher,
dbh.debit_account_id,
dbh.credit_account_id,
dbh.bill_date::TIMESTAMP WITH TIME ZONE,
dbh.payment_term,
dbh.vendor_id,
v.name::text AS vendor_name,
v.email::text AS vendor_email,
v.mobile_number::text AS vendor_mobile_number,
v.phone_number::text AS vendor_phone_number,
v.gstin::text AS vendor_gstin,
v.short_name AS vendor_short_name,
v.pan::text AS vendor_pan,
v.tan::text AS vendor_tan,
dbh.due_date::TIMESTAMP WITH TIME ZONE,
dbh.total_amount,
dbh.taxable_amount,
dbh.fees,
dbh.cgst_amount,
dbh.sgst_amount,
dbh.igst_amount,
dbh.currency_id,
dbh.bill_status_id,
dblst.name::text,
dbh.discount,
dbh.note,
dbh.round_off,
dbh.po_no,
dbh.po_date,
dbh.destination_warehouse_id,
wh.id AS source_warehouse_id,
wh.vendor_id AS source_vendor_id,
wh.name AS source_warehouse_name,
wh.address_id AS source_address_id,
wh.country_name AS source_country_name,
wh.country_id AS source_country_id,
wh.state_name AS source_state_name,
wh.state_id AS source_state_id,
wh.city_name AS source_city_name,
wh.city_id AS source_city_id,
wh.address_line1 AS source_address_line1,
wh.address_line2 AS source_address_line2,
wh.zip_code AS source_zip_code,
wh.gstin AS source_gstin,
jsonb_agg(jsonb_build_object(
'bill_line_id', dbl.id,
'product_id', dbl.product_id,
'price', dbl.price,
'quantity', dbl.quantity,
'fees', dbl.fees,
'discount', dbl.discount,
'taxable_amount', dbl.taxable_amount,
'sgst_amount', dbl.sgst_amount,
'cgst_amount', dbl.cgst_amount,
'igst_amount', dbl.igst_amount,
'total_amount', dbl.total_amount,
'serial_number', dbl.serial_number
) ORDER BY dbl.serial_number) AS bill_lines,
dbh.bill_type_id,
dbh.current_approval_level,
dbh.tds_amount,
dbh.created_on_utc::TIMESTAMP WITH TIME ZONE,
dbh.modified_on_utc::TIMESTAMP WITH TIME ZONE,
dbh.created_by,
dbh.modified_by
FROM draft_bill_headers dbh
LEFT JOIN public.get_vendor_details(dbh.vendor_id) v ON TRUE
LEFT JOIN public.get_warehouse_details(dbh.source_warehouse_id) wh ON TRUE
LEFT JOIN public.get_bill_lines(dbh.id, dbh.bill_status_id) dbl ON TRUE
JOIN public.bill_statuses dblst ON dbh.bill_status_id = dblst.id
WHERE dbh.id = p_bill_header_id
GROUP BY
dbh.id,
dbh.bill_number,
dbh.bill_voucher,
dbh.bill_date,
dbh.payment_term,
dbh.vendor_id,
v.name, v.email,
v.mobile_number,
v.phone_number,
v.gstin,
v.short_name,
v.pan,
v.tan,
dbh.due_date,
dbh.total_amount,
dbh.taxable_amount,
dbh.fees,
dbh.cgst_amount,
dbh.sgst_amount,
dbh.igst_amount,
dbh.currency_id,
dbh.bill_status_id,
dblst.name,
dbh.discount,
dbh.note,
dbh.round_off,
dbh.destination_warehouse_id,
dbh.po_no,
dbh.po_date,
dbh.source_warehouse_id,
wh.id,
wh.vendor_id,
wh.name,
wh.address_id,
wh.country_name,
wh.country_id,
wh.state_name,
wh.state_id,
wh.city_name,
wh.city_id,
wh.address_line1,
wh.address_line2,
wh.zip_code,
wh.gstin,
dbh.bill_type_id,
dbh.current_approval_level,
dbh.tds_amount,
dbh.created_on_utc,
dbh.modified_on_utc,
dbh.created_by,
dbh.modified_by;
END IF;
END;
$function$
-- Function: list_scheduled_bill_ids
CREATE OR REPLACE FUNCTION public.list_scheduled_bill_ids(p_schedule_date timestamp without time zone DEFAULT CURRENT_TIMESTAMP)
RETURNS TABLE(bill_header_id uuid, draft_bill_header_id uuid, company_id uuid, organization_id uuid)
LANGUAGE sql
AS $function$
WITH base AS (
SELECT
s.id AS schedule_id,
s.bill_header_id AS bill_header_id,
s.draft_bill_header_id AS draft_bill_header_id,
s.company_id AS company_id,
c.organization_id AS organization_id,
d.frequency_cycle,
d.day_of_week,
d.day_of_month,
d.month_of_year,
s.starts_from,
s.end_date
FROM public.recurring_bill_schedules s
JOIN public.recurrence_bill_schedule_details d
ON d.schedule_id = s.id
AND d.is_deleted = false
JOIN public.companies c
ON c.id = s.company_id
WHERE s.is_deleted = false
AND s.schedule_status = 'active'
AND s.starts_from <= (p_schedule_date::date)
AND (s.end_date IS NULL OR s.end_date >= (p_schedule_date::date))
),
aligned AS (
SELECT
b.schedule_id,
b.bill_header_id,
b.draft_bill_header_id,
b.company_id,
b.organization_id
FROM base b
WHERE CASE lower(b.frequency_cycle)
WHEN 'daily' THEN TRUE
WHEN 'weekly' THEN b.day_of_week IS NOT NULL
AND b.day_of_week = EXTRACT(ISODOW FROM p_schedule_date)::int
WHEN 'monthly' THEN b.day_of_month IS NOT NULL
AND b.day_of_month = EXTRACT(DAY FROM p_schedule_date)::int
WHEN 'yearly' THEN b.month_of_year IS NOT NULL AND b.day_of_month IS NOT NULL
AND b.month_of_year = EXTRACT(MONTH FROM p_schedule_date)::int
AND b.day_of_month = EXTRACT(DAY FROM p_schedule_date)::int
ELSE FALSE
END
)
SELECT DISTINCT -- 👈 this was missing in your version
a.bill_header_id,
a.draft_bill_header_id,
a.company_id,
a.organization_id
FROM aligned a
WHERE NOT EXISTS (
SELECT 1
FROM public.schedule_execution_logs l
WHERE l.schedule_id = a.schedule_id
AND l.execution_date = (p_schedule_date::date)
AND l.is_deleted = false
);
$function$
-- Function: create_vendor_advance_settlements_123
CREATE OR REPLACE FUNCTION public.create_vendor_advance_settlements_123(p_company_id uuid, p_vendor_id uuid, p_bill_payment_header_id uuid, p_created_by uuid, p_settlements jsonb)
RETURNS TABLE(id uuid, advance_settlement_number text)
LANGUAGE plpgsql
AS $function$
DECLARE
v_settlement_number text;
v_row jsonb;
v_id uuid;
BEGIN
FOR v_row IN SELECT * FROM jsonb_array_elements(p_settlements)
LOOP
v_settlement_number := public.get_new_vendor_advance_settlement_number(
p_company_id,
COALESCE((v_row->>'settled_date')::date, CURRENT_DATE)
);
v_id := (v_row->>'id')::uuid;
INSERT INTO vendor_advance_settlements
(
id,
company_id,
vendor_id,
advance_id,
bill_id,
apply_amount,
applied_in_payment_id,
settled_date,
note,
created_on_utc,
created_by,
is_deleted,
advance_settlement_number
)
VALUES
(
v_id,
p_company_id,
p_vendor_id,
(v_row->>'advance_id')::uuid,
(v_row->>'bill_id')::uuid,
(v_row->>'apply_amount')::numeric,
p_bill_payment_header_id,
COALESCE((v_row->>'settled_date')::timestamp, now()),
NULLIF(v_row->>'note',''),
now(),
p_created_by,
false,
v_settlement_number
);
UPDATE vendor_advances va
SET utilized_amount = utilized_amount + (v_row->>'apply_amount')::numeric,
modified_by = p_created_by,
modified_on_utc = now()
WHERE va.id = (v_row->>'advance_id')::uuid
AND va.company_id = p_company_id
AND va.vendor_id = p_vendor_id
AND va.is_deleted = false;
-- Assign to output variables, then RETURN NEXT;
id := v_id;
advance_settlement_number := v_settlement_number;
RETURN NEXT;
END LOOP;
END;
$function$
-- Function: get_all_vendors
CREATE OR REPLACE FUNCTION public.get_all_vendors(p_company_id uuid, p_fin_year_id integer, p_is_get_all boolean)
RETURNS TABLE(id uuid, name character varying, contact_name text, gst_in text, mobile_number text, email text, created_by uuid, created_by_name text, modified_by uuid, modified_by_name text, created_on_utc timestamp without time zone, modified_on_utc timestamp without time zone, billing_address jsonb, contact_count integer, total_billed_amount numeric, total_vendor_paid_amount numeric, total_tds_paid_amount numeric)
LANGUAGE plpgsql
AS $function$
DECLARE
v_organization_id uuid;
v_start_date date := MAKE_DATE(p_fin_year_id, 4, 1);
v_end_date date := MAKE_DATE(p_fin_year_id + 1, 3, 31);
BEGIN
SELECT com.organization_id INTO v_organization_id
FROM public.companies com
WHERE com.id = p_company_id;
RETURN QUERY
SELECT
v.id,
v.name,
con.first_name || ' ' || con.last_name AS contact_name,
v.gstin AS gst_in,
con.mobile_number,
con.email,
v.created_by,
u_created.first_name || ' ' || u_created.last_name AS created_by_name,
v.modified_by,
u_modified.first_name || ' ' || u_modified.last_name AS modified_by_name,
v.created_on_utc,
v.modified_on_utc,
CASE
WHEN ba.id IS NOT NULL THEN jsonb_build_object(
'AddressLine1', ba.address_line1,
'AddressLine2', ba.address_line2,
'ZipCode', ba.zip_code,
'CountryId', ba.country_id,
'StateId', ba.state_id,
'CityId', ba.city_id
)
ELSE NULL
END AS billing_address,
(SELECT COUNT(*)::integer
FROM public.vendor_contacts vc_count
WHERE vc_count.vendor_id = v.id) AS contact_count,
COALESCE(bill_sums.total_billed_amount, 0) AS total_billed_amount,
COALESCE(bill_sums.total_vendor_paid_amount, 0) AS total_vendor_paid_amount,
COALESCE(bill_sums.total_tds_paid_amount, 0) AS total_tds_paid_amount
FROM public.vendors v
JOIN public.vendor_contacts vc ON v.id = vc.vendor_id
JOIN public.contacts con ON vc.contact_id = con.id
LEFT JOIN public.users u_created ON v.created_by = u_created.id
LEFT JOIN public.users u_modified ON v.modified_by = u_modified.id
LEFT JOIN public.addresses ba ON v.billing_address_id = ba.id
LEFT JOIN (
SELECT
b.vendor_id,
SUM(b.total_amount) AS total_billed_amount,
SUM(b.vendor_paid_amount) AS total_vendor_paid_amount,
SUM(b.tds_paid_amount) AS total_tds_paid_amount
FROM public.bill_headers b
WHERE b.company_id = p_company_id
AND b.is_deleted = false
AND b.bill_date >= v_start_date
AND b.bill_date <= v_end_date
GROUP BY b.vendor_id
) bill_sums ON bill_sums.vendor_id = v.id
WHERE v.company_id IN (
SELECT c.id FROM public.companies c
WHERE (p_is_get_all AND c.organization_id = v_organization_id)
OR (NOT p_is_get_all AND c.id = p_company_id)
)
AND v.is_deleted = false
AND con.is_primary = true;
END;
$function$
-- Function: get_bill_analytics
-- SOURCE
CREATE OR REPLACE FUNCTION public.get_bill_analytics(billing_company_id uuid, p_finance_year_id integer, p_bill_type_id integer DEFAULT NULL::integer, 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(status text, count integer, total_amount numeric, month_start_date date)
LANGUAGE plpgsql
AS $function$
DECLARE
v_financial_year_start DATE;
v_financial_year_end DATE;
v_date_start DATE;
v_date_end DATE;
BEGIN
-- Set default financial year range
v_financial_year_start := TO_DATE(p_finance_year_id || '-04-01', 'YYYY-MM-DD');
v_financial_year_end := TO_DATE((p_finance_year_id + 1) || '-03-31', 'YYYY-MM-DD');
-- Use date range override if provided, otherwise use financial year range
v_date_start := COALESCE(p_start_date::DATE, v_financial_year_start);
v_date_end := COALESCE(p_end_date::DATE, v_financial_year_end);
RETURN QUERY
-- PAID
SELECT 'Paid' AS status,
COUNT(*)::INTEGER AS count,
COALESCE(SUM(b.vendor_paid_amount), 0) AS total_amount,
DATE_TRUNC('month', b.bill_date)::DATE AS month_start_date
FROM bill_headers b
WHERE b.company_id = billing_company_id
AND b.bill_status_id IN (4, 5)
AND b.is_deleted = false
AND b.bill_date BETWEEN v_date_start AND v_date_end
AND (p_bill_type_id IS NULL OR b.bill_type_id = p_bill_type_id)
GROUP BY month_start_date
UNION ALL
-- PENDING
SELECT 'Pending' AS status,
COUNT(*)::INTEGER AS count,
COALESCE(SUM(b.total_amount - b.vendor_paid_amount), 0) AS total_amount,
DATE_TRUNC('month', b.bill_date)::DATE AS month_start_date
FROM bill_headers b
WHERE b.company_id = billing_company_id
AND b.bill_status_id IN (3, 4)
AND b.is_deleted = false
AND b.bill_date BETWEEN v_date_start AND v_date_end
AND (p_bill_type_id IS NULL OR b.bill_type_id = p_bill_type_id)
GROUP BY month_start_date
UNION ALL
-- OVERDUE
SELECT 'Overdue' AS status,
COUNT(*)::INTEGER AS count,
COALESCE(SUM(b.total_amount - b.vendor_paid_amount), 0) AS total_amount,
DATE_TRUNC('month', b.bill_date)::DATE AS month_start_date
FROM bill_headers b
WHERE b.company_id = billing_company_id
AND b.bill_status_id IN (3, 4)
AND b.is_deleted = false
AND b.due_date < CURRENT_DATE
AND b.bill_date BETWEEN v_date_start AND v_date_end
AND (p_bill_type_id IS NULL OR b.bill_type_id = p_bill_type_id)
GROUP BY month_start_date;
END;
$function$
-- TARGET
CREATE OR REPLACE FUNCTION public.get_bill_analytics(billing_company_id uuid, p_finance_year_id integer, p_bill_type_id integer DEFAULT NULL::integer, 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(status text, count integer, total_amount numeric, month_start_date date)
LANGUAGE plpgsql
AS $function$
DECLARE
v_financial_year_start DATE;
v_financial_year_end DATE;
v_date_start DATE;
v_date_end DATE;
BEGIN
-- Set default financial year range
v_financial_year_start := TO_DATE(p_finance_year_id || '-04-01', 'YYYY-MM-DD');
v_financial_year_end := TO_DATE((p_finance_year_id + 1) || '-03-31', 'YYYY-MM-DD');
-- Use date range override if provided, otherwise use financial year range
v_date_start := COALESCE(p_start_date::DATE, v_financial_year_start);
v_date_end := COALESCE(p_end_date::DATE, v_financial_year_end);
RETURN QUERY
-- PAID
SELECT 'Paid' AS status,
COUNT(*)::INTEGER AS count,
COALESCE(SUM(b.setteled_amount), 0) AS total_amount,
DATE_TRUNC('month', b.bill_date)::DATE AS month_start_date
FROM bill_headers b
WHERE b.company_id = billing_company_id
AND b.bill_status_id IN (4, 5)
AND b.is_deleted = false
AND b.bill_date BETWEEN v_date_start AND v_date_end
AND (p_bill_type_id IS NULL OR b.bill_type_id = p_bill_type_id)
GROUP BY month_start_date
UNION ALL
-- PENDING
SELECT 'Pending' AS status,
COUNT(*)::INTEGER AS count,
COALESCE(SUM(b.total_amount - b.setteled_amount), 0) AS total_amount,
DATE_TRUNC('month', b.bill_date)::DATE AS month_start_date
FROM bill_headers b
WHERE b.company_id = billing_company_id
AND b.bill_status_id IN (3, 4)
AND b.is_deleted = false
AND b.bill_date BETWEEN v_date_start AND v_date_end
AND (p_bill_type_id IS NULL OR b.bill_type_id = p_bill_type_id)
GROUP BY month_start_date
UNION ALL
-- OVERDUE
SELECT 'Overdue' AS status,
COUNT(*)::INTEGER AS count,
COALESCE(SUM(b.total_amount - b.setteled_amount), 0) AS total_amount,
DATE_TRUNC('month', b.bill_date)::DATE AS month_start_date
FROM bill_headers b
WHERE b.company_id = billing_company_id
AND b.bill_status_id IN (3, 4)
AND b.is_deleted = false
AND b.due_date < CURRENT_DATE
AND b.bill_date BETWEEN v_date_start AND v_date_end
AND (p_bill_type_id IS NULL OR b.bill_type_id = p_bill_type_id)
GROUP BY month_start_date;
END;
$function$
-- Function: get_new_vendor_advance_number
CREATE OR REPLACE FUNCTION public.get_new_vendor_advance_number(p_company_id uuid, p_date date)
RETURNS character varying
LANGUAGE plpgsql
AS $function$
DECLARE
v_finance_year integer;
new_advance_id integer;
p_prefix varchar;
advance_number varchar;
BEGIN
-- Determine the start year of the financial year based on p_date
IF EXTRACT(MONTH FROM p_date) >= 4 THEN
v_finance_year := EXTRACT(YEAR FROM p_date);
ELSE
v_finance_year := EXTRACT(YEAR FROM p_date) - 1;
END IF;
-- Attempt to update existing row or insert if not found
WITH x AS (
UPDATE public.vendor_advance_ids
SET last_advance_id = last_advance_id + 1
WHERE company_id = p_company_id AND fin_year = v_finance_year
RETURNING last_advance_id, advance_prefix
),
insert_x AS (
INSERT INTO public.vendor_advance_ids (
company_id, fin_year, advance_prefix, last_advance_id, advance_length
)
SELECT p_company_id, v_finance_year, 'VADV', 1, 8
WHERE NOT EXISTS (SELECT 1 FROM x)
RETURNING last_advance_id, advance_prefix
)
SELECT
COALESCE((SELECT last_advance_id FROM x LIMIT 1), (SELECT last_advance_id FROM insert_x LIMIT 1)),
COALESCE((SELECT advance_prefix FROM x LIMIT 1), (SELECT advance_prefix FROM insert_x LIMIT 1))
INTO new_advance_id, p_prefix;
-- Construct the final advance number (e.g., VADV0001)
advance_number := p_prefix || LPAD(new_advance_id::text, 4, '0');
RETURN advance_number;
END;
$function$
-- Function: get_all_bill_headers_by_vendor_id
-- SOURCE
CREATE OR REPLACE FUNCTION public.get_all_bill_headers_by_vendor_id(p_vendor_id uuid, p_financial_year integer)
RETURNS TABLE(id uuid, bill_number text, bill_voucher text, debit_account_id uuid, bill_date timestamp without time zone, vendor_id uuid, vendor_name text, due_date timestamp without time zone, payment_term integer, note text, currency_id integer, discount numeric, bill_status_id integer, bill_status text, total_amount numeric, tds_amount numeric, vendor_paid_amount numeric, tds_paid_amount numeric, is_vendor_paid boolean, is_tds_paid boolean, is_closed boolean, total_paid_amount numeric, vendor_net_amount numeric, created_by uuid, created_on_utc timestamp without time zone, modified_by uuid, modified_on_utc timestamp without time zone, bill_type_id integer, current_approval_level integer)
LANGUAGE plpgsql
AS $function$
DECLARE
v_is_tds_vendor boolean;
start_date date := make_date(p_financial_year, 4, 1);
end_date date := make_date(p_financial_year + 1, 3, 31);
v_company_id uuid;
v_org_id uuid;
BEGIN
-- Get is_tds_vendor flag and company_id for the vendor
SELECT v.is_tds_vendor,
v.company_id
INTO v_is_tds_vendor, v_company_id
FROM public.vendors v
WHERE v.id = p_vendor_id;
-- Get organization_id of the company related to the vendor
SELECT c.organization_id
INTO v_org_id
FROM public.companies c
WHERE c.id = v_company_id;
IF NOT v_is_tds_vendor THEN
RETURN QUERY
SELECT
bh.id AS id,
bh.bill_number AS bill_number,
bh.bill_voucher AS bill_voucher,
bh.debit_account_id AS debit_account_id,
bh.bill_date AS bill_date,
v.id AS vendor_id,
v.name::text AS vendor_name,
bh.due_date AS due_date,
bh.payment_term AS payment_term,
bh.note AS note,
bh.currency_id AS currency_id,
bh.discount AS discount,
bs.id AS bill_status_id,
bs.name::text AS bill_status,
bh.total_amount AS total_amount,
COALESCE(bh.tds_amount, 0) AS tds_amount,
COALESCE(bh.vendor_paid_amount, 0) AS vendor_paid_amount,
COALESCE(bh.tds_paid_amount, 0) AS tds_paid_amount,
COALESCE(bh.is_vendor_paid, false) AS is_vendor_paid,
COALESCE(bh.is_tds_paid, false) AS is_tds_paid,
COALESCE(bh.is_closed, false) AS is_closed,
COALESCE(bh.total_paid_amount, 0) AS total_paid_amount,
COALESCE(bh.vendor_net_amount, 0) AS vendor_net_amount,
bh.created_by AS created_by,
bh.created_on_utc AS created_on_utc,
bh.modified_by AS modified_by,
bh.modified_on_utc AS modified_on_utc,
bh.bill_type_id AS bill_type_id,
bh.current_approval_level AS current_approval_level
FROM public.bill_headers bh
INNER JOIN public.bill_statuses bs ON bs.id = bh.bill_status_id
INNER JOIN public.vendors v ON v.id = bh.vendor_id
WHERE bh.vendor_id = p_vendor_id
AND bh.bill_date BETWEEN start_date AND end_date
AND bh.bill_status_id IN (3, 4)
AND bh.is_deleted = false;
--AND bh.is_vendor_paid = false;
ELSE
RETURN QUERY
SELECT
bh.id AS id,
bh.bill_number AS bill_number,
bh.bill_voucher AS bill_voucher,
bh.debit_account_id AS debit_account_id,
bh.bill_date AS bill_date,
v.id AS vendor_id,
v.name::text AS vendor_name,
bh.due_date AS due_date,
bh.payment_term AS payment_term,
bh.note AS note,
bh.currency_id AS currency_id,
bh.discount AS discount,
bs.id AS bill_status_id,
bs.name::text AS bill_status,
bh.total_amount AS total_amount,
COALESCE(bh.tds_amount, 0) AS tds_amount,
COALESCE(bh.vendor_paid_amount, 0) AS vendor_paid_amount,
COALESCE(bh.tds_paid_amount, 0) AS tds_paid_amount,
COALESCE(bh.is_vendor_paid, false) AS is_vendor_paid,
COALESCE(bh.is_tds_paid, false) AS is_tds_paid,
COALESCE(bh.is_closed, false) AS is_closed,
COALESCE(bh.total_paid_amount, 0) AS total_paid_amount,
COALESCE(bh.vendor_net_amount, 0) AS vendor_net_amount,
bh.created_by AS created_by,
bh.created_on_utc AS created_on_utc,
bh.modified_by AS modified_by,
bh.modified_on_utc AS modified_on_utc,
bh.bill_type_id AS bill_type_id,
bh.current_approval_level AS current_approval_level
FROM public.bill_headers bh
INNER JOIN public.bill_statuses bs ON bs.id = bh.bill_status_id
INNER JOIN public.vendors v ON v.id = bh.vendor_id
WHERE bh.bill_date BETWEEN start_date AND end_date
AND bh.bill_status_id IN (3, 4, 5)
AND bh.is_tds_paid = false
AND COALESCE(bh.tds_amount, 0) > 0
AND bh.is_deleted = false
AND bh.company_id IN (
SELECT c.id
FROM public.companies c
WHERE c.organization_id = v_org_id
);
END IF;
END;
$function$
-- TARGET
CREATE OR REPLACE FUNCTION public.get_all_bill_headers_by_vendor_id(p_vendor_id uuid, p_financial_year integer)
RETURNS TABLE(id uuid, bill_number text, bill_voucher text, debit_account_id uuid, bill_date timestamp without time zone, vendor_id uuid, vendor_name text, due_date timestamp without time zone, payment_term integer, note text, currency_id integer, discount numeric, bill_status_id integer, bill_status text, total_amount numeric, tds_amount numeric, vendor_paid_amount numeric, tds_paid_amount numeric, is_vendor_paid boolean, is_tds_paid boolean, is_closed boolean, total_paid_amount numeric, vendor_net_amount numeric, created_by uuid, created_on_utc timestamp without time zone, modified_by uuid, modified_on_utc timestamp without time zone, bill_type_id integer, current_approval_level integer)
LANGUAGE plpgsql
AS $function$
DECLARE
v_is_tds_vendor boolean;
start_date date := make_date(p_financial_year, 4, 1);
end_date date := make_date(p_financial_year + 1, 3, 31);
BEGIN
-- Get is_tds_vendor flag
SELECT vendors.is_tds_vendor INTO v_is_tds_vendor FROM public.vendors WHERE vendors.id = p_vendor_id;
IF NOT v_is_tds_vendor THEN
RETURN QUERY
SELECT
bh.id,
bh.bill_number,
bh.bill_voucher,
bh.debit_account_id,
bh.bill_date,
v.id AS vendor_id,
v.name::text AS vendor_name,
bh.due_date,
bh.payment_term,
bh.note,
bh.currency_id,
bh.discount,
bs.id AS bill_status_id,
bs.name::text AS bill_status,
bh.total_amount,
COALESCE(bh.tds_amount, 0),
COALESCE(bh.vendor_paid_amount, 0),
COALESCE(bh.tds_paid_amount, 0),
COALESCE(bh.is_vendor_paid, false),
COALESCE(bh.is_tds_paid, false),
COALESCE(bh.is_closed, false),
COALESCE(bh.total_paid_amount, 0),
COALESCE(bh.vendor_net_amount, 0),
bh.created_by,
bh.created_on_utc,
bh.modified_by,
bh.modified_on_utc,
bh.bill_type_id,
bh.current_approval_level
FROM public.bill_headers bh
JOIN public.bill_statuses bs ON bs.id = bh.bill_status_id
JOIN public.vendors v ON v.id = bh.vendor_id
WHERE
bh.vendor_id = p_vendor_id
AND (bh.bill_status_id = 3 OR bh.bill_status_id = 4)
AND bh.is_deleted = false;
ELSE
RETURN QUERY
SELECT
bh.id,
bh.bill_number,
bh.bill_voucher,
bh.debit_account_id,
bh.bill_date,
v.id AS vendor_id,
v.name::text AS vendor_name,
bh.due_date,
bh.payment_term,
bh.note,
bh.currency_id,
bh.discount,
bs.id AS bill_status_id,
bs.name::text AS bill_status,
bh.total_amount,
COALESCE(bh.tds_amount, 0),
COALESCE(bh.vendor_paid_amount, 0),
COALESCE(bh.tds_paid_amount, 0),
COALESCE(bh.is_vendor_paid, false),
COALESCE(bh.is_tds_paid, false),
COALESCE(bh.is_closed, false),
COALESCE(bh.total_paid_amount, 0),
COALESCE(bh.vendor_net_amount, 0),
bh.created_by,
bh.created_on_utc,
bh.modified_by,
bh.modified_on_utc,
bh.bill_type_id,
bh.current_approval_level
FROM public.bill_headers bh
JOIN public.bill_statuses bs ON bs.id = bh.bill_status_id
JOIN public.vendors v ON v.id = bh.vendor_id
WHERE
bh.bill_date >= start_date
AND bh.bill_date <= end_date
AND (bh.bill_status_id = 4 OR bh.bill_status_id = 5)
AND COALESCE(bh.tds_amount, 0) > 0
AND bh.is_deleted = false;
END IF;
END;
$function$
-- Function: get_all_vendor_advances
CREATE OR REPLACE FUNCTION public.get_all_vendor_advances(p_company_id uuid, p_finance_year_id integer, p_only_unutilized boolean DEFAULT false)
RETURNS TABLE(id uuid, vendor_id uuid, vendor_name character varying, advance_number text, paid_date timestamp without time zone, amount numeric, utilized_amount numeric, mode_of_payment text, reference text, description text, currency_id integer)
LANGUAGE plpgsql
AS $function$
DECLARE
v_financial_year_start date;
v_financial_year_end date;
BEGIN
-- Start of financial year: April 1 of the given year
v_financial_year_start := TO_DATE(p_finance_year_id || '-04-01', 'YYYY-MM-DD');
-- End of financial year: March 31 of the next year
v_financial_year_end := TO_DATE((p_finance_year_id + 1) || '-03-31', 'YYYY-MM-DD');
RETURN QUERY
SELECT
va.id,
va.vendor_id,
v.name, -- Add vendor name
va.advance_number,
va.paid_date,
va.amount,
va.utilized_amount,
va.mode_of_payment,
va.reference,
va.description,
va.currency_id
FROM public.vendor_advances va
LEFT JOIN public.vendors v ON va.vendor_id = v.id -- Join here to get the name
WHERE va.company_id = p_company_id
AND va.is_deleted = false
AND v.is_deleted = false
AND va.paid_date >= v_financial_year_start
AND va.paid_date <= v_financial_year_end
AND (
(p_only_unutilized = true AND va.amount > va.utilized_amount)
OR (p_only_unutilized IS DISTINCT FROM true)
);
END;
$function$
-- Function: get_new_vendor_advance_settlement_number
CREATE OR REPLACE FUNCTION public.get_new_vendor_advance_settlement_number(p_company_id uuid, p_date date)
RETURNS character varying
LANGUAGE plpgsql
AS $function$
DECLARE
v_finance_year int;
new_id int;
v_prefix text := 'ADV'; -- 👈 default prefix
v_length int := 4; -- 👈 default length
number varchar;
BEGIN
-- derive financial year
IF EXTRACT(MONTH FROM p_date) >= 4 THEN
v_finance_year := EXTRACT(YEAR FROM p_date);
ELSE
v_finance_year := EXTRACT(YEAR FROM p_date) - 1;
END IF;
-- upsert into vendor_advance_settlement_ids
WITH upd AS (
UPDATE public.vendor_advance_settlement_ids
SET last_settlement_id = last_settlement_id + 1
WHERE company_id = p_company_id AND fin_year = v_finance_year
RETURNING last_settlement_id, settlement_prefix, settlement_length
), ins AS (
INSERT INTO public.vendor_advance_settlement_ids(
company_id, fin_year, settlement_prefix, settlement_length, last_settlement_id
)
SELECT p_company_id, v_finance_year, v_prefix, v_length, 1
WHERE NOT EXISTS (SELECT 1 FROM upd)
RETURNING last_settlement_id, settlement_prefix, settlement_length
)
SELECT COALESCE(u.last_settlement_id, i.last_settlement_id),
COALESCE(u.settlement_prefix, i.settlement_prefix),
COALESCE(u.settlement_length, i.settlement_length)
INTO new_id, v_prefix, v_length
FROM upd u
FULL JOIN ins i ON true;
-- build formatted settlement number
number := v_prefix || LPAD(new_id::text, v_length, '0');
RETURN number;
END;
$function$
-- Function: get_tds_bills_from_span
-- SOURCE
CREATE OR REPLACE FUNCTION public.get_tds_bills_from_span(p_company_id uuid, p_fin_year_id integer, p_user_id uuid, p_bill_type_id integer DEFAULT NULL::integer, 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, bill_voucher text, bill_number text, vendor_name character varying, vendor_id uuid, due_date date, bill_date date, payment_term integer, total_amount numeric, total_paid_amount numeric, currency_id integer, bill_status character varying, bill_status_id integer, created_by uuid, created_on_utc timestamp without time zone, modified_by uuid, modified_on_utc timestamp without time zone, created_by_name character varying, modified_by_name character varying, bill_type character varying, bill_type_id integer, tds_amount numeric, has_next_status_approval boolean, next_status_id integer)
LANGUAGE plpgsql
AS $function$
DECLARE
v_start_date date := COALESCE(p_start_date::date, MAKE_DATE(p_fin_year_id, 4, 1));
v_end_date date := COALESCE(p_end_date::date, MAKE_DATE(p_fin_year_id + 1, 3, 31));
DRAFT_STATUS CONSTANT integer := 1;
BEGIN
RETURN QUERY
SELECT
bh.id,
bh.bill_voucher,
bh.bill_number,
v.name AS vendor_name,
bh.vendor_id,
bh.due_date::date,
bh.bill_date::date,
bh.payment_term,
bh.total_amount,
bh.total_paid_amount,
bh.currency_id,
bs.name::varchar AS bill_status,
bh.bill_status_id,
bh.created_by,
bh.created_on_utc,
bh.modified_by,
bh.modified_on_utc,
CONCAT(cu.first_name, ' ', cu.last_name)::varchar AS created_by_name,
CONCAT(mu.first_name, ' ', mu.last_name)::varchar AS modified_by_name,
bt.name::varchar AS bill_type,
bh.bill_type_id,
bh.tds_amount,
CASE
WHEN bh.bill_status_id = DRAFT_STATUS THEN true
WHEN EXISTS (
SELECT 1
FROM bill_approval_user_account a
WHERE a.account_id = bh.debit_account_id
AND a.status_id = bh.bill_status_id
AND a.user_id = p_user_id
AND a.is_deleted = false
) THEN true
WHEN EXISTS (
SELECT 1
FROM bill_approval_user_company c
WHERE c.company_id = p_company_id
AND c.status_id = bh.bill_status_id
AND c.user_id = p_user_id
AND c.is_deleted = false
) THEN true
ELSE false
END AS has_next_status_approval,
COALESCE(
(SELECT bw.next_status
FROM bill_workflow bw
WHERE bw.company_id = p_company_id
AND bw.status = bh.bill_status_id
AND bw.is_deleted = false
AND (bw.is_enabled IS DISTINCT FROM FALSE)
ORDER BY bw.approval_level ASC NULLS LAST
LIMIT 1),
0
) AS next_status_id
FROM public.bill_headers bh
LEFT JOIN public.vendors v ON v.id = bh.vendor_id
LEFT JOIN public.bill_statuses bs ON bs.id = bh.bill_status_id
LEFT JOIN public.bill_types bt ON bt.id = bh.bill_type_id
LEFT JOIN users cu ON cu.id = bh.created_by
LEFT JOIN users mu ON mu.id = bh.modified_by
WHERE bh.company_id = p_company_id
AND bh.is_deleted = false
AND bh.tds_amount IS NOT NULL
AND bh.tds_amount > 0
AND (p_bill_type_id IS NULL OR p_bill_type_id = 0 OR bh.bill_type_id = p_bill_type_id)
AND bh.bill_date BETWEEN v_start_date AND v_end_date;
END;
$function$
-- TARGET
CREATE OR REPLACE FUNCTION public.get_tds_bills_from_span(p_company_id uuid, p_fin_year_id integer, p_user_id uuid, p_bill_type_id integer DEFAULT NULL::integer, 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, bill_voucher text, bill_number text, vendor_name character varying, vendor_id uuid, due_date date, bill_date date, payment_term integer, total_amount numeric, currency_id integer, bill_status character varying, bill_status_id integer, created_by uuid, created_on_utc timestamp without time zone, modified_by uuid, modified_on_utc timestamp without time zone, created_by_name character varying, modified_by_name character varying, bill_type character varying, bill_type_id integer, tds_amount numeric, has_next_status_approval boolean, next_status_id integer)
LANGUAGE plpgsql
AS $function$
DECLARE
v_start_date date := COALESCE(p_start_date::date, MAKE_DATE(p_fin_year_id, 4, 1));
v_end_date date := COALESCE(p_end_date::date, MAKE_DATE(p_fin_year_id + 1, 3, 31));
DRAFT_STATUS CONSTANT integer := 1;
BEGIN
RETURN QUERY
SELECT
bh.id,
bh.bill_voucher,
bh.bill_number,
v.name AS vendor_name,
bh.vendor_id,
bh.due_date::date,
bh.bill_date::date,
bh.payment_term,
bh.total_amount,
bh.currency_id,
bs.name::varchar AS bill_status, -- ensure varchar
bh.bill_status_id,
bh.created_by,
bh.created_on_utc,
bh.modified_by,
bh.modified_on_utc,
CONCAT(cu.first_name, ' ', cu.last_name)::varchar AS created_by_name,
CONCAT(mu.first_name, ' ', mu.last_name)::varchar AS modified_by_name,
bt.name::varchar AS bill_type, -- ✅ cast to varchar
bh.bill_type_id,
bh.tds_amount,
CASE
WHEN bh.bill_status_id = DRAFT_STATUS THEN true
WHEN EXISTS (
SELECT 1
FROM bill_approval_user_account a
WHERE a.account_id = bh.debit_account_id
AND a.status_id = bh.bill_status_id
AND a.user_id = p_user_id
AND a.is_deleted = false
) THEN true
WHEN EXISTS (
SELECT 1
FROM bill_approval_user_company c
WHERE c.company_id = p_company_id
AND c.status_id = bh.bill_status_id
AND c.user_id = p_user_id
AND c.is_deleted = false
) THEN true
ELSE false
END AS has_next_status_approval,
COALESCE(
(SELECT bw.next_status
FROM bill_workflow bw
WHERE bw.company_id = p_company_id
AND bw.status = bh.bill_status_id
AND bw.is_deleted = false
LIMIT 1),
0
) AS next_status_id
FROM public.bill_headers bh
LEFT JOIN public.vendors v ON v.id = bh.vendor_id
LEFT JOIN public.bill_statuses bs ON bs.id = bh.bill_status_id
LEFT JOIN public.bill_types bt ON bt.id = bh.bill_type_id
LEFT JOIN public.users cu ON cu.id = bh.created_by
LEFT JOIN public.users mu ON mu.id = bh.modified_by
WHERE bh.company_id = p_company_id
AND bh.is_deleted = false
AND bh.tds_amount IS NOT NULL -- ✅ only bills with TDS
AND bh.tds_amount > 0 -- ✅ enforce positive TDS
AND (p_bill_type_id IS NULL OR p_bill_type_id = 0 OR bh.bill_type_id = p_bill_type_id)
AND bh.bill_date BETWEEN v_start_date AND v_end_date;
END;
$function$
-- Procedure: create_bill_payment
-- SOURCE
CREATE OR REPLACE PROCEDURE public.create_bill_payment(IN p_bill_payment_header_id uuid, IN p_company_id uuid, IN p_vendor_id uuid, IN p_paid_date date, IN p_mode_of_payment text, IN p_credit_account_id uuid, IN p_debit_account_id uuid, IN p_reference text, IN p_paid_amount numeric, IN p_grand_total_amount numeric, IN p_advance_amount numeric, IN p_description text, IN p_tds_amount numeric, IN p_created_by uuid, IN p_bill_payment_details jsonb)
LANGUAGE plpgsql
AS $procedure$
DECLARE
v_bill_status_id INT;
v_bill_header_id UUID;
v_payment_amount NUMERIC;
v_tds_amount NUMERIC;
v_serial_number INT;
v_row JSONB;
v_payment_number character varying;
v_is_tds_vendor bool;
v_is_tds_paid bool;
v_bill_total_amount NUMERIC;
v_bill_tds_amount NUMERIC;
v_vendor_paid_amount NUMERIC;
v_tds_paid_amount NUMERIC;
v_tds_status_id INT;
v_payment_status_id INT;
v_vendor_net_amount NUMERIC;
BEGIN
-- Generate new payment number
v_payment_number := get_new_payment_number(p_company_id, p_paid_date);
-- Check if vendor is TDS vendor
SELECT is_tds_vendor
INTO v_is_tds_vendor
FROM vendors
WHERE id = p_vendor_id;
-- Insert payment header
INSERT INTO public.bill_payment_headers (
id,
company_id,
vendor_id,
paid_date,
mode_of_payment,
credit_account_id,
debit_account_id,
reference,
paid_amount,
description,
tds_amount,
advance_amount,
grand_total_amount,
payment_number,
created_by,
created_on_utc,
is_deleted
)
VALUES (
p_bill_payment_header_id,
p_company_id,
p_vendor_id,
p_paid_date,
p_mode_of_payment,
p_credit_account_id,
p_debit_account_id,
p_reference,
p_paid_amount,
p_description,
p_tds_amount,
p_advance_amount,
p_grand_total_amount,
v_payment_number,
p_created_by,
now(),
false
);
-- Loop through each bill payment detail record
FOR v_row IN SELECT * FROM jsonb_array_elements(p_bill_payment_details)
LOOP
v_bill_header_id := (v_row->>'header_id')::UUID;
v_payment_amount := COALESCE((v_row->>'payment_amount')::NUMERIC, 0);
v_tds_amount := COALESCE((v_row->>'tds_amount')::NUMERIC, 0);
v_serial_number := (v_row->>'serial_number')::INT;
v_is_tds_paid := COALESCE((v_row->>'is_tds_paid')::BOOLEAN, false);
-- Fetch current amounts from bill header
SELECT vendor_paid_amount, total_amount, tds_amount, tds_paid_amount, vendor_net_amount
INTO v_vendor_paid_amount, v_bill_total_amount, v_bill_tds_amount, v_tds_paid_amount, v_vendor_net_amount
FROM public.bill_headers
WHERE id = v_bill_header_id;
-- Insert payment details record
INSERT INTO public.bill_payment_details (
id,
bill_payment_header_id,
bill_header_id,
paid_amount,
tds_amount,
serial_number,
created_by,
created_on_utc
)
VALUES (
(v_row->>'id')::uuid,
p_bill_payment_header_id,
v_bill_header_id,
v_payment_amount,
v_tds_amount,
v_serial_number,
p_created_by,
now()
);
-- Update paid amounts based on payment type
IF v_is_tds_vendor OR v_is_tds_paid THEN
v_tds_paid_amount := COALESCE(v_tds_paid_amount, 0) + v_payment_amount;
ELSE
v_vendor_paid_amount := COALESCE(v_vendor_paid_amount, 0) + v_payment_amount;
END IF;
-- Status calculations using updated paid amounts (after applying current payment)
-- Bill payment status
IF (v_vendor_paid_amount >= (v_bill_total_amount - v_bill_tds_amount))
AND (v_tds_paid_amount >= v_bill_tds_amount) THEN
v_bill_status_id := 5; -- PAID
ELSE
v_bill_status_id := 4; -- PARTIALLY_PAID
END IF;
-- TDS status for TDS vendors
IF v_is_tds_vendor THEN
IF v_tds_paid_amount = 0 AND v_bill_tds_amount > 0 THEN
v_tds_status_id := 2; -- Unpaid
ELSIF v_tds_paid_amount > 0 AND v_tds_paid_amount = v_bill_tds_amount THEN
v_tds_status_id := 3; -- Deducted
ELSE
v_tds_status_id := 1; -- N/A fallback
END IF;
ELSE
v_tds_status_id := 1; -- N/A for non-TDS vendors
END IF;
-- Payment status assignment (now covers both TDS and non-TDS vendors)
IF NOT v_is_tds_vendor THEN
IF v_vendor_net_amount > 0 AND v_vendor_paid_amount = 0 THEN
v_payment_status_id := 1; -- Unpaid
ELSIF v_vendor_net_amount > 0 AND v_vendor_paid_amount > 0 AND v_vendor_paid_amount < v_vendor_net_amount THEN
v_payment_status_id := 2; -- PartiallyPaid
ELSIF v_vendor_net_amount = v_vendor_paid_amount THEN
v_payment_status_id := 3; -- FullyPaid
ELSE
v_payment_status_id := 1; -- Default Unpaid fallback
END IF;
ELSE
-- TDS vendors
IF v_vendor_net_amount = v_vendor_paid_amount THEN
v_payment_status_id := 3; -- FullyPaid
ELSIF v_vendor_paid_amount > 0 THEN
v_payment_status_id := 2; -- PartiallyPaid
ELSE
v_payment_status_id := 1; -- Unpaid
END IF;
END IF;
-- Final safeguard to avoid nulls
v_payment_status_id := COALESCE(v_payment_status_id, 1);
-- Update bill header with new payment and status details
UPDATE public.bill_headers
SET vendor_paid_amount = v_vendor_paid_amount,
tds_paid_amount = v_tds_paid_amount,
bill_status_id = v_bill_status_id,
tds_status_id = CASE
WHEN v_is_tds_vendor THEN v_tds_status_id
ELSE 1 -- N/A for non-TDS vendors
END,
payment_status_id = CASE
WHEN v_is_tds_vendor THEN payment_status_id -- leave unchanged for TDS vendors
ELSE v_payment_status_id -- update for non-TDS vendors
END
WHERE id = v_bill_header_id;
END LOOP;
RAISE NOTICE 'Bill payment processed successfully';
END;
$procedure$
-- TARGET
CREATE OR REPLACE PROCEDURE public.create_bill_payment(IN p_bill_payment_header_id uuid, IN p_company_id uuid, IN p_vendor_id uuid, IN p_paid_date date, IN p_mode_of_payment text, IN p_credit_account_id uuid, IN p_debit_account_id uuid, IN p_reference text, IN p_paid_amount numeric, IN p_grand_total_amount numeric, IN p_advance_amount numeric, IN p_description text, IN p_tds_amount numeric, IN p_created_by uuid, IN p_bill_payment_details jsonb)
LANGUAGE plpgsql
AS $procedure$
DECLARE
v_bill_status_id INT;
v_bill_header_id UUID;
v_payment_amount NUMERIC;
v_tds_amount NUMERIC;
v_serial_number INT;
v_row JSONB;
v_payment_number character varying;
v_is_tds_vendor bool;
v_bill_total_amount NUMERIC;
v_bill_tds_amount NUMERIC;
v_vendor_paid_amount NUMERIC;
v_tds_paid_amount NUMERIC;
BEGIN
-- Generate new payment number
v_payment_number := get_new_payment_number(p_company_id, p_paid_date);
-- Check if vendor is TDS vendor
SELECT is_tds_vendor
INTO v_is_tds_vendor
FROM vendors
WHERE id = p_vendor_id;
-- Insert payment header
INSERT INTO public.bill_payment_headers (
id,
company_id,
vendor_id,
paid_date,
mode_of_payment,
credit_account_id,
debit_account_id,
reference,
paid_amount,
description,
tds_amount,
advance_amount,
grand_total_amount,
payment_number,
created_by,
created_on_utc,
is_deleted
)
VALUES (
p_bill_payment_header_id,
p_company_id,
p_vendor_id,
p_paid_date,
p_mode_of_payment,
p_credit_account_id,
p_debit_account_id,
p_reference,
p_paid_amount,
p_description,
p_tds_amount,
p_advance_amount,
p_grand_total_amount,
v_payment_number,
p_created_by,
now(),
false
);
-- Loop through each bill payment detail record
FOR v_row IN SELECT * FROM jsonb_array_elements(p_bill_payment_details)
LOOP
v_bill_header_id := (v_row->>'header_id')::UUID;
v_payment_amount := COALESCE((v_row->>'payment_amount')::NUMERIC, 0);
v_tds_amount := COALESCE((v_row->>'tds_amount')::NUMERIC, 0);
v_serial_number := (v_row->>'serial_number')::INT;
-- Fetch current paid amounts and bill totals
SELECT vendor_paid_amount, total_amount, tds_amount, tds_paid_amount
INTO v_vendor_paid_amount, v_bill_total_amount, v_bill_tds_amount, v_tds_paid_amount
FROM public.bill_headers
WHERE id = v_bill_header_id;
-- Insert payment details record
INSERT INTO public.bill_payment_details (
id,
bill_payment_header_id,
bill_header_id,
paid_amount,
tds_amount,
serial_number,
created_by,
created_on_utc
)
VALUES (
(v_row->>'id')::uuid,
p_bill_payment_header_id,
v_bill_header_id,
v_payment_amount,
v_tds_amount,
v_serial_number,
p_created_by,
now()
);
-- Payment logic based on vendor type
IF v_is_tds_vendor THEN
-- TDS payment to government: update tds_paid_amount only
v_tds_paid_amount := COALESCE(v_tds_paid_amount, 0) + v_payment_amount;
ELSE
-- Vendor payment: update vendor_paid_amount only
v_vendor_paid_amount := COALESCE(v_vendor_paid_amount, 0) + v_payment_amount;
END IF;
-- Bill status calculation
IF (v_vendor_paid_amount >= (v_bill_total_amount - v_bill_tds_amount))
AND (v_tds_paid_amount >= v_bill_tds_amount) THEN
v_bill_status_id := 5; -- PAID
ELSE
v_bill_status_id := 4; -- PARTIALLY_PAID
END IF;
-- Update bill header with new paid info
UPDATE public.bill_headers
SET vendor_paid_amount = v_vendor_paid_amount,
tds_paid_amount = v_tds_paid_amount,
bill_status_id = v_bill_status_id
WHERE id = v_bill_header_id;
END LOOP;
RAISE NOTICE 'Bill payment processed successfully';
END;
$procedure$
-- Procedure: create_multiple_bill_payments
CREATE OR REPLACE PROCEDURE public.create_multiple_bill_payments(IN p_bill_payments jsonb)
LANGUAGE plpgsql
AS $procedure$
DECLARE
bill_payment RECORD;
payment_statuses TEXT[] := ARRAY[]::TEXT[];
BEGIN
FOR bill_payment IN
SELECT * FROM jsonb_to_recordset(p_bill_payments) AS (
bill_payment_header_id uuid,
company_id uuid,
vendor_id uuid,
paid_date date,
paid_amount numeric,
grand_total_amount numeric,
tds_amount numeric,
advance_amount numeric,
description text,
debit_account_id uuid,
credit_account_id uuid,
mode_of_payment text,
reference text,
created_by uuid,
detail_lines jsonb
)
LOOP
BEGIN
IF NOT EXISTS (
SELECT 1
FROM public.bill_payment_headers
WHERE reference = bill_payment.reference
AND company_id = bill_payment.company_id
) THEN
CALL public.create_bill_payment(
bill_payment.bill_payment_header_id,
bill_payment.company_id,
bill_payment.vendor_id,
bill_payment.paid_date,
bill_payment.mode_of_payment,
bill_payment.credit_account_id,
bill_payment.debit_account_id,
bill_payment.reference,
bill_payment.paid_amount,
bill_payment.grand_total_amount,
bill_payment.advance_amount,
bill_payment.description,
bill_payment.tds_amount,
bill_payment.created_by,
bill_payment.detail_lines
);
payment_statuses := payment_statuses || format('%s:success', bill_payment.bill_payment_header_id);
ELSE
RAISE NOTICE 'Duplicate bill payment found for reference: %, company_id: %. Skipping.', bill_payment.reference, bill_payment.company_id;
payment_statuses := payment_statuses || format('%s:duplicate', bill_payment.bill_payment_header_id);
CONTINUE;
END IF;
EXCEPTION
WHEN OTHERS THEN
RAISE NOTICE 'An error occurred while processing bill payment ID: %, Error: %', bill_payment.bill_payment_header_id, SQLERRM;
payment_statuses := payment_statuses || format('%s:failed', bill_payment.bill_payment_header_id);
END;
END LOOP;
RAISE NOTICE 'BILL_PAYMENT_STATUS:%', to_json(payment_statuses);
RAISE NOTICE 'All bill payments processed.';
END;
$procedure$
-- Procedure: create_vendor_advance
CREATE OR REPLACE PROCEDURE public.create_vendor_advance(IN p_vendor_advance_id uuid, IN p_company_id uuid, IN p_vendor_id uuid, IN p_paid_date date, IN p_mode_of_payment text, IN p_through_account_id uuid, IN p_reference text, IN p_amount numeric, IN p_description text, IN p_currency_id integer, IN p_created_by uuid)
LANGUAGE plpgsql
AS $procedure$
DECLARE
v_advance_number varchar;
BEGIN
-- Generate a new vendor advance number for this company & date
v_advance_number := public.get_new_vendor_advance_number(p_company_id, p_paid_date);
-- Insert into vendor_advances
INSERT INTO vendor_advances
(
id,
company_id,
vendor_id,
advance_number,
paid_date,
amount,
mode_of_payment,
through_account_id,
reference,
description,
currency_id,
utilized_amount,
created_on_utc,
created_by
)
VALUES
(
p_vendor_advance_id,
p_company_id,
p_vendor_id,
v_advance_number,
p_paid_date,
p_amount,
p_mode_of_payment,
p_through_account_id,
NULLIF(p_reference, ''),
NULLIF(p_description, ''),
p_currency_id,
0.0, -- initialize utilized_amount
now(),
p_created_by
);
END;
$procedure$
-- Procedure: update_account_level_approval_configuration
-- SOURCE
CREATE OR REPLACE PROCEDURE public.update_account_level_approval_configuration(IN p_company_id uuid, IN p_account_id uuid, IN p_status_id integer, IN p_required_approval_levels integer, IN p_approvals jsonb, IN p_modified_by uuid)
LANGUAGE plpgsql
AS $procedure$
DECLARE
approval_item JSONB;
existing_id INT;
v_status_id INT;
BEGIN
-- 1️⃣ Soft delete old records not present in incoming approvals:
UPDATE bill_approval_user_account
SET is_deleted = TRUE,
deleted_on_utc = NOW(),
modified_on_utc = NOW(),
modified_by = p_modified_by
WHERE company_id = p_company_id
AND account_id = p_account_id
AND NOT EXISTS (
SELECT 1
FROM jsonb_array_elements(p_approvals) AS elem
WHERE (elem->>'userId')::UUID = bill_approval_user_account.user_id
AND (elem->>'approvalLevel')::INT = bill_approval_user_account.approval_level
AND (elem->>'statusId')::INT = bill_approval_user_account.status_id
);
-- 2️⃣ Upsert incoming approvals:
FOR approval_item IN SELECT * FROM jsonb_array_elements(p_approvals)
LOOP
SELECT id INTO existing_id
FROM bill_approval_user_account
WHERE company_id = p_company_id
AND account_id = p_account_id
AND user_id = (approval_item->>'userId')::UUID
AND approval_level = (approval_item->>'approvalLevel')::INT
AND status_id = (approval_item->>'statusId')::INT
AND is_deleted = FALSE
LIMIT 1;
IF existing_id IS NOT NULL THEN
-- -- UPDATE bill_approval_user_account
-- SET status_id = (approval_item->>'statusId')::INT,
-- modified_on_utc = NOW(),
-- modified_by = p_modified_by
-- WHERE id = existing_id;
RAISE NOTICE 'existing IDs: %', existing_id;
ELSE
INSERT INTO bill_approval_user_account (
company_id,
account_id,
status_id,
user_id,
approval_level,
is_deleted,
created_on_utc,
created_by
)
VALUES (
p_company_id,
p_account_id,
(approval_item->>'statusId')::INT,
(approval_item->>'userId')::UUID,
(approval_item->>'approvalLevel')::INT,
FALSE,
NOW(),
p_modified_by
);
END IF;
END LOOP;
-- 3️⃣ Upsert bill_account_approval_levels:
IF EXISTS (
SELECT 1
FROM bill_account_approval_levels
WHERE company_id = p_company_id
AND account_id = p_account_id
AND status_id = 2 -- temparary updating only for status 2
) THEN
UPDATE bill_account_approval_levels
SET approval_level_required = p_required_approval_levels,
status_id = 2,
modified_on_utc = NOW(),
modified_by = p_modified_by
WHERE company_id = p_company_id
AND account_id = p_account_id
AND status_id = 2; -- temparary checking for only status 2
ELSE
INSERT INTO bill_account_approval_levels (
company_id,
account_id,
status_id,
approval_level_required,
created_on_utc,
created_by
)
VALUES (
p_company_id,
p_account_id,
2,
p_required_approval_levels,
NOW(),
p_modified_by
);
END IF;
END;
$procedure$
-- TARGET
CREATE OR REPLACE PROCEDURE public.update_account_level_approval_configuration(IN p_company_id uuid, IN p_account_id uuid, IN p_status_id integer, IN p_required_approval_levels integer, IN p_approvals jsonb, IN p_modified_by uuid)
LANGUAGE plpgsql
AS $procedure$
DECLARE
approval_item JSONB;
existing_id INT;
BEGIN
-- 1️⃣ Soft delete old records not present in incoming approvals:
UPDATE bill_approval_user_account
SET is_deleted = TRUE,
deleted_on_utc = NOW(),
modified_on_utc = NOW(),
modified_by = p_modified_by
WHERE company_id = p_company_id
AND account_id = p_account_id
AND NOT EXISTS (
SELECT 1
FROM jsonb_array_elements(p_approvals) AS elem
WHERE (elem->>'userId')::UUID = bill_approval_user_account.user_id
AND (elem->>'approvalLevel')::INT = bill_approval_user_account.approval_level
);
-- 2️⃣ Upsert incoming approvals:
FOR approval_item IN SELECT * FROM jsonb_array_elements(p_approvals)
LOOP
SELECT id INTO existing_id
FROM bill_approval_user_account
WHERE company_id = p_company_id
AND account_id = p_account_id
AND user_id = (approval_item->>'userId')::UUID
AND approval_level = (approval_item->>'approvalLevel')::INT
AND is_deleted = FALSE
LIMIT 1;
IF existing_id IS NOT NULL THEN
UPDATE bill_approval_user_account
SET status_id = (approval_item->>'statusId')::INT,
modified_on_utc = NOW(),
modified_by = p_modified_by
WHERE id = existing_id;
ELSE
INSERT INTO bill_approval_user_account (
company_id,
account_id,
status_id,
user_id,
approval_level,
is_deleted,
created_on_utc,
created_by
)
VALUES (
p_company_id,
p_account_id,
(approval_item->>'statusId')::INT,
(approval_item->>'userId')::UUID,
(approval_item->>'approvalLevel')::INT,
FALSE,
NOW(),
p_modified_by
);
END IF;
END LOOP;
-- 3️⃣ Upsert bill_account_approval_levels:
IF EXISTS (
SELECT 1
FROM bill_account_approval_levels
WHERE company_id = p_company_id
AND account_id = p_account_id
AND status_id = 2 -- temparary updating only for status 2
) THEN
UPDATE bill_account_approval_levels
SET approval_level_required = p_required_approval_levels,
status_id = 2,
modified_on_utc = NOW(),
modified_by = p_modified_by
WHERE company_id = p_company_id
AND account_id = p_account_id;
-- AND status_id = p_status_id; temparary checking for only status 2
ELSE
INSERT INTO bill_account_approval_levels (
company_id,
account_id,
status_id,
approval_level_required,
created_on_utc,
created_by
)
VALUES (
p_company_id,
p_account_id,
2,
p_required_approval_levels,
NOW(),
p_modified_by
);
END IF;
END;
$procedure$
-- Procedure: transfer_draft_to_bill
-- SOURCE
CREATE OR REPLACE PROCEDURE public.transfer_draft_to_bill(IN p_draft_bill_id uuid, IN p_modified_by uuid)
LANGUAGE plpgsql
AS $procedure$
DECLARE
draft_bill RECORD;
draft_bill_details RECORD;
BEGIN
-- Fetch the draft bill header
SELECT * INTO draft_bill
FROM public.draft_bill_headers
WHERE id = p_draft_bill_id;
IF draft_bill IS NULL THEN
RAISE EXCEPTION 'Draft bill not found: %', p_draft_bill_id;
END IF;
-- Call to create bill header using the procedure
CALL public.create_bill_header(
draft_bill.id,
draft_bill.company_id,
draft_bill.vendor_id,
draft_bill.credit_account_id,
draft_bill.debit_account_id,
draft_bill.bill_date::date,
draft_bill.due_date::date,
draft_bill.payment_term,
draft_bill.taxable_amount,
draft_bill.cgst_amount,
draft_bill.sgst_amount,
draft_bill.igst_amount,
draft_bill.tds_amount,
draft_bill.total_amount,
draft_bill.discount,
draft_bill.fees,
draft_bill.round_off,
draft_bill.round_off_tds,
draft_bill.currency_id,
draft_bill.note,
draft_bill.bill_status_id,
draft_bill.created_by,
draft_bill.source_warehouse_id,
draft_bill.destination_warehouse_id,
draft_bill.bill_type_id,
draft_bill.bill_number,
draft_bill.po_no,
draft_bill.po_date::date
);
-- Raise a notice to indicate success
RAISE NOTICE 'Bill header inserted with ID: %', draft_bill.id;
-- Fetch and insert bill details
FOR draft_bill_details IN
SELECT * FROM public.draft_bill_details
WHERE bill_header_id = p_draft_bill_id
LOOP
INSERT INTO public.bill_details(
id, bill_header_id, product_id, price, quantity, fees, discount,
taxable_amount, sgst_amount, cgst_amount, igst_amount, total_amount, serial_number)
VALUES(
draft_bill_details.id, draft_bill_details.bill_header_id,
draft_bill_details.product_id, draft_bill_details.price,
draft_bill_details.quantity, draft_bill_details.fees,
draft_bill_details.discount, draft_bill_details.taxable_amount,
draft_bill_details.sgst_amount, draft_bill_details.cgst_amount,
draft_bill_details.igst_amount, draft_bill_details.total_amount,
draft_bill_details.serial_number
);
UPDATE public.draft_bill_details
SET
is_deleted = TRUE,
deleted_on_utc = now()
WHERE bill_header_id = p_draft_bill_id;
END LOOP;
-- Mark the draft bill as deleted
UPDATE public.draft_bill_headers
SET
is_deleted = TRUE,
deleted_on_utc = now()
WHERE id = p_draft_bill_id;
END;
$procedure$
-- TARGET
CREATE OR REPLACE PROCEDURE public.transfer_draft_to_bill(IN p_draft_bill_id uuid, IN p_modified_by uuid)
LANGUAGE plpgsql
AS $procedure$
DECLARE
draft_bill RECORD;
draft_bill_details RECORD;
BEGIN
-- Fetch the draft bill header
SELECT * INTO draft_bill
FROM public.draft_bill_headers
WHERE id = p_draft_bill_id;
IF draft_bill IS NULL THEN
RAISE EXCEPTION 'Draft bill not found: %', p_draft_bill_id;
END IF;
-- Call to create bill header using the procedure
CALL public.create_bill_header(
draft_bill.id,
draft_bill.company_id,
draft_bill.vendor_id,
draft_bill.credit_account_id,
draft_bill.debit_account_id,
draft_bill.bill_date::date,
draft_bill.due_date::date,
draft_bill.payment_term,
draft_bill.taxable_amount,
draft_bill.cgst_amount,
draft_bill.sgst_amount,
draft_bill.igst_amount,
draft_bill.tds_amount,
draft_bill.total_amount,
draft_bill.discount,
draft_bill.fees,
draft_bill.round_off,
draft_bill.currency_id,
draft_bill.note,
draft_bill.bill_status_id,
draft_bill.created_by,
draft_bill.source_warehouse_id,
draft_bill.destination_warehouse_id,
draft_bill.bill_type_id,
draft_bill.bill_number,
draft_bill.po_no,
draft_bill.po_date::date
);
-- Raise a notice to indicate success
RAISE NOTICE 'Bill header inserted with ID: %', draft_bill.id;
-- Fetch and insert bill details
FOR draft_bill_details IN
SELECT * FROM public.draft_bill_details
WHERE bill_header_id = p_draft_bill_id
LOOP
INSERT INTO public.bill_details(
id, bill_header_id, product_id, price, quantity, fees, discount,
taxable_amount, sgst_amount, cgst_amount, igst_amount, total_amount, serial_number)
VALUES(
draft_bill_details.id, draft_bill_details.bill_header_id,
draft_bill_details.product_id, draft_bill_details.price,
draft_bill_details.quantity, draft_bill_details.fees,
draft_bill_details.discount, draft_bill_details.taxable_amount,
draft_bill_details.sgst_amount, draft_bill_details.cgst_amount,
draft_bill_details.igst_amount, draft_bill_details.total_amount,
draft_bill_details.serial_number
);
UPDATE public.draft_bill_details
SET
is_deleted = TRUE,
deleted_on_utc = now()
WHERE bill_header_id = p_draft_bill_id;
END LOOP;
-- Mark the draft bill as deleted
UPDATE public.draft_bill_headers
SET
is_deleted = TRUE,
deleted_on_utc = now()
WHERE id = p_draft_bill_id;
END;
$procedure$
-- Procedure: clean_up_org_purchase
CREATE OR REPLACE PROCEDURE public.clean_up_org_purchase(IN p_organization_id uuid)
LANGUAGE plpgsql
AS $procedure$
DECLARE
v_company_id uuid;
v_vendor_id uuid;
v_bill_header_id uuid;
v_bill_payment_header_id uuid;
v_vendor_note_header_id uuid;
v_gate_pass_header_id uuid;
v_user_id uuid;
BEGIN
FOR v_company_id IN SELECT id FROM companies WHERE organization_id = p_organization_id LOOP
-- Vendors and their children
FOR v_vendor_id IN SELECT id FROM vendors WHERE company_id = v_company_id LOOP
DELETE FROM vendor_contacts WHERE vendor_id = v_vendor_id;
DELETE FROM vendor_bank_accounts WHERE vendor_id = v_vendor_id;
DELETE FROM vendor_upis WHERE vendor_id = v_vendor_id;
DELETE FROM vendor_default_accounts WHERE vendor_id = v_vendor_id;
FOR v_vendor_note_header_id IN SELECT id FROM vendor_note_headers WHERE vendor_id = v_vendor_id LOOP
DELETE FROM vendor_note_details WHERE vendor_note_header_id = v_vendor_note_header_id;
END LOOP;
DELETE FROM vendor_note_headers WHERE vendor_id = v_vendor_id;
-- If you want to delete warehouses for this vendor:
-- DELETE FROM warehouses WHERE vendor_id = v_vendor_id;
END LOOP;
DELETE FROM vendors WHERE company_id = v_company_id;
DELETE FROM vendor_note_work_flows WHERE company_id = v_company_id;
DELETE FROM vendor_note_statuses WHERE company_id = v_company_id;
-- Bills and their children
FOR v_bill_header_id IN SELECT id FROM bill_headers WHERE company_id = v_company_id LOOP
DELETE FROM bill_details WHERE bill_header_id = v_bill_header_id;
DELETE FROM bill_approval_logs WHERE bill_id = v_bill_header_id;
DELETE FROM bill_approval_issue_logs WHERE bill_id = v_bill_header_id;
END LOOP;
DELETE FROM bill_headers WHERE company_id = v_company_id;
DELETE FROM bill_header_ids WHERE company_id = v_company_id;
DELETE FROM bill_workflow WHERE company_id = v_company_id;
DELETE FROM bill_status_company_configs WHERE company_id = v_company_id;
DELETE FROM bill_account_approval_levels WHERE company_id = v_company_id;
DELETE FROM bill_approval_user_account WHERE company_id = v_company_id;
DELETE FROM bill_approval_user_company WHERE company_id = v_company_id;
-- Bill payments
FOR v_bill_payment_header_id IN SELECT id FROM bill_payment_headers WHERE company_id = v_company_id LOOP
DELETE FROM bill_payment_details WHERE bill_payment_header_id = v_bill_payment_header_id;
END LOOP;
DELETE FROM bill_payment_headers WHERE company_id = v_company_id;
DELETE FROM bill_payment_header_ids WHERE company_id = v_company_id;
-- Draft bills
FOR v_bill_header_id IN SELECT id FROM draft_bill_headers WHERE company_id = v_company_id LOOP
DELETE FROM draft_bill_details WHERE bill_header_id = v_bill_header_id;
END LOOP;
DELETE FROM draft_bill_headers WHERE company_id = v_company_id;
DELETE FROM draft_bill_header_ids WHERE company_id = v_company_id;
-- Recurring bills
DELETE FROM recurring_bill_schedules WHERE company_id = v_company_id;
-- Gate pass and details
FOR v_gate_pass_header_id IN SELECT id FROM gate_pass_headers WHERE company_id = v_company_id LOOP
DELETE FROM gate_pass_details WHERE gate_pass_header_id = v_gate_pass_header_id;
END LOOP;
DELETE FROM gate_pass_headers WHERE company_id = v_company_id;
-- Users & their roles
FOR v_user_id IN SELECT id FROM users WHERE company_id = v_company_id LOOP
DELETE FROM user_roles WHERE user_id = v_user_id;
END LOOP;
DELETE FROM users WHERE company_id = v_company_id;
-- Finally, company itself
DELETE FROM companies WHERE id = v_company_id;
END LOOP;
-- Organization record itself
DELETE FROM organizations WHERE id = p_organization_id;
RAISE NOTICE 'Organization % and all related purchase data deleted.', p_organization_id;
END;
$procedure$
-- Procedure: update_company_level_approval_configuration
-- SOURCE
CREATE OR REPLACE PROCEDURE public.update_company_level_approval_configuration(IN p_company_id uuid, IN p_status_id integer, IN p_required_approval_levels integer, IN p_approvals jsonb, IN p_modified_by uuid)
LANGUAGE plpgsql
AS $procedure$
DECLARE
approval_item JSONB;
existing_id INT;
upsert_count INT := 0;
delete_count INT := 0;
BEGIN
RAISE NOTICE 'Starting update_company_level_approval_configuration for company %, status %', p_company_id, p_status_id;
RAISE NOTICE 'Required approval levels: %', p_required_approval_levels;
RAISE NOTICE 'Approvals payload: %', p_approvals;
-- 1️⃣ Soft delete old records not present in incoming approvals:
UPDATE bill_approval_user_company
SET is_deleted = TRUE,
deleted_on_utc = NOW(),
modified_on_utc = NOW(),
modified_by = p_modified_by
WHERE company_id = p_company_id
AND is_deleted = FALSE
AND NOT EXISTS (
SELECT 1
FROM jsonb_array_elements(p_approvals) AS elem
WHERE (elem->>'userId')::UUID = bill_approval_user_company.user_id
AND (elem->>'statusId')::INT = bill_approval_user_company.status_id
AND (elem->>'approvalLevel')::INT = bill_approval_user_company.approval_level
);
GET DIAGNOSTICS delete_count = ROW_COUNT;
RAISE NOTICE 'Soft deleted % records not present in payload', delete_count;
-- 2️⃣ Upsert incoming approvals:
FOR approval_item IN SELECT * FROM jsonb_array_elements(p_approvals)
LOOP
SELECT id INTO existing_id
FROM bill_approval_user_company
WHERE company_id = p_company_id
AND user_id = (approval_item->>'userId')::UUID
AND status_id = (approval_item->>'statusId')::INT
AND approval_level = (approval_item->>'approvalLevel')::INT
AND is_deleted = FALSE
LIMIT 1;
IF existing_id IS NOT NULL THEN
UPDATE bill_approval_user_company
SET status_id = (approval_item->>'statusId')::INT,
modified_on_utc = NOW(),
modified_by = p_modified_by
WHERE id = existing_id;
RAISE NOTICE 'Updated approval for user %, level %, status %', (approval_item->>'userId'), (approval_item->>'approvalLevel'), (approval_item->>'statusId');
ELSE
INSERT INTO bill_approval_user_company (
company_id,
status_id,
user_id,
approval_level,
is_deleted,
created_on_utc,
created_by
)
VALUES (
p_company_id,
(approval_item->>'statusId')::INT,
(approval_item->>'userId')::UUID,
(approval_item->>'approvalLevel')::INT,
FALSE,
NOW(),
p_modified_by
);
RAISE NOTICE 'Inserted approval for user %, level %, status %', (approval_item->>'userId'), (approval_item->>'approvalLevel'), (approval_item->>'statusId');
END IF;
upsert_count := upsert_count + 1;
END LOOP;
RAISE NOTICE 'Upserted % approval records', upsert_count;
-- 3️⃣ Update workflow approval level for the given status:
UPDATE bill_workflow
SET approval_level = p_required_approval_levels
WHERE company_id = p_company_id
AND status = p_status_id;
RAISE NOTICE 'Updated bill_workflow approval_level to % for company % and status %', p_required_approval_levels, p_company_id, p_status_id;
RAISE NOTICE 'Completed update_company_level_approval_configuration for company %', p_company_id;
END;
$procedure$
-- TARGET
CREATE OR REPLACE PROCEDURE public.update_company_level_approval_configuration(IN p_company_id uuid, IN p_status_id integer, IN p_required_approval_levels integer, IN p_approvals jsonb, IN p_modified_by uuid)
LANGUAGE plpgsql
AS $procedure$
DECLARE
approval_item JSONB;
existing_id INT;
BEGIN
-- 1️⃣ Soft delete old records not present in incoming approvals:
UPDATE bill_approval_user_company
SET is_deleted = TRUE,
deleted_on_utc = NOW(),
modified_on_utc = NOW(),
modified_by = p_modified_by
WHERE company_id = p_company_id
AND NOT EXISTS (
SELECT 1
FROM jsonb_array_elements(p_approvals) AS elem
WHERE (elem->>'userId')::UUID = bill_approval_user_company.user_id
AND (elem->>'approvalLevel')::INT = bill_approval_user_company.approval_level
);
-- 2️⃣ Upsert incoming approvals:
FOR approval_item IN SELECT * FROM jsonb_array_elements(p_approvals)
LOOP
SELECT id INTO existing_id
FROM bill_approval_user_company
WHERE company_id = p_company_id
AND user_id = (approval_item->>'userId')::UUID
AND approval_level = (approval_item->>'approvalLevel')::INT
AND is_deleted = FALSE
LIMIT 1;
IF existing_id IS NOT NULL THEN
UPDATE bill_approval_user_company
SET status_id = (approval_item->>'statusId')::INT,
modified_on_utc = NOW(),
modified_by = p_modified_by
WHERE id = existing_id;
ELSE
INSERT INTO bill_approval_user_company (
company_id,
status_id,
user_id,
approval_level,
is_deleted,
created_on_utc,
created_by
)
VALUES (
p_company_id,
(approval_item->>'statusId')::INT,
(approval_item->>'userId')::UUID,
(approval_item->>'approvalLevel')::INT,
FALSE,
NOW(),
p_modified_by
);
END IF;
END LOOP;
-- 3️⃣ Update workflow approval level:
UPDATE bill_workflow
SET approval_level = p_required_approval_levels
WHERE company_id = p_company_id
AND status = 2;
END;
$procedure$
-- Procedure: insert_draft_bill
CREATE OR REPLACE PROCEDURE public.insert_draft_bill(IN p_id uuid, IN p_company_id uuid, IN p_vendor_id uuid, IN p_discount numeric, IN p_currency_id integer, IN p_bill_date timestamp without time zone, IN p_payment_term integer, IN p_bill_status_id integer, IN p_due_date timestamp without time zone, IN p_total_amount numeric, IN p_taxable_amount numeric, IN p_fees numeric, IN p_sgst_amount numeric, IN p_cgst_amount numeric, IN p_igst_amount numeric, IN p_tds_amount numeric, IN p_note text, IN p_round_off numeric, IN p_round_off_tds numeric, IN p_debit_account_id uuid, IN p_credit_account_id uuid, IN p_po_no text, IN p_po_date timestamp without time zone, IN p_source_warehouse_id uuid, IN p_destination_warehouse_id uuid, IN p_bill_type_id integer, IN p_created_by uuid, IN p_bill_details jsonb, IN p_bill_number text, IN p_setteled_amount numeric DEFAULT 0)
LANGUAGE plpgsql
AS $procedure$
DECLARE
detail RECORD; -- Declaring a record variable for the loop
v_source_warehouse_id uuid;
v_destination_warehouse_id uuid;
v_detail_id uuid;
BEGIN
-- Set default values for warehouses based on bill type
IF p_bill_type_id = 2 OR p_bill_type_id = 3 THEN
v_source_warehouse_id := NULL;
v_destination_warehouse_id := NULL;
ELSE
v_source_warehouse_id := COALESCE(p_source_warehouse_id, NULL);
v_destination_warehouse_id := COALESCE(p_destination_warehouse_id, NULL);
END IF;
RAISE NOTICE 'v_source_warehouse_id: %', v_source_warehouse_id;
RAISE NOTICE 'v_destination_warehouse_id: %', v_destination_warehouse_id;
-- Call to create_draft_bill_header procedure
CALL public.create_draft_bill_header(
p_id,
p_company_id,
p_vendor_id,
p_credit_account_id,
p_debit_account_id,
p_bill_date::date,
p_due_date::date,
p_payment_term,
p_taxable_amount,
p_cgst_amount,
p_sgst_amount,
p_igst_amount,
p_tds_amount,
p_total_amount,
p_discount,
p_fees,
p_round_off,
p_round_off_tds,
p_currency_id,
p_note ,
p_bill_status_id ,
p_created_by ,
v_source_warehouse_id ,
v_destination_warehouse_id ,
p_bill_type_id,
p_bill_number,
p_po_no,
p_po_date::date
);
RAISE NOTICE 'Bill header inserted with ID: %', p_id;
-- Loop through the JSONB array of bill details
FOR detail IN
SELECT * FROM jsonb_to_recordset(p_bill_details) AS (
id uuid, product_id uuid, price numeric, quantity numeric,
fees numeric, discount numeric, taxable_amount numeric,
sgst_amount numeric, cgst_amount numeric, igst_amount numeric,
total_amount numeric, serial_number integer
)
LOOP
-- Log each field to ensure they are being read correctly
--RAISE NOTICE 'Detail - product_id: %, price: %, quantity: %, taxable_amount: %', detail."productId", detail.price, detail.quantity, detail."taxableAmount";
-- Insert into draft_bill_details, using "productId" from JSON
INSERT INTO public.draft_bill_details (
id, bill_header_id, product_id, price, quantity, fees, discount,
taxable_amount, sgst_amount, cgst_amount, igst_amount,
total_amount, serial_number, is_deleted
) VALUES (
detail.id, p_id, detail.product_id, detail.price, detail.quantity,
detail.fees, detail.discount, detail.taxable_amount, detail.sgst_amount,
detail.cgst_amount, detail.igst_amount, detail.total_Amount, detail.serial_number, false
);
RAISE NOTICE 'Bill detail inserted with ID: %', detail.id;
END LOOP;
-- No explicit COMMIT needed
EXCEPTION
-- No explicit ROLLBACK needed; just raise the error
WHEN OTHERS THEN
RAISE NOTICE 'An error occurred: %, transaction rolled back', SQLERRM;
RAISE;
END;
$procedure$
-- Procedure: insert_bill_by_excel
CREATE OR REPLACE PROCEDURE public.insert_bill_by_excel(IN p_id uuid, IN p_company_id uuid, IN p_vendor_id uuid, IN p_discount numeric, IN p_currency_id integer, IN p_bill_date timestamp without time zone, IN p_bill_number text, IN p_payment_term integer, IN p_bill_status_id integer, IN p_due_date timestamp without time zone, IN p_total_amount numeric, IN p_taxable_amount numeric, IN p_fees numeric, IN p_sgst_amount numeric, IN p_cgst_amount numeric, IN p_igst_amount numeric, IN p_note text, IN p_round_off numeric, IN p_round_off_tds numeric, IN p_debit_account_id uuid, IN p_credit_account_id uuid, IN p_po_no text, IN p_po_date timestamp without time zone, IN p_source_warehouse_id uuid, IN p_destination_warehouse_id uuid, IN p_created_by uuid, IN p_bill_details jsonb, IN p_type integer, IN p_setteled_amount numeric DEFAULT 0)
LANGUAGE plpgsql
AS $procedure$
DECLARE
detail RECORD;
v_source_warehouse_id uuid;
v_destination_warehouse_id uuid;
v_bill_voucher text;
v_created_by uuid;
BEGIN
-- Set default values for warehouses
v_source_warehouse_id := COALESCE(p_source_warehouse_id, NULL);
v_destination_warehouse_id := COALESCE(p_destination_warehouse_id, NULL);
v_bill_voucher := get_new_bill_voucher(p_company_id, p_bill_date::date);
-- Check if the bill voucher was generated successfully
--IF v_bill_voucher IS NULL THEN
-- RAISE EXCEPTION 'Failed to generate bill voucher for company ID: %', p_company_id;
--ELSE
-- RAISE NOTICE 'Generated Bill Voucher: %', v_bill_voucher;
--END IF;
-- Fetch the system user ID from the users table
SELECT id INTO v_created_by FROM public.users
WHERE first_name = 'System'
LIMIT 1;
-- Raise an exception if the system user is not found
IF v_created_by IS NULL THEN
RAISE EXCEPTION 'System user not found in the users table';
END IF;
-- Insert into the bill header
INSERT INTO public.bill_headers(
id,
company_id,
vendor_id,
credit_account_id,
debit_account_id,
bill_voucher,
bill_number,
bill_date,
due_date,
payment_term,
taxable_amount,
cgst_amount,
sgst_amount,
igst_amount,
tds_amount,
total_amount,
discount,
fees,
round_off,
round_off_tds,
currency_id,
note,
bill_status_id,
created_by,
source_warehouse_id,
destination_warehouse_id,
bill_type_id,
created_on_utc,
po_no,
po_date
)
VALUES (
p_id,
p_company_id,
p_vendor_id,
p_credit_account_id,
p_debit_account_id,
v_bill_voucher,
p_bill_number,
p_bill_date,
p_due_date,
p_payment_term,
p_taxable_amount,
p_cgst_amount,
p_sgst_amount,
p_igst_amount,
0,
p_total_amount,
p_discount,
p_fees,
p_round_off,
p_round_off_tds,
p_currency_id,
p_note,
p_bill_status_id,
p_created_by,
p_source_warehouse_id,
p_destination_warehouse_id,
p_type,
(CURRENT_TIMESTAMP AT TIME ZONE 'Asia/Kolkata')::timestamp,
p_po_no,
p_po_date
);
RAISE NOTICE 'bill header inserted with ID: %', p_id;
EXCEPTION
-- Handle unique violation (duplicate key)
WHEN unique_violation THEN
RAISE NOTICE 'Duplicate entry: %, rolling back', SQLERRM;
ROLLBACK;
-- Handle foreign key violation
WHEN foreign_key_violation THEN
RAISE NOTICE 'Foreign key violation: %, rolling back', SQLERRM;
ROLLBACK;
-- Catch all other exceptions
WHEN OTHERS THEN
RAISE NOTICE 'An error occurred: %, transaction rolled back', SQLERRM;
ROLLBACK;
END;
$procedure$
-- Procedure: insert_bill
CREATE OR REPLACE PROCEDURE public.insert_bill(IN p_id uuid, IN p_company_id uuid, IN p_vendor_id uuid, IN p_discount numeric, IN p_currency_id integer, IN p_bill_date timestamp without time zone, IN p_payment_term integer, IN p_bill_status_id integer, IN p_due_date timestamp without time zone, IN p_total_amount numeric, IN p_taxable_amount numeric, IN p_fees numeric, IN p_sgst_amount numeric, IN p_cgst_amount numeric, IN p_igst_amount numeric, IN p_tds_amount numeric, IN p_note text, IN p_round_off numeric, IN p_round_off_tds numeric, IN p_debit_account_id uuid, IN p_credit_account_id uuid, IN p_po_no text, IN p_po_date timestamp without time zone, IN p_source_warehouse_id uuid, IN p_destination_warehouse_id uuid, IN p_bill_type_id integer, IN p_created_by uuid, IN p_bill_details jsonb, IN p_bill_number text, IN p_setteled_amount numeric DEFAULT 0)
LANGUAGE plpgsql
AS $procedure$
DECLARE
detail RECORD; -- Declaring a record variable for the loop
v_source_warehouse_id uuid;
v_destination_warehouse_id uuid;
v_detail_id uuid;
BEGIN
-- Set default values for warehouses based on bill type
IF p_bill_type_id = 2 OR p_bill_type_id = 3 THEN
v_source_warehouse_id := NULL;
v_destination_warehouse_id := NULL;
ELSE
v_source_warehouse_id := COALESCE(p_source_warehouse_id, NULL);
v_destination_warehouse_id := COALESCE(p_destination_warehouse_id, NULL);
END IF;
RAISE NOTICE 'v_source_warehouse_id: %', v_source_warehouse_id;
RAISE NOTICE 'v_destination_warehouse_id: %', v_destination_warehouse_id;
-- Call to create_bill_header procedure
CALL public.create_bill_header(
p_id,
p_company_id,
p_vendor_id,
p_credit_account_id,
p_debit_account_id,
p_bill_date::date,
p_due_date::date,
p_payment_term,
p_taxable_amount,
p_cgst_amount,
p_sgst_amount,
p_igst_amount,
p_tds_amount,
p_total_amount,
p_discount,
p_fees,
p_round_off,
p_round_off_tds,
p_currency_id,
p_note ,
p_bill_status_id ,
p_created_by ,
v_source_warehouse_id ,
v_destination_warehouse_id ,
p_bill_type_id,
p_bill_number
);
RAISE NOTICE 'Bill header inserted with ID: %', p_id;
-- Loop through the JSONB array of bill details
FOR detail IN
SELECT * FROM jsonb_to_recordset(p_bill_details) AS (
id uuid, product_id uuid, price numeric, quantity integer,
fees numeric, discount numeric, taxable_amount numeric,
sgst_amount numeric, cgst_amount numeric, igst_amount numeric,
total_amount numeric, serial_number integer
)
LOOP
INSERT INTO public.bill_details (
id, bill_header_id, product_id, price, quantity, fees, discount,
taxable_amount, sgst_amount, cgst_amount, igst_amount,
total_amount, serial_number, is_deleted
) VALUES (
detail.id, p_id, detail.product_id, detail.price, detail.quantity,
detail.fees, detail.discount, detail.taxable_amount, detail.sgst_amount,
detail.cgst_amount, detail.igst_amount, detail.total_Amount, detail.serial_number, false
);
RAISE NOTICE 'Bill detail inserted with ID: %', detail.id;
END LOOP;
-- No explicit COMMIT needed
EXCEPTION
-- No explicit ROLLBACK needed; just raise the error
WHEN OTHERS THEN
RAISE NOTICE 'An error occurred: %, transaction rolled back', SQLERRM;
RAISE;
END;
$procedure$
-- Procedure: create_draft_bill_header
CREATE OR REPLACE PROCEDURE public.create_draft_bill_header(IN p_id uuid, IN p_company_id uuid, IN p_vendor_id uuid, IN p_credit_account_id uuid, IN p_debit_account_id uuid, IN p_bill_date date, IN p_due_date date, IN p_payment_term integer, IN p_taxable_amount numeric, IN p_cgst_amount numeric, IN p_sgst_amount numeric, IN p_igst_amount numeric, IN p_tds_amount numeric, IN p_total_amount numeric, IN p_discount numeric, IN p_fees numeric, IN p_round_off numeric, IN p_round_off_tds numeric, IN p_currency_id integer, IN p_note character varying, IN p_bill_status_id integer, IN p_created_by uuid, IN p_source_warehouse_id uuid DEFAULT NULL::uuid, IN p_destination_warehouse_id uuid DEFAULT NULL::uuid, IN p_bill_type_id integer DEFAULT 1, IN p_bill_number character varying DEFAULT NULL::character varying, IN p_po_no text DEFAULT NULL::text, IN p_po_date date DEFAULT NULL::date)
LANGUAGE plpgsql
AS $procedure$
DECLARE
v_bill_voucher character varying;
source_warehouse_id uuid;
destination_warehouse_id uuid;
BEGIN
-- Generate bill voucher using the draft bill function
v_bill_voucher := get_new_draft_bill_voucher(p_company_id,p_bill_date);
-- Log the generated bill voucher
RAISE NOTICE 'Bill Voucher: %', v_bill_voucher;
-- Set default values for warehouses based on bill type
IF p_bill_type_id = 2 OR p_bill_type_id = 3 THEN
source_warehouse_id := NULL;
destination_warehouse_id := NULL;
ELSE
-- Use passed values or default to null if not provided
source_warehouse_id := COALESCE(p_source_warehouse_id, NULL);
destination_warehouse_id := COALESCE(p_destination_warehouse_id, NULL);
END IF;
-- Insert into draft_bill_headers table
INSERT INTO
public.draft_bill_headers(
id,
company_id,
vendor_id,
credit_account_id,
debit_account_id,
bill_voucher,
bill_number,
bill_date,
due_date,
payment_term,
taxable_amount,
cgst_amount,
sgst_amount,
igst_amount,
tds_amount,
total_amount,
discount,
fees,
round_off,
round_off_tds,
currency_id,
note,
bill_status_id,
created_by,
source_warehouse_id,
destination_warehouse_id,
bill_type_id,
created_on_utc,
po_no,
po_date
)
VALUES (
p_id,
p_company_id,
p_vendor_id,
p_credit_account_id,
p_debit_account_id,
v_bill_voucher,
p_bill_number,
p_bill_date,
p_due_date,
p_payment_term,
p_taxable_amount,
p_cgst_amount,
p_sgst_amount,
p_igst_amount,
p_tds_amount,
p_total_amount,
p_discount,
p_fees,
p_round_off,
p_round_off_tds,
p_currency_id,
p_note,
p_bill_status_id,
p_created_by,
p_source_warehouse_id,
p_destination_warehouse_id,
p_bill_type_id,
(CURRENT_TIMESTAMP AT TIME ZONE 'Asia/Kolkata')::timestamp,
p_po_no,
p_po_date
);
-- Log success message
RAISE NOTICE 'Draft Bill header inserted successfully with Bill Voucher: % ', v_bill_voucher ;
END;
$procedure$
-- Procedure: create_bill_header
CREATE OR REPLACE PROCEDURE public.create_bill_header(IN p_id uuid, IN p_company_id uuid, IN p_vendor_id uuid, IN p_credit_account_id uuid, IN p_debit_account_id uuid, IN p_bill_date date, IN p_due_date date, IN p_payment_term integer, IN p_taxable_amount numeric, IN p_cgst_amount numeric, IN p_sgst_amount numeric, IN p_igst_amount numeric, IN p_tds_amount numeric, IN p_total_amount numeric, IN p_discount numeric, IN p_fees numeric, IN p_round_off numeric, IN p_round_off_tds numeric, IN p_currency_id integer, IN p_note character varying, IN p_bill_status_id integer, IN p_created_by uuid, IN p_source_warehouse_id uuid DEFAULT NULL::uuid, IN p_destination_warehouse_id uuid DEFAULT NULL::uuid, IN p_bill_type_id integer DEFAULT 1, IN p_bill_number character varying DEFAULT NULL::character varying, IN p_po_no text DEFAULT NULL::text, IN p_po_date date DEFAULT NULL::date)
LANGUAGE plpgsql
AS $procedure$
DECLARE
v_bill_voucher character varying;
source_warehouse_id uuid;
destination_warehouse_id uuid;
BEGIN
-- Attempt to generate bill voucher
v_bill_voucher := get_new_bill_voucher(p_company_id, p_bill_date);
-- Check if the bill voucher was generated successfully
IF v_bill_voucher IS NULL THEN
RAISE EXCEPTION 'Failed to generate bill voucher for company ID: %', p_company_id;
ELSE
RAISE NOTICE 'Generated Bill Voucher: %', v_bill_voucher;
END IF;
-- Set default values for warehouses based on bill type
IF p_bill_type_id = 2 OR p_bill_type_id = 3 THEN
source_warehouse_id := NULL;
destination_warehouse_id := NULL;
ELSE
-- Use passed values or default to null if not provided
source_warehouse_id := COALESCE(p_source_warehouse_id, NULL);
destination_warehouse_id := COALESCE(p_destination_warehouse_id, NULL);
END IF;
-- Insert into bill_headers table
INSERT INTO public.bill_headers(
id,
company_id,
vendor_id,
credit_account_id,
debit_account_id,
bill_voucher,
bill_number,
bill_date,
due_date,
payment_term,
taxable_amount,
cgst_amount,
sgst_amount,
igst_amount,
tds_amount,
total_amount,
discount,
fees,
round_off,
round_off_tds,
currency_id,
note,
bill_status_id,
created_by,
source_warehouse_id,
destination_warehouse_id,
bill_type_id,
created_on_utc,
po_no,
po_date
)
VALUES (
p_id,
p_company_id,
p_vendor_id,
p_credit_account_id,
p_debit_account_id,
v_bill_voucher,
p_bill_number,
p_bill_date,
p_due_date,
p_payment_term,
p_taxable_amount,
p_cgst_amount,
p_sgst_amount,
p_igst_amount,
p_tds_amount,
p_total_amount,
p_discount,
p_fees,
p_round_off,
p_round_off_tds,
p_currency_id,
p_note,
p_bill_status_id,
p_created_by,
p_source_warehouse_id,
p_destination_warehouse_id,
p_bill_type_id,
(CURRENT_TIMESTAMP AT TIME ZONE 'Asia/Kolkata')::timestamp,
p_po_no,
p_po_date
);
RAISE NOTICE 'Bill header inserted successfully with Bill Voucher: %', v_bill_voucher;
END;
$procedure$
-- Procedure: run_scheduled_bill
CREATE OR REPLACE PROCEDURE public.run_scheduled_bill(IN p_bill_header_id uuid, IN p_draft_bill_header_id uuid, IN p_schedule_date timestamp without time zone DEFAULT CURRENT_TIMESTAMP)
LANGUAGE plpgsql
AS $procedure$
DECLARE
v_system_user_id uuid;
v_execution_log_id uuid := gen_random_uuid();
v_related_schedule_id uuid;
v_new_draft_bill_id uuid;
v_source_is_live boolean := false;
v_source_bill_id uuid; -- final chosen source id (live or draft)
src_header RECORD;
src_detail RECORD;
BEGIN
----------------------------------------------------------------
-- 1. Find 'System' user (same as sales side)
----------------------------------------------------------------
SELECT u.id
INTO v_system_user_id
FROM public.users AS u
WHERE u.is_deleted = false
AND lower(u.first_name) = 'system'
ORDER BY u.created_on_utc NULLS LAST, u.id
LIMIT 1;
IF v_system_user_id IS NULL THEN
RAISE NOTICE 'No user with first_name="System" found; proceeding with NULL triggered_by.';
END IF;
----------------------------------------------------------------
-- 2. Decide source: live bill or draft bill (same idea as invoices)
----------------------------------------------------------------
IF p_bill_header_id IS NOT NULL THEN
v_source_bill_id := p_bill_header_id;
v_source_is_live := true;
ELSIF p_draft_bill_header_id IS NOT NULL THEN
v_source_bill_id := p_draft_bill_header_id;
v_source_is_live := false;
ELSE
RAISE NOTICE 'Both bill_header_id and draft_bill_header_id are NULL; aborting.';
RETURN;
END IF;
----------------------------------------------------------------
-- 3. Get related schedule_id from recurring_bill_schedules
-- must match on live OR draft id, just like sales version
----------------------------------------------------------------
SELECT s.id
INTO v_related_schedule_id
FROM public.recurring_bill_schedules AS s
WHERE s.is_deleted = false
AND (
(v_source_is_live AND s.bill_header_id = v_source_bill_id)
OR (NOT v_source_is_live AND s.draft_bill_header_id = v_source_bill_id)
)
ORDER BY s.starts_from DESC
LIMIT 1;
----------------------------------------------------------------
-- 4. Fetch source header from correct table
----------------------------------------------------------------
IF v_source_is_live THEN
SELECT *
INTO src_header
FROM public.bill_headers
WHERE id = v_source_bill_id
AND is_deleted = false;
ELSE
SELECT *
INTO src_header
FROM public.draft_bill_headers
WHERE id = v_source_bill_id
AND is_deleted = false;
END IF;
-- if nothing found, log failure and stop
IF NOT FOUND THEN
INSERT INTO public.schedule_execution_logs (
id, schedule_id, execution_date, execution_time, status, error_message, triggered_by, is_deleted
) VALUES (
v_execution_log_id,
v_related_schedule_id,
(p_schedule_date::date),
NOW(),
'Failure',
'Source bill header not found in live or draft tables.',
v_system_user_id,
false
);
RAISE NOTICE 'Source bill header % not found; aborting for date %.',
v_source_bill_id, (p_schedule_date::date);
RETURN;
END IF;
----------------------------------------------------------------
-- 5. Create new draft bill header (clone header-level data)
----------------------------------------------------------------
v_new_draft_bill_id := gen_random_uuid();
CALL public.create_draft_bill_header(
/* p_id */ v_new_draft_bill_id,
/* p_company_id */ src_header.company_id,
/* p_vendor_id */ src_header.vendor_id,
/* p_credit_account_id */ src_header.credit_account_id,
/* p_debit_account_id */ src_header.debit_account_id,
/* p_bill_date */ (p_schedule_date::date),
/* p_due_date */ ((p_schedule_date::date) + COALESCE(src_header.payment_term, 10))::date,
/* p_payment_term */ src_header.payment_term,
/* p_taxable_amount */ src_header.taxable_amount,
/* p_cgst_amount */ src_header.cgst_amount,
/* p_sgst_amount */ src_header.sgst_amount,
/* p_igst_amount */ src_header.igst_amount,
/* p_tds_amount */ COALESCE(src_header.tds_amount, 0),
/* p_total_amount */ src_header.total_amount,
/* p_discount */ src_header.discount,
/* p_fees */ src_header.fees,
/* p_round_off */ src_header.round_off,
/* p_round_off_tds */ COALESCE(src_header.round_off_tds, 0),
/* p_currency_id */ src_header.currency_id,
/* p_note (varchar) */ COALESCE(src_header.note, '')::varchar,
/* p_bill_status_id */ 1,
/* p_created_by */ v_system_user_id,
/* p_source_wh_id */ src_header.source_warehouse_id,
/* p_destination_wh_id */ src_header.destination_warehouse_id,
/* p_bill_type_id */ src_header.bill_type_id,
/* p_bill_number */ COALESCE(src_header.bill_number, '')::varchar,
/* p_po_no */ src_header.po_no::text,
/* p_po_date */ (src_header.po_date)::date
);
----------------------------------------------------------------
-- 6. Clone all details from correct source table
----------------------------------------------------------------
IF v_source_is_live THEN
FOR src_detail IN
SELECT *
FROM public.bill_details
WHERE bill_header_id = v_source_bill_id
LOOP
CALL public.create_draft_bill_detail(
v_new_draft_bill_id,
src_detail.price,
src_detail.quantity,
src_detail.discount,
src_detail.fees,
src_detail.cgst_amount,
src_detail.igst_amount,
src_detail.sgst_amount,
src_detail.taxable_amount,
src_detail.total_amount,
src_detail.serial_number,
src_detail.product_id
);
END LOOP;
ELSE
FOR src_detail IN
SELECT *
FROM public.draft_bill_details
WHERE bill_header_id = v_source_bill_id
LOOP
CALL public.create_draft_bill_detail(
v_new_draft_bill_id,
src_detail.price,
src_detail.quantity,
src_detail.discount,
src_detail.fees,
src_detail.cgst_amount,
src_detail.igst_amount,
src_detail.sgst_amount,
src_detail.taxable_amount,
src_detail.total_amount,
src_detail.serial_number,
src_detail.product_id
);
END LOOP;
END IF;
----------------------------------------------------------------
-- 7. Log success
----------------------------------------------------------------
INSERT INTO public.schedule_execution_logs (
id, schedule_id, execution_date, execution_time, status, error_message, triggered_by, is_deleted
) VALUES (
v_execution_log_id,
v_related_schedule_id,
(p_schedule_date::date),
NOW(),
'Success',
'',
v_system_user_id,
false
);
RAISE NOTICE 'Draft bill % created from % (live=%), schedule_id %, on %.',
v_new_draft_bill_id, v_source_bill_id, v_source_is_live, v_related_schedule_id, (p_schedule_date::date);
EXCEPTION WHEN OTHERS THEN
INSERT INTO public.schedule_execution_logs (
id, schedule_id, execution_date, execution_time, status, error_message, triggered_by, is_deleted
) VALUES (
COALESCE(v_execution_log_id, gen_random_uuid()),
v_related_schedule_id,
(p_schedule_date::date),
NOW(),
'Failure',
SQLERRM,
v_system_user_id,
false
);
RAISE NOTICE 'Error while cloning bill % on %: %',
v_source_bill_id, (p_schedule_date::date), SQLERRM;
END;
$procedure$
-- Procedure: save_bill_and_details
CREATE OR REPLACE PROCEDURE public.save_bill_and_details(IN p_id uuid, IN p_company_id uuid, IN p_vendor_id uuid, IN p_discount numeric, IN p_bill_date timestamp without time zone, IN p_payment_term integer, IN p_bill_status_id integer, IN p_due_date timestamp without time zone, IN p_total_amount numeric, IN p_taxable_amount numeric, IN p_fees numeric, IN p_sgst_amount numeric, IN p_cgst_amount numeric, IN p_igst_amount numeric, IN p_note text, IN p_round_off numeric, IN p_round_off_tds numeric, IN p_debit_account_id uuid, IN p_credit_account_id uuid, IN p_created_by uuid, IN p_currency_id integer, IN p_po_date timestamp without time zone, IN p_po_no text, IN p_destination_warehouse_id uuid, IN p_source_warehouse_id uuid, IN p_bill_type_id integer, IN p_bill_number text, IN p_tds_amount numeric, IN p_details jsonb)
LANGUAGE plpgsql
AS $procedure$
DECLARE
v_bill_voucher text;
v_source_warehouse_id uuid;
v_destination_warehouse_id uuid;
BEGIN
-- Set default values for warehouses
v_source_warehouse_id := COALESCE(p_source_warehouse_id, '00000000-0000-0000-0000-000000000000'::uuid);
v_destination_warehouse_id := COALESCE(p_destination_warehouse_id, '00000000-0000-0000-0000-000000000000'::uuid);
-- Generate the new bill voucher number
v_bill_voucher := public.get_new_bill_voucher(p_company_id, p_bill_date::date);
RAISE NOTICE 'Generated Bill Voucher: %', v_bill_voucher;
-- Validate p_details JSONB
IF p_details IS NULL OR jsonb_array_length(p_details) = 0 THEN
RAISE EXCEPTION 'Details JSONB parameter (p_details) cannot be empty or NULL';
END IF;
-- Insert into bill_headers table
INSERT INTO public.bill_headers (
id, company_id, vendor_id, discount, bill_voucher, bill_date, payment_term,
bill_status_id, due_date, total_amount, taxable_amount, fees, sgst_amount,
cgst_amount, igst_amount, note, round_off, round_off_tds, debit_account_id, credit_account_id,
created_on_utc, created_by, currency_id, po_date, po_no, destination_warehouse_id,
source_warehouse_id, bill_type_id, bill_number, tds_amount
) VALUES (
p_id, p_company_id, p_vendor_id, p_discount, v_bill_voucher, p_bill_date,
p_payment_term, p_bill_status_id, p_due_date, p_total_amount, p_taxable_amount, p_fees,
p_sgst_amount, p_cgst_amount, p_igst_amount, p_note, p_round_off, p_round_off_tds, p_debit_account_id,
p_credit_account_id, NOW(), p_created_by, p_currency_id, p_po_date, p_po_no,
v_destination_warehouse_id, v_source_warehouse_id, p_bill_type_id, p_bill_number, p_tds_amount
);
-- Insert into bill_details table
INSERT INTO public.bill_details (
id, bill_header_id, price, quantity, cgst_amount, discount, fees, igst_amount,
sgst_amount, taxable_amount, total_amount, serial_number, product_id
)
SELECT
gen_random_uuid(), p_id, (detail->>'price')::numeric, (detail->>'quantity')::numeric,
(detail->>'cgst_amount')::numeric, (detail->>'discount')::numeric, (detail->>'fees')::numeric,
(detail->>'igst_amount')::numeric, (detail->>'sgst_amount')::numeric, (detail->>'taxable_amount')::numeric,
(detail->>'total_amount')::numeric, (detail->>'serial_number')::integer, (detail->>'product_id')::uuid
FROM jsonb_array_elements(p_details) AS detail;
EXCEPTION
WHEN OTHERS THEN
RAISE EXCEPTION 'An error occurred: %', SQLERRM;
END;
$procedure$
-- Procedure: update_draft_bill_next_status_1
CREATE OR REPLACE PROCEDURE public.update_draft_bill_next_status_1(IN p_company_id uuid, IN p_bill_ids uuid[], IN p_modified_by uuid)
LANGUAGE plpgsql
AS $procedure$
DECLARE
v_bill RECORD;
v_account_id uuid;
v_current_approval_level integer;
v_approval_level_required integer;
v_issue TEXT;
APPROVED_STATUS CONSTANT INTEGER := 3;
v_debug_next_status TEXT;
v_next_temp_id int;
BEGIN
RAISE NOTICE 'START update_draft_bill_next_status_1 for company %, user %', p_company_id, p_modified_by;
RAISE NOTICE 'Input bill IDs: %', p_bill_ids;
FOR v_bill IN
SELECT id, bill_status_id, debit_account_id, current_approval_level
FROM public.draft_bill_headers
WHERE id = ANY(p_bill_ids) AND bill_status_id = 2
LOOP
RAISE NOTICE 'Processing Bill ID: %', v_bill.id;
v_account_id := v_bill.debit_account_id;
v_current_approval_level := v_bill.current_approval_level;
SELECT approval_level_required
INTO v_approval_level_required
FROM public.bill_approval_level_view
WHERE company_id = p_company_id
AND status_id = v_bill.bill_status_id
AND (account_id = v_account_id OR account_id IS NULL)
ORDER BY account_id ASC
LIMIT 1;
RAISE NOTICE 'Bill % current level: %, required: %', v_bill.id, v_current_approval_level, v_approval_level_required;
IF v_approval_level_required IS NULL THEN
v_issue := 'Approval level not found';
INSERT INTO public.bill_approval_issue_logs(bill_id, issue, created_on_utc, created_by)
VALUES (v_bill.id, v_issue, now(), p_modified_by);
SELECT COALESCE(MAX(id), 0) + 1 INTO v_next_temp_id FROM temp_bill_next_status;
INSERT INTO temp_bill_next_status(id, bill_id, status, error)
VALUES (v_next_temp_id, v_bill.id, 'Not found', v_issue);
RAISE NOTICE 'Bill % skipped: approval level not found', v_bill.id;
CONTINUE;
END IF;
-- 🔔 Replace this with call to `check_bill_approval_permissions`
PERFORM public.check_bill_approval_permissions(
p_company_id,
v_bill.bill_status_id,
p_modified_by,
v_account_id,
v_current_approval_level + 1
);
IF NOT FOUND THEN
v_issue := 'User not authorized to approve at the next level';
INSERT INTO public.bill_approval_issue_logs(bill_id, issue, created_on_utc, created_by)
VALUES (v_bill.id, v_issue, now(), p_modified_by);
SELECT COALESCE(MAX(id), 0) + 1 INTO v_next_temp_id FROM temp_bill_next_status;
INSERT INTO temp_bill_next_status(id, bill_id, status, error)
VALUES (v_next_temp_id, v_bill.id, 'User not authorized', v_issue);
RAISE NOTICE 'Bill % skipped: user not authorized', v_bill.id;
CONTINUE;
END IF;
IF v_current_approval_level + 1 < v_approval_level_required THEN
v_debug_next_status := CONCAT('Level ', v_current_approval_level + 1);
ELSE
v_debug_next_status := 'Approved';
END IF;
RAISE NOTICE 'Bill % next computed status: %', v_bill.id, v_debug_next_status;
IF v_approval_level_required > (v_current_approval_level + 1) THEN
UPDATE public.draft_bill_headers
SET current_approval_level = v_current_approval_level + 1,
modified_by = p_modified_by,
modified_on_utc = now()
WHERE id = v_bill.id;
INSERT INTO public.bill_approval_logs(
bill_id, status_id, approved_by, approved_on, "comment",
created_on_utc, created_by, approval_level
)
VALUES (
v_bill.id, v_bill.bill_status_id, p_modified_by, now(),
CONCAT('Approved at level ', v_current_approval_level + 1),
now(), p_modified_by, v_current_approval_level + 1
);
RAISE NOTICE 'Bill % moved to next approval level: %', v_bill.id, v_current_approval_level + 1;
SELECT COALESCE(MAX(id), 0) + 1 INTO v_next_temp_id FROM temp_bill_next_status;
INSERT INTO temp_bill_next_status(id, bill_id, status, error)
VALUES (v_next_temp_id, v_bill.id, v_debug_next_status, NULL);
ELSE
UPDATE public.draft_bill_headers
SET bill_status_id = APPROVED_STATUS,
modified_by = p_modified_by,
modified_on_utc = now(),
current_approval_level = v_current_approval_level + 1
WHERE id = v_bill.id;
INSERT INTO public.bill_approval_logs(
bill_id, status_id, approved_by, approved_on, "comment",
created_on_utc, created_by, approval_level
)
VALUES (
v_bill.id, v_bill.bill_status_id, p_modified_by, now(),
'Final Approval',
now(), p_modified_by, v_current_approval_level + 1
);
CALL transfer_draft_to_bill(v_bill.id, p_modified_by);
RAISE NOTICE 'Bill % approved and transferred', v_bill.id;
SELECT COALESCE(MAX(id), 0) + 1 INTO v_next_temp_id FROM temp_bill_next_status;
INSERT INTO temp_bill_next_status(id, bill_id, status, error)
VALUES (v_next_temp_id, v_bill.id, v_debug_next_status, NULL);
END IF;
RAISE NOTICE '--- Finished processing Bill: % ---', v_bill.id;
END LOOP;
RAISE NOTICE 'END update_draft_bill_next_status';
END
$procedure$
-- Procedure: create_schedule_bills
CREATE OR REPLACE PROCEDURE public.create_schedule_bills(IN p_company_id uuid, IN p_frequency_cycle text, IN p_starts_from date, IN p_end_date date, IN p_created_by uuid, IN p_bill_header_id uuid DEFAULT NULL::uuid, IN p_draft_bill_header_id uuid DEFAULT NULL::uuid, IN p_month_of_year integer DEFAULT NULL::integer, IN p_day_of_month integer DEFAULT NULL::integer, IN p_day_of_week integer DEFAULT NULL::integer, IN p_time_of_day interval DEFAULT '00:00:00'::interval)
LANGUAGE plpgsql
AS $procedure$
DECLARE
v_recurring_schedule_id uuid := gen_random_uuid(); -- New ID for recurring_bill_schedules
v_schedule_status text;
-- Normalized copies (treat all-zero GUID as NULL)
v_bill_header_id uuid := NULLIF(p_bill_header_id, '00000000-0000-0000-0000-000000000000'::uuid);
v_draft_bill_header_id uuid := NULLIF(p_draft_bill_header_id, '00000000-0000-0000-0000-000000000000'::uuid);
v_day_of_week integer := COALESCE(p_day_of_week, 0);
v_day_of_month integer := COALESCE(p_day_of_month, 0);
v_month_of_year integer := COALESCE(p_month_of_year, 0);
v_time_of_day interval := COALESCE(p_time_of_day, '00:00:00'::interval);
BEGIN
----------------------------------------------------------------
-- Validate: exactly ONE of bill_header_id / draft_bill_header_id
----------------------------------------------------------------
IF (v_bill_header_id IS NULL AND v_draft_bill_header_id IS NULL) THEN
RAISE EXCEPTION 'Either bill_header_id or draft_bill_header_id must be provided.';
ELSIF (v_bill_header_id IS NOT NULL AND v_draft_bill_header_id IS NOT NULL) THEN
RAISE EXCEPTION 'Provide only one of bill_header_id or draft_bill_header_id, not both.';
END IF;
----------------------------------------------------------------
-- Determine the initial schedule status
----------------------------------------------------------------
IF p_starts_from > CURRENT_DATE THEN
v_schedule_status := 'inactive';
ELSE
v_schedule_status := 'active';
END IF;
----------------------------------------------------------------
-- Insert into the recurring_bill_schedules table
----------------------------------------------------------------
INSERT INTO public.recurring_bill_schedules (
id,
bill_header_id,
draft_bill_header_id,
company_id,
frequency_cycle,
schedule_status,
starts_from,
end_date,
created_on_utc,
created_by,
is_deleted
)
VALUES (
v_recurring_schedule_id,
v_bill_header_id,
v_draft_bill_header_id,
p_company_id,
p_frequency_cycle,
v_schedule_status,
p_starts_from,
p_end_date,
NOW(),
p_created_by,
false
);
----------------------------------------------------------------
-- Insert into recurrence_bill_schedule_details
----------------------------------------------------------------
IF p_frequency_cycle = 'Daily' THEN
INSERT INTO public.recurrence_bill_schedule_details (
schedule_id,
frequency_cycle,
time_of_day,
is_deleted
)
VALUES (
v_recurring_schedule_id,
p_frequency_cycle,
v_time_of_day,
false
);
ELSIF p_frequency_cycle = 'Weekly' THEN
INSERT INTO public.recurrence_bill_schedule_details (
schedule_id,
frequency_cycle,
day_of_week,
time_of_day,
is_deleted
)
VALUES (
v_recurring_schedule_id,
p_frequency_cycle,
v_day_of_week,
v_time_of_day,
false
);
ELSIF p_frequency_cycle = 'Monthly' THEN
INSERT INTO public.recurrence_bill_schedule_details (
schedule_id,
frequency_cycle,
day_of_month,
time_of_day,
is_deleted
)
VALUES (
v_recurring_schedule_id,
p_frequency_cycle,
v_day_of_month,
v_time_of_day,
false
);
ELSIF p_frequency_cycle = 'Yearly' THEN
INSERT INTO public.recurrence_bill_schedule_details (
schedule_id,
frequency_cycle,
month_of_year,
day_of_month,
time_of_day,
is_deleted
)
VALUES (
v_recurring_schedule_id,
p_frequency_cycle,
v_month_of_year,
v_day_of_month,
v_time_of_day,
false
);
ELSE
RAISE NOTICE 'Invalid frequency cycle: %', p_frequency_cycle;
END IF;
RAISE NOTICE 'Recurring bill schedule created successfully with ID: %, Status: %',
v_recurring_schedule_id, v_schedule_status;
END;
$procedure$
-- View: bill_details_with_payments
SELECT bh.bill_number,
bh.bill_date,
bd.product_id,
bd.quantity,
bd.price,
bd.total_amount AS bill_amount,
COALESCE(bp.paid_amount, (0)::numeric) AS paid_amount,
(bd.total_amount - COALESCE(bp.paid_amount, (0)::numeric)) AS remaining_amount,
bd.cgst_amount,
bd.sgst_amount,
bd.igst_amount,
bd.taxable_amount,
bd.discount,
bd.fees
FROM (((bill_details bd
JOIN bill_headers bh ON ((bd.bill_header_id = bh.id)))
LEFT JOIN bill_payment_details bpd ON ((bd.bill_header_id = bpd.bill_header_id)))
LEFT JOIN bill_payment_headers bp ON ((bpd.bill_payment_header_id = bp.id)))
WHERE ((bh.is_deleted = false) AND (bd.is_deleted = false));
-- View: bill_with_details
SELECT bh.bill_number,
bh.bill_date,
bd.product_id,
bd.quantity,
bd.price,
bd.total_amount AS bill_amount,
COALESCE(bp.paid_amount, (0)::numeric) AS paid_amount,
(bd.total_amount - COALESCE(bp.paid_amount, (0)::numeric)) AS remaining_amount,
bd.cgst_amount,
bd.sgst_amount,
bd.igst_amount,
bd.taxable_amount,
bd.discount,
bd.fees
FROM (((bill_details bd
JOIN bill_headers bh ON ((bd.bill_header_id = bh.id)))
LEFT JOIN bill_payment_details bpd ON ((bd.bill_header_id = bpd.bill_header_id)))
LEFT JOIN bill_payment_headers bp ON ((bpd.bill_payment_header_id = bp.id)))
WHERE ((bh.is_deleted = false) AND (bd.is_deleted = false));
-- View: vw_bill_approval_permissions
-- SOURCE
SELECT company_id,
status_id,
user_id,
approval_level,
account_id,
account_approval_level,
account_status_id
FROM ( SELECT baua.company_id,
baua.status_id,
baua.user_id,
baua.approval_level,
baua.account_id,
baua.approval_level AS account_approval_level,
baua.status_id AS account_status_id
FROM bill_approval_user_account baua
WHERE baua.is_deleted = false
UNION ALL
SELECT bauc.company_id,
bauc.status_id,
bauc.user_id,
bauc.approval_level,
NULL::uuid AS account_id,
NULL::integer AS account_approval_level,
bauc.status_id AS account_status_id
FROM bill_approval_user_company bauc
WHERE bauc.is_deleted = false AND NOT (EXISTS ( SELECT 1
FROM bill_approval_user_account baua
WHERE baua.company_id = bauc.company_id AND baua.status_id = bauc.status_id AND baua.user_id = bauc.user_id AND baua.is_deleted = false))) sub;
-- TARGET
SELECT COALESCE(baua.company_id, bauc.company_id) AS company_id,
COALESCE(baua.status_id, bauc.status_id) AS status_id,
COALESCE(baua.user_id, bauc.user_id) AS user_id,
COALESCE(baua.approval_level, bauc.approval_level) AS approval_level,
baua.account_id,
baua.approval_level AS account_approval_level,
baua.status_id AS account_status_id
FROM bill_approval_user_company bauc
LEFT JOIN bill_approval_user_account baua ON bauc.company_id = baua.company_id AND bauc.status_id = baua.status_id
WHERE baua.company_id = bauc.company_id AND baua.status_id = bauc.status_id OR baua.company_id IS NULL AND bauc.is_deleted = false AND baua.is_deleted = false;