@fayz-ai/plugin-banking-br 0.10.42 → 0.10.44
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
CHANGED
|
@@ -3,6 +3,7 @@ import { Download, Loader2 } from 'lucide-react';
|
|
|
3
3
|
import { DatePicker, Button, Checkbox, toast } from '@fayz-ai/ui';
|
|
4
4
|
import { connectionStatus, formatCurrency, createConnectionStore, getSupabaseClientOptional } from '@fayz-ai/core';
|
|
5
5
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
6
|
+
import { MIGRATIONS } from '#migrations/main';
|
|
6
7
|
|
|
7
8
|
// src/connectors/connectorDef.tsx
|
|
8
9
|
var FUNCTION = "plugbank-sync";
|
|
@@ -384,614 +385,6 @@ function buildBankingDomainEvents() {
|
|
|
384
385
|
}
|
|
385
386
|
];
|
|
386
387
|
}
|
|
387
|
-
|
|
388
|
-
// src/migrations/index.ts
|
|
389
|
-
var MIGRATION_000_BASELINE = `-- ============================================================================
|
|
390
|
-
-- plugins/plugin-banking-br/src/migrations/000_baseline.sql \u2014 what installing this provisions, as one file.
|
|
391
|
-
--
|
|
392
|
-
-- GENERATED by scripts/emit-unit-baselines.mjs (#338). Do not edit by hand:
|
|
393
|
-
-- change the schema, regenerate, and let scripts/check-chain-equivalence.mjs
|
|
394
|
-
-- prove the result still matches.
|
|
395
|
-
--
|
|
396
|
-
-- This replaced 2 migration files. They are kept, unapplied, in
|
|
397
|
-
-- migrations.archive/ \u2014 a year of decisions is worth reading even when it is no
|
|
398
|
-
-- longer worth replaying.
|
|
399
|
-
--
|
|
400
|
-
-- Object order is fixed rather than dependency-sorted: schemas, types,
|
|
401
|
-
-- sequences, tables, defaults, functions, constraints, views, indexes, foreign
|
|
402
|
-
-- keys, triggers, row security, policies, grants. Dependencies BETWEEN units
|
|
403
|
-
-- are carried by the order packages/db/chain.json declares.
|
|
404
|
-
-- ============================================================================
|
|
405
|
-
|
|
406
|
-
-- A LANGUAGE sql function binds its body at CREATE time, and 570 functions
|
|
407
|
-
-- sorted by name are not sorted by who calls whom. This is what lets them load
|
|
408
|
-
-- in any order; every body is still compiled on first call, so a genuinely
|
|
409
|
-
-- broken reference surfaces then rather than never. Session-scoped.
|
|
410
|
-
SET check_function_bodies = false;
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
-- \u2500\u2500 default privileges: reset \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
415
|
-
-- Cleared BEFORE anything is created, so every object below gets the ACL the
|
|
416
|
-
-- dump describes rather than the image's defaults on top of it. Restored at
|
|
417
|
-
-- the end of this file.
|
|
418
|
-
|
|
419
|
-
DO $dp$ BEGIN
|
|
420
|
-
EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public REVOKE ALL ON SEQUENCES FROM anon, authenticated, service_role, fayz_connector$stmt$;
|
|
421
|
-
EXCEPTION WHEN insufficient_privilege OR undefined_object THEN
|
|
422
|
-
RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public REVOKE ALL ON SEQUENCES FROM anon, authenticated, service_role, fayz_connector$stmt$;
|
|
423
|
-
END $dp$;
|
|
424
|
-
|
|
425
|
-
DO $dp$ BEGIN
|
|
426
|
-
EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public REVOKE ALL ON SEQUENCES FROM anon, authenticated, service_role, fayz_connector$stmt$;
|
|
427
|
-
EXCEPTION WHEN insufficient_privilege OR undefined_object THEN
|
|
428
|
-
RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public REVOKE ALL ON SEQUENCES FROM anon, authenticated, service_role, fayz_connector$stmt$;
|
|
429
|
-
END $dp$;
|
|
430
|
-
|
|
431
|
-
DO $dp$ BEGIN
|
|
432
|
-
EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public REVOKE ALL ON FUNCTIONS FROM anon, authenticated, service_role, fayz_connector$stmt$;
|
|
433
|
-
EXCEPTION WHEN insufficient_privilege OR undefined_object THEN
|
|
434
|
-
RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public REVOKE ALL ON FUNCTIONS FROM anon, authenticated, service_role, fayz_connector$stmt$;
|
|
435
|
-
END $dp$;
|
|
436
|
-
|
|
437
|
-
DO $dp$ BEGIN
|
|
438
|
-
EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public REVOKE ALL ON FUNCTIONS FROM anon, authenticated, service_role, fayz_connector$stmt$;
|
|
439
|
-
EXCEPTION WHEN insufficient_privilege OR undefined_object THEN
|
|
440
|
-
RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public REVOKE ALL ON FUNCTIONS FROM anon, authenticated, service_role, fayz_connector$stmt$;
|
|
441
|
-
END $dp$;
|
|
442
|
-
|
|
443
|
-
DO $dp$ BEGIN
|
|
444
|
-
EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public REVOKE ALL ON TABLES FROM anon, authenticated, service_role, fayz_connector$stmt$;
|
|
445
|
-
EXCEPTION WHEN insufficient_privilege OR undefined_object THEN
|
|
446
|
-
RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public REVOKE ALL ON TABLES FROM anon, authenticated, service_role, fayz_connector$stmt$;
|
|
447
|
-
END $dp$;
|
|
448
|
-
|
|
449
|
-
DO $dp$ BEGIN
|
|
450
|
-
EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public REVOKE ALL ON TABLES FROM anon, authenticated, service_role, fayz_connector$stmt$;
|
|
451
|
-
EXCEPTION WHEN insufficient_privilege OR undefined_object THEN
|
|
452
|
-
RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public REVOKE ALL ON TABLES FROM anon, authenticated, service_role, fayz_connector$stmt$;
|
|
453
|
-
END $dp$;
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
-- \u2500\u2500 function \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
457
|
-
|
|
458
|
-
CREATE FUNCTION public.plg_bank_emit_movement_imported() RETURNS trigger
|
|
459
|
-
LANGUAGE plpgsql SECURITY DEFINER
|
|
460
|
-
SET search_path TO 'public'
|
|
461
|
-
AS $$
|
|
462
|
-
DECLARE
|
|
463
|
-
v_payload jsonb;
|
|
464
|
-
BEGIN
|
|
465
|
-
IF NEW.external_source IS NULL OR NEW.external_id IS NULL THEN
|
|
466
|
-
RETURN NULL;
|
|
467
|
-
END IF;
|
|
468
|
-
|
|
469
|
-
-- The contract every consumer reads. Not the row: a whole-row payload would
|
|
470
|
-
-- put the ledger's internal bookkeeping inside every webhook by default.
|
|
471
|
-
v_payload := jsonb_build_object(
|
|
472
|
-
'movement_id', NEW.id::text,
|
|
473
|
-
'provider', NEW.external_source,
|
|
474
|
-
'external_id', NEW.external_id,
|
|
475
|
-
'direction', NEW.direction,
|
|
476
|
-
'amount', NEW.amount,
|
|
477
|
-
'payment_date', NEW.payment_date,
|
|
478
|
-
'description', NEW.notes,
|
|
479
|
-
'bank_account_id', NEW.bank_account_id);
|
|
480
|
-
|
|
481
|
-
BEGIN
|
|
482
|
-
PERFORM public.plg_emit_event(
|
|
483
|
-
'financial.movement.imported', v_payload, 'financial_movement',
|
|
484
|
-
NEW.id::text, NEW.tenant_id, NULL);
|
|
485
|
-
EXCEPTION WHEN OTHERS THEN
|
|
486
|
-
-- Observability must never be able to reject the write that caused it.
|
|
487
|
-
RAISE WARNING '[plg_bank_emit_movement_imported] % on %: %', TG_OP, TG_TABLE_NAME, SQLERRM;
|
|
488
|
-
END;
|
|
489
|
-
|
|
490
|
-
RETURN NULL;
|
|
491
|
-
END;
|
|
492
|
-
$$;
|
|
493
|
-
|
|
494
|
-
CREATE FUNCTION public.plugbank_import_movements(p_payload jsonb) RETURNS jsonb
|
|
495
|
-
LANGUAGE plpgsql SECURITY DEFINER
|
|
496
|
-
SET search_path TO 'public'
|
|
497
|
-
AS $$
|
|
498
|
-
DECLARE
|
|
499
|
-
v_tenants uuid[];
|
|
500
|
-
v_tenant uuid;
|
|
501
|
-
v_connection uuid;
|
|
502
|
-
v_instance text;
|
|
503
|
-
v_account uuid;
|
|
504
|
-
v_connector text;
|
|
505
|
-
v_trigger text;
|
|
506
|
-
v_from date;
|
|
507
|
-
v_to date;
|
|
508
|
-
v_started timestamptz := now();
|
|
509
|
-
v_line jsonb;
|
|
510
|
-
v_external text;
|
|
511
|
-
v_amount numeric(14,2);
|
|
512
|
-
v_date date;
|
|
513
|
-
v_fetched int := 0;
|
|
514
|
-
v_imported int := 0;
|
|
515
|
-
v_duplicates int := 0;
|
|
516
|
-
v_inserted uuid;
|
|
517
|
-
v_run uuid;
|
|
518
|
-
BEGIN
|
|
519
|
-
v_connection := nullif(btrim(p_payload->>'integration_id'), '')::uuid;
|
|
520
|
-
IF v_connection IS NULL THEN
|
|
521
|
-
RAISE EXCEPTION 'plugbank_import_movements: integration_id is required'
|
|
522
|
-
USING ERRCODE = 'invalid_parameter_value';
|
|
523
|
-
END IF;
|
|
524
|
-
|
|
525
|
-
SELECT c.tenant_id, c.instance_key, c.connector_id
|
|
526
|
-
INTO v_tenant, v_instance, v_connector
|
|
527
|
-
FROM public.plg_connections c
|
|
528
|
-
WHERE c.id = v_connection AND c.active;
|
|
529
|
-
|
|
530
|
-
IF v_tenant IS NULL THEN
|
|
531
|
-
RAISE EXCEPTION 'plugbank_import_movements: no active connection %', v_connection
|
|
532
|
-
USING ERRCODE = 'no_data_found';
|
|
533
|
-
END IF;
|
|
534
|
-
|
|
535
|
-
-- The account this connection is for. \`instance_key\` is text because it also
|
|
536
|
-
-- carries things that are not uuids for other connectors (a Bling company, a
|
|
537
|
-
-- gateway instance); here it is the bank account, and '' is the account-less
|
|
538
|
-
-- default that \`bank_account_id IS NULL\` used to mean.
|
|
539
|
-
v_account := nullif(btrim(v_instance), '')::uuid;
|
|
540
|
-
|
|
541
|
-
-- SECURITY DEFINER bypasses RLS, so the membership check the policy would
|
|
542
|
-
-- have made has to happen here.
|
|
543
|
-
SELECT array_agg(t) INTO v_tenants FROM public.user_tenant_ids() AS t;
|
|
544
|
-
IF auth.uid() IS NOT NULL AND NOT (v_tenant = ANY (COALESCE(v_tenants, '{}'::uuid[]))) THEN
|
|
545
|
-
RAISE EXCEPTION 'plugbank_import_movements: caller is not a member of tenant %', v_tenant
|
|
546
|
-
USING ERRCODE = 'insufficient_privilege';
|
|
547
|
-
END IF;
|
|
548
|
-
|
|
549
|
-
-- Refusing beats ignoring: a caller that believes it is importing into
|
|
550
|
-
-- another tenant must be told, not silently redirected into this one.
|
|
551
|
-
IF nullif(btrim(p_payload->>'tenant_id'), '') IS NOT NULL
|
|
552
|
-
AND (p_payload->>'tenant_id')::uuid IS DISTINCT FROM v_tenant THEN
|
|
553
|
-
RAISE EXCEPTION 'plugbank_import_movements: payload names another tenant; the tenant comes from the connection'
|
|
554
|
-
USING ERRCODE = 'insufficient_privilege';
|
|
555
|
-
END IF;
|
|
556
|
-
|
|
557
|
-
-- Same rule as the tenant, one level down: the account is the connection's,
|
|
558
|
-
-- and a payload naming a different one is a caller importing into the wrong
|
|
559
|
-
-- ledger account. 002 let the payload override it silently \u2014 with one
|
|
560
|
-
-- connection per account that override is how two accounts get mixed.
|
|
561
|
-
IF nullif(btrim(p_payload->>'bank_account_id'), '') IS NOT NULL
|
|
562
|
-
AND (p_payload->>'bank_account_id')::uuid IS DISTINCT FROM v_account THEN
|
|
563
|
-
RAISE EXCEPTION 'plugbank_import_movements: payload names another bank account; the account comes from the connection'
|
|
564
|
-
USING ERRCODE = 'insufficient_privilege';
|
|
565
|
-
END IF;
|
|
566
|
-
|
|
567
|
-
v_from := nullif(btrim(p_payload->>'period_from'), '')::date;
|
|
568
|
-
v_to := nullif(btrim(p_payload->>'period_to'), '')::date;
|
|
569
|
-
v_trigger := lower(coalesce(nullif(btrim(p_payload->>'trigger'), ''), 'manual'));
|
|
570
|
-
IF v_trigger NOT IN ('on-write', 'scheduled', 'manual', 'webhook') THEN
|
|
571
|
-
RAISE EXCEPTION 'plugbank_import_movements: unknown trigger %', v_trigger
|
|
572
|
-
USING ERRCODE = 'invalid_parameter_value';
|
|
573
|
-
END IF;
|
|
574
|
-
|
|
575
|
-
FOR v_line IN SELECT * FROM jsonb_array_elements(coalesce(p_payload->'lines', '[]'::jsonb))
|
|
576
|
-
LOOP
|
|
577
|
-
v_fetched := v_fetched + 1;
|
|
578
|
-
v_external := nullif(btrim(v_line->>'external_id'), '');
|
|
579
|
-
v_date := nullif(btrim(v_line->>'date'), '')::date;
|
|
580
|
-
v_amount := abs(coalesce((v_line->>'amount')::numeric, 0));
|
|
581
|
-
|
|
582
|
-
-- A line with no provider id cannot be recognised on re-delivery, so it
|
|
583
|
-
-- would import again on every run. Skipping it is the lesser wrong.
|
|
584
|
-
IF v_external IS NULL OR v_date IS NULL OR v_amount = 0 THEN
|
|
585
|
-
CONTINUE;
|
|
586
|
-
END IF;
|
|
587
|
-
|
|
588
|
-
INSERT INTO public.plg_financial_movements (
|
|
589
|
-
tenant_id, direction, movement_kind, amount, paid_amount, status,
|
|
590
|
-
due_date, payment_date, bank_account_id, notes, external_id, external_source)
|
|
591
|
-
VALUES (
|
|
592
|
-
v_tenant,
|
|
593
|
-
CASE WHEN upper(coalesce(v_line->>'type', 'C')) LIKE 'C%' THEN 'credit' ELSE 'debit' END,
|
|
594
|
-
'payment', v_amount, v_amount, 'paid',
|
|
595
|
-
v_date, v_date, v_account,
|
|
596
|
-
nullif(btrim(v_line->>'description'), ''),
|
|
597
|
-
v_external, coalesce(v_connector, 'plugbank'))
|
|
598
|
-
ON CONFLICT (tenant_id, external_source, external_id)
|
|
599
|
-
WHERE external_id IS NOT NULL AND external_source IS NOT NULL
|
|
600
|
-
DO NOTHING
|
|
601
|
-
RETURNING id INTO v_inserted;
|
|
602
|
-
|
|
603
|
-
IF v_inserted IS NULL THEN
|
|
604
|
-
v_duplicates := v_duplicates + 1;
|
|
605
|
-
ELSE
|
|
606
|
-
v_imported := v_imported + 1;
|
|
607
|
-
END IF;
|
|
608
|
-
v_inserted := NULL;
|
|
609
|
-
END LOOP;
|
|
610
|
-
|
|
611
|
-
UPDATE public.plg_connections
|
|
612
|
-
SET last_sync_at = now(), status = 'connected', status_detail = NULL, updated_at = now()
|
|
613
|
-
WHERE id = v_connection;
|
|
614
|
-
|
|
615
|
-
-- \`stats\` is where the three facts a manual statement import has and a generic
|
|
616
|
-
-- run does not go: the operator picked a period, a re-import of that period is
|
|
617
|
-
-- mostly duplicates by design, and someone pressed the button. The panel reads
|
|
618
|
-
-- all three back from here.
|
|
619
|
-
INSERT INTO public.plg_sync_runs (
|
|
620
|
-
tenant_id, connection_id, connector_id, direction, trigger_kind, status,
|
|
621
|
-
stream, fetched, written, stats, started_at, finished_at)
|
|
622
|
-
VALUES (
|
|
623
|
-
v_tenant, v_connection, coalesce(v_connector, 'plugbank'), 'inbound', v_trigger, 'success',
|
|
624
|
-
'statement', v_fetched, v_imported,
|
|
625
|
-
jsonb_strip_nulls(jsonb_build_object(
|
|
626
|
-
'duplicates', v_duplicates,
|
|
627
|
-
'period_from', v_from,
|
|
628
|
-
'period_to', v_to,
|
|
629
|
-
'triggered_by_user_id', auth.uid())),
|
|
630
|
-
v_started, now())
|
|
631
|
-
RETURNING id INTO v_run;
|
|
632
|
-
|
|
633
|
-
RETURN jsonb_build_object(
|
|
634
|
-
'ok', true,
|
|
635
|
-
'sync_run_id', v_run,
|
|
636
|
-
'fetched', v_fetched,
|
|
637
|
-
'imported', v_imported,
|
|
638
|
-
'duplicates', v_duplicates);
|
|
639
|
-
END;
|
|
640
|
-
$$;
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
-- \u2500\u2500 trigger \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
644
|
-
|
|
645
|
-
CREATE TRIGGER plg_financial_movements_emit_imported AFTER INSERT ON public.plg_financial_movements FOR EACH ROW EXECUTE FUNCTION public.plg_bank_emit_movement_imported();
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
-- \u2500\u2500 acl \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
649
|
-
|
|
650
|
-
REVOKE ALL ON FUNCTION public.plg_bank_emit_movement_imported() FROM PUBLIC;
|
|
651
|
-
GRANT ALL ON FUNCTION public.plg_bank_emit_movement_imported() TO authenticated;
|
|
652
|
-
GRANT ALL ON FUNCTION public.plg_bank_emit_movement_imported() TO service_role;
|
|
653
|
-
|
|
654
|
-
REVOKE ALL ON FUNCTION public.plugbank_import_movements(p_payload jsonb) FROM PUBLIC;
|
|
655
|
-
GRANT ALL ON FUNCTION public.plugbank_import_movements(p_payload jsonb) TO authenticated;
|
|
656
|
-
GRANT ALL ON FUNCTION public.plugbank_import_movements(p_payload jsonb) TO service_role;
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
-- \u2500\u2500 comment \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
660
|
-
|
|
661
|
-
COMMENT ON FUNCTION public.plugbank_import_movements(p_payload jsonb) IS 'Import bank-statement lines into the financial ledger and file the run in plg_sync_runs. The tenant AND the bank account come from the plg_connections row named by integration_id, never from the payload.';
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
-- \u2500\u2500 default privileges: restore \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
665
|
-
-- Put back, so the NEXT thing installed inherits what the chain had.
|
|
666
|
-
|
|
667
|
-
DO $dp$ BEGIN
|
|
668
|
-
EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON SEQUENCES TO postgres$stmt$;
|
|
669
|
-
EXCEPTION WHEN insufficient_privilege OR undefined_object THEN
|
|
670
|
-
RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON SEQUENCES TO postgres$stmt$;
|
|
671
|
-
END $dp$;
|
|
672
|
-
|
|
673
|
-
DO $dp$ BEGIN
|
|
674
|
-
EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON SEQUENCES TO anon$stmt$;
|
|
675
|
-
EXCEPTION WHEN insufficient_privilege OR undefined_object THEN
|
|
676
|
-
RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON SEQUENCES TO anon$stmt$;
|
|
677
|
-
END $dp$;
|
|
678
|
-
|
|
679
|
-
DO $dp$ BEGIN
|
|
680
|
-
EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON SEQUENCES TO authenticated$stmt$;
|
|
681
|
-
EXCEPTION WHEN insufficient_privilege OR undefined_object THEN
|
|
682
|
-
RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON SEQUENCES TO authenticated$stmt$;
|
|
683
|
-
END $dp$;
|
|
684
|
-
|
|
685
|
-
DO $dp$ BEGIN
|
|
686
|
-
EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON SEQUENCES TO service_role$stmt$;
|
|
687
|
-
EXCEPTION WHEN insufficient_privilege OR undefined_object THEN
|
|
688
|
-
RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON SEQUENCES TO service_role$stmt$;
|
|
689
|
-
END $dp$;
|
|
690
|
-
|
|
691
|
-
DO $dp$ BEGIN
|
|
692
|
-
EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON SEQUENCES TO postgres$stmt$;
|
|
693
|
-
EXCEPTION WHEN insufficient_privilege OR undefined_object THEN
|
|
694
|
-
RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON SEQUENCES TO postgres$stmt$;
|
|
695
|
-
END $dp$;
|
|
696
|
-
|
|
697
|
-
DO $dp$ BEGIN
|
|
698
|
-
EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON SEQUENCES TO anon$stmt$;
|
|
699
|
-
EXCEPTION WHEN insufficient_privilege OR undefined_object THEN
|
|
700
|
-
RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON SEQUENCES TO anon$stmt$;
|
|
701
|
-
END $dp$;
|
|
702
|
-
|
|
703
|
-
DO $dp$ BEGIN
|
|
704
|
-
EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON SEQUENCES TO authenticated$stmt$;
|
|
705
|
-
EXCEPTION WHEN insufficient_privilege OR undefined_object THEN
|
|
706
|
-
RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON SEQUENCES TO authenticated$stmt$;
|
|
707
|
-
END $dp$;
|
|
708
|
-
|
|
709
|
-
DO $dp$ BEGIN
|
|
710
|
-
EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON SEQUENCES TO service_role$stmt$;
|
|
711
|
-
EXCEPTION WHEN insufficient_privilege OR undefined_object THEN
|
|
712
|
-
RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON SEQUENCES TO service_role$stmt$;
|
|
713
|
-
END $dp$;
|
|
714
|
-
|
|
715
|
-
DO $dp$ BEGIN
|
|
716
|
-
EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON FUNCTIONS TO postgres$stmt$;
|
|
717
|
-
EXCEPTION WHEN insufficient_privilege OR undefined_object THEN
|
|
718
|
-
RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON FUNCTIONS TO postgres$stmt$;
|
|
719
|
-
END $dp$;
|
|
720
|
-
|
|
721
|
-
DO $dp$ BEGIN
|
|
722
|
-
EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON FUNCTIONS TO authenticated$stmt$;
|
|
723
|
-
EXCEPTION WHEN insufficient_privilege OR undefined_object THEN
|
|
724
|
-
RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON FUNCTIONS TO authenticated$stmt$;
|
|
725
|
-
END $dp$;
|
|
726
|
-
|
|
727
|
-
DO $dp$ BEGIN
|
|
728
|
-
EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON FUNCTIONS TO service_role$stmt$;
|
|
729
|
-
EXCEPTION WHEN insufficient_privilege OR undefined_object THEN
|
|
730
|
-
RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON FUNCTIONS TO service_role$stmt$;
|
|
731
|
-
END $dp$;
|
|
732
|
-
|
|
733
|
-
DO $dp$ BEGIN
|
|
734
|
-
EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON FUNCTIONS TO postgres$stmt$;
|
|
735
|
-
EXCEPTION WHEN insufficient_privilege OR undefined_object THEN
|
|
736
|
-
RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON FUNCTIONS TO postgres$stmt$;
|
|
737
|
-
END $dp$;
|
|
738
|
-
|
|
739
|
-
DO $dp$ BEGIN
|
|
740
|
-
EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON FUNCTIONS TO anon$stmt$;
|
|
741
|
-
EXCEPTION WHEN insufficient_privilege OR undefined_object THEN
|
|
742
|
-
RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON FUNCTIONS TO anon$stmt$;
|
|
743
|
-
END $dp$;
|
|
744
|
-
|
|
745
|
-
DO $dp$ BEGIN
|
|
746
|
-
EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON FUNCTIONS TO authenticated$stmt$;
|
|
747
|
-
EXCEPTION WHEN insufficient_privilege OR undefined_object THEN
|
|
748
|
-
RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON FUNCTIONS TO authenticated$stmt$;
|
|
749
|
-
END $dp$;
|
|
750
|
-
|
|
751
|
-
DO $dp$ BEGIN
|
|
752
|
-
EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON FUNCTIONS TO service_role$stmt$;
|
|
753
|
-
EXCEPTION WHEN insufficient_privilege OR undefined_object THEN
|
|
754
|
-
RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON FUNCTIONS TO service_role$stmt$;
|
|
755
|
-
END $dp$;
|
|
756
|
-
|
|
757
|
-
DO $dp$ BEGIN
|
|
758
|
-
EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON TABLES TO postgres$stmt$;
|
|
759
|
-
EXCEPTION WHEN insufficient_privilege OR undefined_object THEN
|
|
760
|
-
RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON TABLES TO postgres$stmt$;
|
|
761
|
-
END $dp$;
|
|
762
|
-
|
|
763
|
-
DO $dp$ BEGIN
|
|
764
|
-
EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT MAINTAIN ON TABLES TO anon$stmt$;
|
|
765
|
-
EXCEPTION WHEN insufficient_privilege OR undefined_object THEN
|
|
766
|
-
RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT MAINTAIN ON TABLES TO anon$stmt$;
|
|
767
|
-
END $dp$;
|
|
768
|
-
|
|
769
|
-
DO $dp$ BEGIN
|
|
770
|
-
EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON TABLES TO authenticated$stmt$;
|
|
771
|
-
EXCEPTION WHEN insufficient_privilege OR undefined_object THEN
|
|
772
|
-
RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON TABLES TO authenticated$stmt$;
|
|
773
|
-
END $dp$;
|
|
774
|
-
|
|
775
|
-
DO $dp$ BEGIN
|
|
776
|
-
EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON TABLES TO service_role$stmt$;
|
|
777
|
-
EXCEPTION WHEN insufficient_privilege OR undefined_object THEN
|
|
778
|
-
RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON TABLES TO service_role$stmt$;
|
|
779
|
-
END $dp$;
|
|
780
|
-
|
|
781
|
-
DO $dp$ BEGIN
|
|
782
|
-
EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON TABLES TO postgres$stmt$;
|
|
783
|
-
EXCEPTION WHEN insufficient_privilege OR undefined_object THEN
|
|
784
|
-
RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON TABLES TO postgres$stmt$;
|
|
785
|
-
END $dp$;
|
|
786
|
-
|
|
787
|
-
DO $dp$ BEGIN
|
|
788
|
-
EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON TABLES TO anon$stmt$;
|
|
789
|
-
EXCEPTION WHEN insufficient_privilege OR undefined_object THEN
|
|
790
|
-
RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON TABLES TO anon$stmt$;
|
|
791
|
-
END $dp$;
|
|
792
|
-
|
|
793
|
-
DO $dp$ BEGIN
|
|
794
|
-
EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON TABLES TO authenticated$stmt$;
|
|
795
|
-
EXCEPTION WHEN insufficient_privilege OR undefined_object THEN
|
|
796
|
-
RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON TABLES TO authenticated$stmt$;
|
|
797
|
-
END $dp$;
|
|
798
|
-
|
|
799
|
-
DO $dp$ BEGIN
|
|
800
|
-
EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON TABLES TO service_role$stmt$;
|
|
801
|
-
EXCEPTION WHEN insufficient_privilege OR undefined_object THEN
|
|
802
|
-
RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON TABLES TO service_role$stmt$;
|
|
803
|
-
END $dp$;
|
|
804
|
-
|
|
805
|
-
DO $dp$ BEGIN
|
|
806
|
-
EXECUTE $stmt$--
|
|
807
|
-
-- PostgreSQL database dump complete
|
|
808
|
-
--$stmt$;
|
|
809
|
-
EXCEPTION WHEN insufficient_privilege OR undefined_object THEN
|
|
810
|
-
RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$--
|
|
811
|
-
-- PostgreSQL database dump complete
|
|
812
|
-
--$stmt$;
|
|
813
|
-
END $dp$;
|
|
814
|
-
`;
|
|
815
|
-
var MIGRATION_001_THE_IMPORT_RPC_NAMES_REAL_TABLES = `-- ==========================================================================
|
|
816
|
-
-- 001_the_import_rpc_names_real_tables \u2014 plugbank_import_movements
|
|
817
|
-
-- stops reaching through 141's bridge (#352).
|
|
818
|
-
--
|
|
819
|
-
-- 107 renamed the connector spine out of the \`plg_\` prefix \u2014 the prefix means
|
|
820
|
-
-- "a plugin owns this", and the connector framework's tables are the BASE's.
|
|
821
|
-
-- 141 left \`plg_connections\` and \`plg_sync_runs\` answering as views so callers
|
|
822
|
-
-- could move without a synchronised deploy. This function never moved, and it
|
|
823
|
-
-- is not alone: \`plg_connections\` had 13 callers and \`connections\` had none.
|
|
824
|
-
-- The bridge became the road.
|
|
825
|
-
--
|
|
826
|
-
-- The body is 000_baseline's, unchanged except for the three names. It is not
|
|
827
|
-
-- an edit of that file \u2014 an applied migration is immutable and the ledger
|
|
828
|
-
-- treats drift as a hard stop \u2014 it is a CREATE OR REPLACE that lands after it.
|
|
829
|
-
--
|
|
830
|
-
-- Why this plugin has to carry its own: the function is banking-br's, and the
|
|
831
|
-
-- views it stops using are the spine's. The spine runs FIRST, so a spine file
|
|
832
|
-
-- cannot drop a view that a plugin applied later still reads. Each package
|
|
833
|
-
-- moves its own callers; the aliases go once nobody is left.
|
|
834
|
-
--
|
|
835
|
-
-- Replay-safe: CREATE OR REPLACE, same signature, same grants (which 000 set
|
|
836
|
-
-- and this does not touch).
|
|
837
|
-
-- ==========================================================================
|
|
838
|
-
|
|
839
|
-
CREATE OR REPLACE FUNCTION public.plugbank_import_movements(p_payload jsonb) RETURNS jsonb
|
|
840
|
-
LANGUAGE plpgsql SECURITY DEFINER
|
|
841
|
-
SET search_path TO 'public'
|
|
842
|
-
AS $$
|
|
843
|
-
DECLARE
|
|
844
|
-
v_tenants uuid[];
|
|
845
|
-
v_tenant uuid;
|
|
846
|
-
v_connection uuid;
|
|
847
|
-
v_instance text;
|
|
848
|
-
v_account uuid;
|
|
849
|
-
v_connector text;
|
|
850
|
-
v_trigger text;
|
|
851
|
-
v_from date;
|
|
852
|
-
v_to date;
|
|
853
|
-
v_started timestamptz := now();
|
|
854
|
-
v_line jsonb;
|
|
855
|
-
v_external text;
|
|
856
|
-
v_amount numeric(14,2);
|
|
857
|
-
v_date date;
|
|
858
|
-
v_fetched int := 0;
|
|
859
|
-
v_imported int := 0;
|
|
860
|
-
v_duplicates int := 0;
|
|
861
|
-
v_inserted uuid;
|
|
862
|
-
v_run uuid;
|
|
863
|
-
BEGIN
|
|
864
|
-
v_connection := nullif(btrim(p_payload->>'integration_id'), '')::uuid;
|
|
865
|
-
IF v_connection IS NULL THEN
|
|
866
|
-
RAISE EXCEPTION 'plugbank_import_movements: integration_id is required'
|
|
867
|
-
USING ERRCODE = 'invalid_parameter_value';
|
|
868
|
-
END IF;
|
|
869
|
-
|
|
870
|
-
SELECT c.tenant_id, c.instance_key, c.connector_id
|
|
871
|
-
INTO v_tenant, v_instance, v_connector
|
|
872
|
-
FROM public.connections c
|
|
873
|
-
WHERE c.id = v_connection AND c.active;
|
|
874
|
-
|
|
875
|
-
IF v_tenant IS NULL THEN
|
|
876
|
-
RAISE EXCEPTION 'plugbank_import_movements: no active connection %', v_connection
|
|
877
|
-
USING ERRCODE = 'no_data_found';
|
|
878
|
-
END IF;
|
|
879
|
-
|
|
880
|
-
-- The account this connection is for. \`instance_key\` is text because it also
|
|
881
|
-
-- carries things that are not uuids for other connectors (a Bling company, a
|
|
882
|
-
-- gateway instance); here it is the bank account, and '' is the account-less
|
|
883
|
-
-- default that \`bank_account_id IS NULL\` used to mean.
|
|
884
|
-
v_account := nullif(btrim(v_instance), '')::uuid;
|
|
885
|
-
|
|
886
|
-
-- SECURITY DEFINER bypasses RLS, so the membership check the policy would
|
|
887
|
-
-- have made has to happen here.
|
|
888
|
-
SELECT array_agg(t) INTO v_tenants FROM public.user_tenant_ids() AS t;
|
|
889
|
-
IF auth.uid() IS NOT NULL AND NOT (v_tenant = ANY (COALESCE(v_tenants, '{}'::uuid[]))) THEN
|
|
890
|
-
RAISE EXCEPTION 'plugbank_import_movements: caller is not a member of tenant %', v_tenant
|
|
891
|
-
USING ERRCODE = 'insufficient_privilege';
|
|
892
|
-
END IF;
|
|
893
|
-
|
|
894
|
-
-- Refusing beats ignoring: a caller that believes it is importing into
|
|
895
|
-
-- another tenant must be told, not silently redirected into this one.
|
|
896
|
-
IF nullif(btrim(p_payload->>'tenant_id'), '') IS NOT NULL
|
|
897
|
-
AND (p_payload->>'tenant_id')::uuid IS DISTINCT FROM v_tenant THEN
|
|
898
|
-
RAISE EXCEPTION 'plugbank_import_movements: payload names another tenant; the tenant comes from the connection'
|
|
899
|
-
USING ERRCODE = 'insufficient_privilege';
|
|
900
|
-
END IF;
|
|
901
|
-
|
|
902
|
-
-- Same rule as the tenant, one level down: the account is the connection's,
|
|
903
|
-
-- and a payload naming a different one is a caller importing into the wrong
|
|
904
|
-
-- ledger account. 002 let the payload override it silently \u2014 with one
|
|
905
|
-
-- connection per account that override is how two accounts get mixed.
|
|
906
|
-
IF nullif(btrim(p_payload->>'bank_account_id'), '') IS NOT NULL
|
|
907
|
-
AND (p_payload->>'bank_account_id')::uuid IS DISTINCT FROM v_account THEN
|
|
908
|
-
RAISE EXCEPTION 'plugbank_import_movements: payload names another bank account; the account comes from the connection'
|
|
909
|
-
USING ERRCODE = 'insufficient_privilege';
|
|
910
|
-
END IF;
|
|
911
|
-
|
|
912
|
-
v_from := nullif(btrim(p_payload->>'period_from'), '')::date;
|
|
913
|
-
v_to := nullif(btrim(p_payload->>'period_to'), '')::date;
|
|
914
|
-
v_trigger := lower(coalesce(nullif(btrim(p_payload->>'trigger'), ''), 'manual'));
|
|
915
|
-
IF v_trigger NOT IN ('on-write', 'scheduled', 'manual', 'webhook') THEN
|
|
916
|
-
RAISE EXCEPTION 'plugbank_import_movements: unknown trigger %', v_trigger
|
|
917
|
-
USING ERRCODE = 'invalid_parameter_value';
|
|
918
|
-
END IF;
|
|
919
|
-
|
|
920
|
-
FOR v_line IN SELECT * FROM jsonb_array_elements(coalesce(p_payload->'lines', '[]'::jsonb))
|
|
921
|
-
LOOP
|
|
922
|
-
v_fetched := v_fetched + 1;
|
|
923
|
-
v_external := nullif(btrim(v_line->>'external_id'), '');
|
|
924
|
-
v_date := nullif(btrim(v_line->>'date'), '')::date;
|
|
925
|
-
v_amount := abs(coalesce((v_line->>'amount')::numeric, 0));
|
|
926
|
-
|
|
927
|
-
-- A line with no provider id cannot be recognised on re-delivery, so it
|
|
928
|
-
-- would import again on every run. Skipping it is the lesser wrong.
|
|
929
|
-
IF v_external IS NULL OR v_date IS NULL OR v_amount = 0 THEN
|
|
930
|
-
CONTINUE;
|
|
931
|
-
END IF;
|
|
932
|
-
|
|
933
|
-
INSERT INTO public.plg_financial_movements (
|
|
934
|
-
tenant_id, direction, movement_kind, amount, paid_amount, status,
|
|
935
|
-
due_date, payment_date, bank_account_id, notes, external_id, external_source)
|
|
936
|
-
VALUES (
|
|
937
|
-
v_tenant,
|
|
938
|
-
CASE WHEN upper(coalesce(v_line->>'type', 'C')) LIKE 'C%' THEN 'credit' ELSE 'debit' END,
|
|
939
|
-
'payment', v_amount, v_amount, 'paid',
|
|
940
|
-
v_date, v_date, v_account,
|
|
941
|
-
nullif(btrim(v_line->>'description'), ''),
|
|
942
|
-
v_external, coalesce(v_connector, 'plugbank'))
|
|
943
|
-
ON CONFLICT (tenant_id, external_source, external_id)
|
|
944
|
-
WHERE external_id IS NOT NULL AND external_source IS NOT NULL
|
|
945
|
-
DO NOTHING
|
|
946
|
-
RETURNING id INTO v_inserted;
|
|
947
|
-
|
|
948
|
-
IF v_inserted IS NULL THEN
|
|
949
|
-
v_duplicates := v_duplicates + 1;
|
|
950
|
-
ELSE
|
|
951
|
-
v_imported := v_imported + 1;
|
|
952
|
-
END IF;
|
|
953
|
-
v_inserted := NULL;
|
|
954
|
-
END LOOP;
|
|
955
|
-
|
|
956
|
-
UPDATE public.connections
|
|
957
|
-
SET last_sync_at = now(), status = 'connected', status_detail = NULL, updated_at = now()
|
|
958
|
-
WHERE id = v_connection;
|
|
959
|
-
|
|
960
|
-
-- \`stats\` is where the three facts a manual statement import has and a generic
|
|
961
|
-
-- run does not go: the operator picked a period, a re-import of that period is
|
|
962
|
-
-- mostly duplicates by design, and someone pressed the button. The panel reads
|
|
963
|
-
-- all three back from here.
|
|
964
|
-
INSERT INTO public.sync_runs (
|
|
965
|
-
tenant_id, connection_id, connector_id, direction, trigger_kind, status,
|
|
966
|
-
stream, fetched, written, stats, started_at, finished_at)
|
|
967
|
-
VALUES (
|
|
968
|
-
v_tenant, v_connection, coalesce(v_connector, 'plugbank'), 'inbound', v_trigger, 'success',
|
|
969
|
-
'statement', v_fetched, v_imported,
|
|
970
|
-
jsonb_strip_nulls(jsonb_build_object(
|
|
971
|
-
'duplicates', v_duplicates,
|
|
972
|
-
'period_from', v_from,
|
|
973
|
-
'period_to', v_to,
|
|
974
|
-
'triggered_by_user_id', auth.uid())),
|
|
975
|
-
v_started, now())
|
|
976
|
-
RETURNING id INTO v_run;
|
|
977
|
-
|
|
978
|
-
RETURN jsonb_build_object(
|
|
979
|
-
'ok', true,
|
|
980
|
-
'sync_run_id', v_run,
|
|
981
|
-
'fetched', v_fetched,
|
|
982
|
-
'imported', v_imported,
|
|
983
|
-
'duplicates', v_duplicates);
|
|
984
|
-
END;
|
|
985
|
-
$$;
|
|
986
|
-
COMMENT ON FUNCTION public.plugbank_import_movements(p_payload jsonb) IS
|
|
987
|
-
'Import bank-statement lines into the financial ledger and file the run in public.sync_runs. The tenant AND the bank account come from the public.connections row named by integration_id, never from the payload.';
|
|
988
|
-
`;
|
|
989
|
-
var MIGRATIONS = [
|
|
990
|
-
{ id: "000_baseline", sql: MIGRATION_000_BASELINE },
|
|
991
|
-
{ id: "001_the_import_rpc_names_real_tables", sql: MIGRATION_001_THE_IMPORT_RPC_NAMES_REAL_TABLES }
|
|
992
|
-
];
|
|
993
|
-
|
|
994
|
-
// src/index.ts
|
|
995
388
|
function createOpenBankingPlugin() {
|
|
996
389
|
return {
|
|
997
390
|
id: "openbanking",
|