001-finance-init.xml 3.29 KB
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
                                       https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.27.xsd">

    <!--
        pbc-finance initial schema (minimal AR/AP, v0.16).

        Owns: finance__journal_entry.

        This is the framework's first CONSUMER PBC: the table only ever
        receives writes from event subscribers (see
        org.vibeerp.pbc.finance.event.OrderEventSubscribers). The
        unique constraint on `code` is the durability anchor for
        idempotent event delivery — a duplicate event with the same
        eventId is rejected by Postgres rather than producing a second
        row. JournalEntryService.recordSalesConfirmed/recordPurchaseConfirmed
        also do an existsByCode pre-check so the duplicate path is a
        clean no-op rather than a constraint-violation exception.

        NEITHER `partner_code` NOR `order_code` is a foreign key. They
        are cross-PBC references; a database FK across PBCs would
        couple the schemas of pbc-finance, pbc-partners, pbc-orders-sales
        and pbc-orders-purchase at the storage level, defeating the
        bounded-context rule (CLAUDE.md guardrail #9).
    -->

    <changeSet id="finance-init-001" author="vibe_erp">
        <comment>Create finance__journal_entry table</comment>
        <sql>
            CREATE TABLE finance__journal_entry (
                id              uuid PRIMARY KEY,
                code            varchar(64)   NOT NULL,
                type            varchar(8)    NOT NULL,
                partner_code    varchar(64)   NOT NULL,
                order_code      varchar(64)   NOT NULL,
                amount          numeric(18,4) NOT NULL,
                currency_code   varchar(3)    NOT NULL,
                posted_at       timestamptz   NOT NULL,
                ext             jsonb         NOT NULL DEFAULT '{}'::jsonb,
                created_at      timestamptz   NOT NULL,
                created_by      varchar(128)  NOT NULL,
                updated_at      timestamptz   NOT NULL,
                updated_by      varchar(128)  NOT NULL,
                version         bigint        NOT NULL DEFAULT 0,
                CONSTRAINT finance__journal_entry_type_check CHECK (type IN ('AR', 'AP'))
            );
            CREATE UNIQUE INDEX finance__journal_entry_code_uk
                ON finance__journal_entry (code);
            CREATE INDEX finance__journal_entry_partner_idx
                ON finance__journal_entry (partner_code);
            CREATE INDEX finance__journal_entry_order_idx
                ON finance__journal_entry (order_code);
            CREATE INDEX finance__journal_entry_type_idx
                ON finance__journal_entry (type);
            CREATE INDEX finance__journal_entry_posted_idx
                ON finance__journal_entry (posted_at);
            CREATE INDEX finance__journal_entry_ext_gin
                ON finance__journal_entry USING GIN (ext jsonb_path_ops);
        </sql>
        <rollback>
            DROP TABLE finance__journal_entry;
        </rollback>
    </changeSet>

</databaseChangeLog>