@fayz-ai/plugin-financial 0.24.6 → 0.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +257 -1
- package/dist/index.js.map +1 -1
- package/dist/migrations/index.d.ts +1 -0
- package/dist/migrations/index.d.ts.map +1 -1
- package/dist/server/index.d.ts +34 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +58 -0
- package/dist/server/index.js.map +1 -0
- package/package.json +6 -1
package/dist/index.js
CHANGED
|
@@ -44899,6 +44899,261 @@ END $$;
|
|
|
44899
44899
|
REVOKE ALL ON FUNCTION public.financial_set_invoice_lines(uuid, jsonb, boolean) FROM PUBLIC, anon;
|
|
44900
44900
|
GRANT EXECUTE ON FUNCTION public.financial_set_invoice_lines(uuid, jsonb, boolean) TO authenticated, service_role;
|
|
44901
44901
|
`;
|
|
44902
|
+
var MIGRATION_136_A_BANK_STATEMENT_ARRIVES_FROM_THE_SERVICE = `-- ============================================================================
|
|
44903
|
+
-- 136 \u2014 a bank statement arrives from the integration service
|
|
44904
|
+
--
|
|
44905
|
+
-- The V2 sink of the banking module (ADR 0003): the doors through which the
|
|
44906
|
+
-- integration service hands this product the lines of a store's bank or
|
|
44907
|
+
-- credit-card statement, withdraws the ones the bank took back, reports the
|
|
44908
|
+
-- connection's state, and reads which lines are already here. The provider
|
|
44909
|
+
-- calls, the schedule and the waiting are the service's; what a line IS here
|
|
44910
|
+
-- is decided here.
|
|
44911
|
+
--
|
|
44912
|
+
-- A line is keyed as before: (tenant, external_source, external_id), where
|
|
44913
|
+
-- external_source is the statement source's provider ('openbanking:pagbank')
|
|
44914
|
+
-- and external_id the provider's transaction id. Lines already brought in by
|
|
44915
|
+
-- the V1 migration match the ones the service brings, so nothing doubles.
|
|
44916
|
+
--
|
|
44917
|
+
-- Providers revise statements for days: an Open Finance bank can drop a
|
|
44918
|
+
-- transaction and bring it back under a new id. A withdrawn line is marked
|
|
44919
|
+
-- (provider_deleted), never deleted, and comes back if the provider brings
|
|
44920
|
+
-- the same id again. A line already reconciled keeps its money and its date:
|
|
44921
|
+
-- a revision of either is recorded beside it, for a person to look at.
|
|
44922
|
+
--
|
|
44923
|
+
-- No person is behind these calls; the actor is the service, acting for the
|
|
44924
|
+
-- tenant it names. The functions are closed to every signed-in user and
|
|
44925
|
+
-- opened only to the service's role, \`fayz_integration\`, when it exists.
|
|
44926
|
+
-- ============================================================================
|
|
44927
|
+
|
|
44928
|
+
CREATE OR REPLACE FUNCTION public.financial_statement_source_get(p_tenant uuid, p_source uuid)
|
|
44929
|
+
RETURNS jsonb
|
|
44930
|
+
LANGUAGE sql STABLE SECURITY DEFINER
|
|
44931
|
+
SET search_path TO ''
|
|
44932
|
+
AS $$
|
|
44933
|
+
SELECT jsonb_build_object(
|
|
44934
|
+
'id', s.id, 'tenantId', s.tenant_id, 'unitId', s.unit_id, 'accountId', s.account_id,
|
|
44935
|
+
'provider', s.provider, 'kind', s.statement_kind, 'cardLast4', s.card_last4,
|
|
44936
|
+
'accountRef', s.provider_account_hash, 'active', s.is_active,
|
|
44937
|
+
'lastSyncedAt', s.last_synced_at, 'lastSyncStatus', s.last_sync_status
|
|
44938
|
+
)
|
|
44939
|
+
FROM public.plg_financial_account_statement_sources s
|
|
44940
|
+
WHERE s.id = p_source AND s.tenant_id = p_tenant
|
|
44941
|
+
$$;
|
|
44942
|
+
|
|
44943
|
+
-- Every source tied to a provider account, across tenants: how the service
|
|
44944
|
+
-- finds the connections the V1 migration left, to adopt them.
|
|
44945
|
+
CREATE OR REPLACE FUNCTION public.financial_statement_sources_linked(p_provider_prefix text)
|
|
44946
|
+
RETURNS SETOF jsonb
|
|
44947
|
+
LANGUAGE sql STABLE SECURITY DEFINER
|
|
44948
|
+
SET search_path TO ''
|
|
44949
|
+
AS $$
|
|
44950
|
+
SELECT jsonb_build_object(
|
|
44951
|
+
'id', s.id, 'tenantId', s.tenant_id, 'unitId', s.unit_id, 'accountId', s.account_id,
|
|
44952
|
+
'provider', s.provider, 'kind', s.statement_kind, 'cardLast4', s.card_last4,
|
|
44953
|
+
'accountRef', s.provider_account_hash, 'active', s.is_active
|
|
44954
|
+
)
|
|
44955
|
+
FROM public.plg_financial_account_statement_sources s
|
|
44956
|
+
WHERE s.provider LIKE p_provider_prefix || '%' AND s.provider_account_hash IS NOT NULL
|
|
44957
|
+
ORDER BY s.tenant_id, s.created_at
|
|
44958
|
+
$$;
|
|
44959
|
+
|
|
44960
|
+
-- Ties a source to the provider's account, once the store has authorized it.
|
|
44961
|
+
CREATE OR REPLACE FUNCTION public.financial_statement_source_link(p_tenant uuid, p_source uuid, p_account_ref text)
|
|
44962
|
+
RETURNS void
|
|
44963
|
+
LANGUAGE plpgsql SECURITY DEFINER
|
|
44964
|
+
SET search_path TO ''
|
|
44965
|
+
AS $$
|
|
44966
|
+
BEGIN
|
|
44967
|
+
IF p_account_ref IS NULL OR btrim(p_account_ref) = '' THEN
|
|
44968
|
+
RAISE EXCEPTION 'financial_statement_source_link: account ref is required' USING ERRCODE = '22023';
|
|
44969
|
+
END IF;
|
|
44970
|
+
UPDATE public.plg_financial_account_statement_sources
|
|
44971
|
+
SET provider_account_hash = btrim(p_account_ref), updated_at = now()
|
|
44972
|
+
WHERE id = p_source AND tenant_id = p_tenant;
|
|
44973
|
+
IF NOT FOUND THEN
|
|
44974
|
+
RAISE EXCEPTION 'financial_statement_source_link: source % is not of tenant %', p_source, p_tenant USING ERRCODE = 'P0002';
|
|
44975
|
+
END IF;
|
|
44976
|
+
END
|
|
44977
|
+
$$;
|
|
44978
|
+
|
|
44979
|
+
CREATE OR REPLACE FUNCTION public.financial_statement_lines_ingest(p_tenant uuid, p_source uuid, p_lines jsonb)
|
|
44980
|
+
RETURNS jsonb
|
|
44981
|
+
LANGUAGE plpgsql SECURITY DEFINER
|
|
44982
|
+
SET search_path TO ''
|
|
44983
|
+
AS $$
|
|
44984
|
+
DECLARE
|
|
44985
|
+
v_source public.plg_financial_account_statement_sources%ROWTYPE;
|
|
44986
|
+
v_line jsonb;
|
|
44987
|
+
v_amount numeric;
|
|
44988
|
+
v_day date;
|
|
44989
|
+
v_method text;
|
|
44990
|
+
v_meta jsonb;
|
|
44991
|
+
v_existing public.plg_financial_bank_statement_lines%ROWTYPE;
|
|
44992
|
+
v_inserted integer := 0;
|
|
44993
|
+
v_updated integer := 0;
|
|
44994
|
+
BEGIN
|
|
44995
|
+
SELECT * INTO v_source FROM public.plg_financial_account_statement_sources
|
|
44996
|
+
WHERE id = p_source AND tenant_id = p_tenant;
|
|
44997
|
+
IF NOT FOUND THEN
|
|
44998
|
+
RAISE EXCEPTION 'financial_statement_lines_ingest: source % is not of tenant %', p_source, p_tenant USING ERRCODE = 'P0002';
|
|
44999
|
+
END IF;
|
|
45000
|
+
IF jsonb_typeof(p_lines) <> 'array' THEN
|
|
45001
|
+
RAISE EXCEPTION 'financial_statement_lines_ingest: lines must be an array' USING ERRCODE = '22023';
|
|
45002
|
+
END IF;
|
|
45003
|
+
|
|
45004
|
+
FOR v_line IN SELECT value FROM jsonb_array_elements(p_lines) LOOP
|
|
45005
|
+
IF coalesce(v_line ->> 'externalId', '') = '' OR (v_line ->> 'direction') NOT IN ('credit', 'debit') THEN
|
|
45006
|
+
RAISE EXCEPTION 'financial_statement_lines_ingest: a line needs externalId and direction' USING ERRCODE = '22023';
|
|
45007
|
+
END IF;
|
|
45008
|
+
v_amount := round((v_line ->> 'amountCents')::numeric / 100, 2);
|
|
45009
|
+
v_day := (v_line ->> 'postedOn')::date;
|
|
45010
|
+
IF v_amount IS NULL OR v_amount < 0 OR v_day IS NULL THEN
|
|
45011
|
+
RAISE EXCEPTION 'financial_statement_lines_ingest: line % needs a positive amount and a day', v_line ->> 'externalId' USING ERRCODE = '22023';
|
|
45012
|
+
END IF;
|
|
45013
|
+
v_method := nullif(v_line ->> 'paymentMethod', '');
|
|
45014
|
+
v_meta := jsonb_strip_nulls(jsonb_build_object(
|
|
45015
|
+
'statement', jsonb_build_object('ref', v_line ->> 'statementRef', 'via', 'integration-service'),
|
|
45016
|
+
'paymentMethod', v_method,
|
|
45017
|
+
'currency', coalesce(v_line ->> 'currency', 'BRL'),
|
|
45018
|
+
'boleto', v_line -> 'boleto',
|
|
45019
|
+
'card', v_line -> 'card'
|
|
45020
|
+
));
|
|
45021
|
+
|
|
45022
|
+
SELECT * INTO v_existing FROM public.plg_financial_bank_statement_lines
|
|
45023
|
+
WHERE tenant_id = p_tenant AND external_source = v_source.provider AND external_id = v_line ->> 'externalId';
|
|
45024
|
+
|
|
45025
|
+
IF NOT FOUND THEN
|
|
45026
|
+
INSERT INTO public.plg_financial_bank_statement_lines (
|
|
45027
|
+
tenant_id, unit_id, bank_account_id, external_source, external_id, posted_on, direction, amount,
|
|
45028
|
+
description, counterparty_name, counterparty_document, document, code, category, transaction_type,
|
|
45029
|
+
provider_deleted, raw_payload, metadata
|
|
45030
|
+
) VALUES (
|
|
45031
|
+
p_tenant, v_source.unit_id, v_source.account_id, v_source.provider, v_line ->> 'externalId', v_day,
|
|
45032
|
+
v_line ->> 'direction', v_amount,
|
|
45033
|
+
nullif(v_line ->> 'description', ''), nullif(v_line #>> '{counterparty,name}', ''),
|
|
45034
|
+
nullif(v_line #>> '{counterparty,document}', ''), nullif(v_line ->> 'reference', ''),
|
|
45035
|
+
nullif(v_line ->> 'code', ''), nullif(v_line ->> 'category', ''),
|
|
45036
|
+
coalesce(v_method, upper(v_line ->> 'direction')),
|
|
45037
|
+
false, coalesce(v_line -> 'raw', '{}'::jsonb), v_meta
|
|
45038
|
+
);
|
|
45039
|
+
v_inserted := v_inserted + 1;
|
|
45040
|
+
ELSE
|
|
45041
|
+
UPDATE public.plg_financial_bank_statement_lines l SET
|
|
45042
|
+
-- Money, day and direction move only while nobody has reconciled the line.
|
|
45043
|
+
posted_on = CASE WHEN l.reconciled_at IS NULL THEN v_day ELSE l.posted_on END,
|
|
45044
|
+
amount = CASE WHEN l.reconciled_at IS NULL THEN v_amount ELSE l.amount END,
|
|
45045
|
+
direction = CASE WHEN l.reconciled_at IS NULL THEN v_line ->> 'direction' ELSE l.direction END,
|
|
45046
|
+
description = coalesce(nullif(v_line ->> 'description', ''), l.description),
|
|
45047
|
+
counterparty_name = coalesce(nullif(v_line #>> '{counterparty,name}', ''), l.counterparty_name),
|
|
45048
|
+
counterparty_document = coalesce(nullif(v_line #>> '{counterparty,document}', ''), l.counterparty_document),
|
|
45049
|
+
document = coalesce(nullif(v_line ->> 'reference', ''), l.document),
|
|
45050
|
+
code = coalesce(nullif(v_line ->> 'code', ''), l.code),
|
|
45051
|
+
category = coalesce(nullif(v_line ->> 'category', ''), l.category),
|
|
45052
|
+
transaction_type = coalesce(v_method, l.transaction_type),
|
|
45053
|
+
provider_deleted = false,
|
|
45054
|
+
raw_payload = coalesce(v_line -> 'raw', l.raw_payload),
|
|
45055
|
+
metadata = (l.metadata - 'withdrawn') || v_meta
|
|
45056
|
+
|| CASE WHEN l.reconciled_at IS NOT NULL AND (l.amount <> v_amount OR l.posted_on <> v_day OR l.direction <> v_line ->> 'direction')
|
|
45057
|
+
THEN jsonb_build_object('revisedAfterReconciliation', jsonb_build_object(
|
|
45058
|
+
'amount', v_amount, 'postedOn', v_day, 'direction', v_line ->> 'direction', 'at', now()))
|
|
45059
|
+
ELSE '{}'::jsonb END,
|
|
45060
|
+
updated_at = now()
|
|
45061
|
+
WHERE l.id = v_existing.id;
|
|
45062
|
+
v_updated := v_updated + 1;
|
|
45063
|
+
END IF;
|
|
45064
|
+
END LOOP;
|
|
45065
|
+
|
|
45066
|
+
RETURN jsonb_build_object('inserted', v_inserted, 'updated', v_updated);
|
|
45067
|
+
END
|
|
45068
|
+
$$;
|
|
45069
|
+
|
|
45070
|
+
CREATE OR REPLACE FUNCTION public.financial_statement_lines_withdraw(p_tenant uuid, p_source uuid, p_external_ids text[], p_reason text)
|
|
45071
|
+
RETURNS integer
|
|
45072
|
+
LANGUAGE plpgsql SECURITY DEFINER
|
|
45073
|
+
SET search_path TO ''
|
|
45074
|
+
AS $$
|
|
45075
|
+
DECLARE
|
|
45076
|
+
v_source public.plg_financial_account_statement_sources%ROWTYPE;
|
|
45077
|
+
v_count integer;
|
|
45078
|
+
BEGIN
|
|
45079
|
+
SELECT * INTO v_source FROM public.plg_financial_account_statement_sources
|
|
45080
|
+
WHERE id = p_source AND tenant_id = p_tenant;
|
|
45081
|
+
IF NOT FOUND THEN
|
|
45082
|
+
RAISE EXCEPTION 'financial_statement_lines_withdraw: source % is not of tenant %', p_source, p_tenant USING ERRCODE = 'P0002';
|
|
45083
|
+
END IF;
|
|
45084
|
+
UPDATE public.plg_financial_bank_statement_lines l
|
|
45085
|
+
SET provider_deleted = true,
|
|
45086
|
+
metadata = l.metadata || jsonb_build_object('withdrawn', jsonb_build_object('reason', p_reason, 'at', now())),
|
|
45087
|
+
updated_at = now()
|
|
45088
|
+
WHERE l.tenant_id = p_tenant AND l.external_source = v_source.provider
|
|
45089
|
+
AND l.bank_account_id = v_source.account_id
|
|
45090
|
+
AND l.external_id = ANY (p_external_ids) AND NOT l.provider_deleted;
|
|
45091
|
+
GET DIAGNOSTICS v_count = ROW_COUNT;
|
|
45092
|
+
RETURN v_count;
|
|
45093
|
+
END
|
|
45094
|
+
$$;
|
|
45095
|
+
|
|
45096
|
+
-- The lines here for a source in a window, withdrawn ones left out: what the
|
|
45097
|
+
-- service compares with the provider to find the ones the bank took back.
|
|
45098
|
+
CREATE OR REPLACE FUNCTION public.financial_statement_lines_known(p_tenant uuid, p_source uuid, p_from date, p_to date)
|
|
45099
|
+
RETURNS SETOF text
|
|
45100
|
+
LANGUAGE sql STABLE SECURITY DEFINER
|
|
45101
|
+
SET search_path TO ''
|
|
45102
|
+
AS $$
|
|
45103
|
+
SELECT l.external_id
|
|
45104
|
+
FROM public.plg_financial_bank_statement_lines l
|
|
45105
|
+
JOIN public.plg_financial_account_statement_sources s
|
|
45106
|
+
ON s.id = p_source AND s.tenant_id = p_tenant
|
|
45107
|
+
WHERE l.tenant_id = p_tenant AND l.external_source = s.provider AND l.bank_account_id = s.account_id
|
|
45108
|
+
AND l.posted_on BETWEEN p_from AND p_to AND NOT l.provider_deleted
|
|
45109
|
+
$$;
|
|
45110
|
+
|
|
45111
|
+
CREATE OR REPLACE FUNCTION public.financial_statement_source_report(
|
|
45112
|
+
p_tenant uuid, p_source uuid, p_status text, p_error text, p_details jsonb
|
|
45113
|
+
) RETURNS void
|
|
45114
|
+
LANGUAGE plpgsql SECURITY DEFINER
|
|
45115
|
+
SET search_path TO ''
|
|
45116
|
+
AS $$
|
|
45117
|
+
BEGIN
|
|
45118
|
+
IF p_status NOT IN ('ok', 'error', 'pending', 'revoked') THEN
|
|
45119
|
+
RAISE EXCEPTION 'financial_statement_source_report: unknown status %', p_status USING ERRCODE = '22023';
|
|
45120
|
+
END IF;
|
|
45121
|
+
UPDATE public.plg_financial_account_statement_sources s
|
|
45122
|
+
SET last_sync_status = p_status,
|
|
45123
|
+
last_sync_error = CASE WHEN p_status = 'error' THEN left(p_error, 500) ELSE NULL END,
|
|
45124
|
+
last_synced_at = CASE WHEN p_status = 'ok' THEN now() ELSE s.last_synced_at END,
|
|
45125
|
+
metadata = s.metadata || jsonb_build_object('connection', coalesce(p_details, '{}'::jsonb)),
|
|
45126
|
+
updated_at = now()
|
|
45127
|
+
WHERE s.id = p_source AND s.tenant_id = p_tenant;
|
|
45128
|
+
IF NOT FOUND THEN
|
|
45129
|
+
RAISE EXCEPTION 'financial_statement_source_report: source % is not of tenant %', p_source, p_tenant USING ERRCODE = 'P0002';
|
|
45130
|
+
END IF;
|
|
45131
|
+
END
|
|
45132
|
+
$$;
|
|
45133
|
+
|
|
45134
|
+
REVOKE ALL ON FUNCTION public.financial_statement_source_get(uuid, uuid) FROM PUBLIC, anon, authenticated;
|
|
45135
|
+
REVOKE ALL ON FUNCTION public.financial_statement_sources_linked(text) FROM PUBLIC, anon, authenticated;
|
|
45136
|
+
REVOKE ALL ON FUNCTION public.financial_statement_source_link(uuid, uuid, text) FROM PUBLIC, anon, authenticated;
|
|
45137
|
+
REVOKE ALL ON FUNCTION public.financial_statement_lines_ingest(uuid, uuid, jsonb) FROM PUBLIC, anon, authenticated;
|
|
45138
|
+
REVOKE ALL ON FUNCTION public.financial_statement_lines_withdraw(uuid, uuid, text[], text) FROM PUBLIC, anon, authenticated;
|
|
45139
|
+
REVOKE ALL ON FUNCTION public.financial_statement_lines_known(uuid, uuid, date, date) FROM PUBLIC, anon, authenticated;
|
|
45140
|
+
REVOKE ALL ON FUNCTION public.financial_statement_source_report(uuid, uuid, text, text, jsonb) FROM PUBLIC, anon, authenticated;
|
|
45141
|
+
|
|
45142
|
+
DO $$
|
|
45143
|
+
BEGIN
|
|
45144
|
+
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'fayz_integration') THEN
|
|
45145
|
+
GRANT USAGE ON SCHEMA public TO fayz_integration;
|
|
45146
|
+
GRANT EXECUTE ON FUNCTION public.financial_statement_source_get(uuid, uuid) TO fayz_integration;
|
|
45147
|
+
GRANT EXECUTE ON FUNCTION public.financial_statement_sources_linked(text) TO fayz_integration;
|
|
45148
|
+
GRANT EXECUTE ON FUNCTION public.financial_statement_source_link(uuid, uuid, text) TO fayz_integration;
|
|
45149
|
+
GRANT EXECUTE ON FUNCTION public.financial_statement_lines_ingest(uuid, uuid, jsonb) TO fayz_integration;
|
|
45150
|
+
GRANT EXECUTE ON FUNCTION public.financial_statement_lines_withdraw(uuid, uuid, text[], text) TO fayz_integration;
|
|
45151
|
+
GRANT EXECUTE ON FUNCTION public.financial_statement_lines_known(uuid, uuid, date, date) TO fayz_integration;
|
|
45152
|
+
GRANT EXECUTE ON FUNCTION public.financial_statement_source_report(uuid, uuid, text, text, jsonb) TO fayz_integration;
|
|
45153
|
+
END IF;
|
|
45154
|
+
END
|
|
45155
|
+
$$;
|
|
45156
|
+
`;
|
|
44902
45157
|
var MIGRATIONS = [
|
|
44903
45158
|
{ id: "000_baseline", sql: MIGRATION_000_BASELINE },
|
|
44904
45159
|
{ id: "001_the_comanda_becomes_a_receivable_when_it_closes", sql: MIGRATION_001_THE_COMANDA_BECOMES_A_RECEIVABLE_WHEN_IT_CLOSES },
|
|
@@ -45035,7 +45290,8 @@ var MIGRATIONS = [
|
|
|
45035
45290
|
{ id: "132_the_statement_source_knows_its_provider_account", sql: MIGRATION_132_THE_STATEMENT_SOURCE_KNOWS_ITS_PROVIDER_ACCOUNT },
|
|
45036
45291
|
{ id: "133_reversing_a_payment_releases_its_bank_line", sql: MIGRATION_133_REVERSING_A_PAYMENT_RELEASES_ITS_BANK_LINE },
|
|
45037
45292
|
{ id: "134_a_bill_knows_who_filed_it", sql: MIGRATION_134_A_BILL_KNOWS_WHO_FILED_IT },
|
|
45038
|
-
{ id: "135_a_bill_and_its_lines_are_written_together", sql: MIGRATION_135_A_BILL_AND_ITS_LINES_ARE_WRITTEN_TOGETHER }
|
|
45293
|
+
{ id: "135_a_bill_and_its_lines_are_written_together", sql: MIGRATION_135_A_BILL_AND_ITS_LINES_ARE_WRITTEN_TOGETHER },
|
|
45294
|
+
{ id: "136_a_bank_statement_arrives_from_the_service", sql: MIGRATION_136_A_BANK_STATEMENT_ARRIVES_FROM_THE_SERVICE }
|
|
45039
45295
|
];
|
|
45040
45296
|
var ACQUIRERS = ["stone", "cielo", "pagbank", "mercadopago"];
|
|
45041
45297
|
var LOGOS = "https://assets.fayz.ai/connectors";
|