Transaksi, referensi, dan bagaimana menegakkan pembukuan entri ganda? (PG)

8

Pembukuan entri ganda adalah

seperangkat aturan untuk mencatat informasi keuangan dalam sistem akuntansi keuangan di mana setiap transaksi atau peristiwa mengubah setidaknya dua akun buku besar nominal yang berbeda.

Akun dapat "didebit" atau "dikreditkan", dan jumlah semua kredit harus sama dengan jumlah semua debit.

Bagaimana Anda menerapkan ini dalam database Postgres? Menentukan DDL berikut:

CREATE TABLE accounts(
    account_id serial NOT NULL PRIMARY KEY,
    account_name varchar(64) NOT NULL
);


CREATE TABLE transactions(
    transaction_id serial NOT NULL PRIMARY KEY,
    transaction_date date NOT NULL
);


CREATE TABLE transactions_details(
    id serial8 NOT NULL PRIMARY KEY,
    transaction_id integer NOT NULL 
        REFERENCES transactions (transaction_id)
        ON UPDATE CASCADE
        ON DELETE CASCADE
        DEFERRABLE INITIALLY DEFERRED,
    account_id integer NOT NULL
        REFERENCES accounts (account_id)
        ON UPDATE CASCADE
        ON DELETE RESTRICT
        NOT DEFERRABLE INITIALLY IMMEDIATE,
    amount decimal(19,6) NOT NULL,
    flag varchar(1) NOT NULL CHECK (flag IN ('C','D'))
);

Catatan: tabel transaction_details tidak menentukan akun debit / kredit eksplisit, karena sistem harus dapat mendebit / kredit lebih dari satu akun dalam satu transaksi.

DDL ini menciptakan persyaratan berikut: Setelah transaksi database dilakukan pada tabel transaction_details, itu harus mendebit dan mengkredit jumlah yang sama untuk masing-masing transaction_id, misalnya :

INSERT INTO accounts VALUES (100, 'Accounts receivable');
INSERT INTO accounts VALUES (200, 'Revenue');

INSERT INTO transactions VALUES (1, CURRENT_DATE);

-- The following must succeed
BEGIN;
    INSERT INTO transactions_details VALUES (DEFAULT, 1, 100, '1000'::decimal, 'D');
    INSERT INTO transactions_details VALUES (DEFAULT, 1, 200, '1000'::decimal, 'C');
COMMIT;


-- But this must raise some error
BEGIN;
    INSERT INTO transactions_details VALUES (DEFAULT, 1, 100, '1000'::decimal, 'D');
    INSERT INTO transactions_details VALUES (DEFAULT, 1, 200, '500'::decimal, 'C');
COMMIT;

Apakah mungkin untuk mengimplementasikan ini dalam database PostgreSQL? Tanpa menentukan tabel tambahan untuk menyimpan status pemicu.

Cochise Ruhulessin
sumber

Jawaban:

5

Pertama, ini persis pertanyaan yang ada dalam pikiran saya ketika saya bertanya kendala Pemodelan pada agregat himpunan bagian? yang tentunya tempat untuk memulai. Pertanyaan itu lebih umum daripada ini dan jadi jawaban saya di sini akan memiliki sedikit lebih banyak informasi mengenai pendekatan praktis.

Anda mungkin tidak ingin melakukan ini secara deklaratif di PostgreSQL. Satu-satunya solusi deklaratif yang mungkin memecahkan 1NF atau sangat rumit dan karenanya ini berarti melakukannya secara imperatif.

Dalam LedgerSMB kami berharap untuk melakukan penegakan ini dalam dua tahap (keduanya ketat).

  1. Semua entri jurnal akan masuk melalui prosedur tersimpan. Prosedur tersimpan ini akan menerima daftar item baris sebagai array dan memeriksa bahwa jumlahnya sama dengan 0. Model kami di db adalah bahwa kami memiliki kolom jumlah tunggal dengan angka negatif yang didebit dan angka positif menjadi kredit (jika saya adalah mulai dari awal, saya akan memiliki angka positif sebagai debit dan angka negatif sebagai kredit karena ini hanya sedikit lebih alami tetapi alasan di sini tidak jelas). Debit dan kredit digabung pada penyimpanan dan dipisahkan pada pengambilan oleh lapisan presentasi. Ini membuat total berlari jauh lebih mudah.

  2. Kami akan menggunakan pemicu kendala yang ditangguhkan yang akan memeriksa komit berdasarkan bidang sistem pada tabel. Ini berarti bahwa garis yang dimasukkan dalam transaksi tertentu harus seimbang, tetapi kita dapat melakukan ini di luar garis itu sendiri.

Chris Travers
sumber
BTW, jika Anda melakukan banyak akuntansi entri ganda, Anda berharap untuk merekayasa ulang skema keuangan kami (on postgreSQL) dalam sekitar satu tahun ke depan. Saya tidak tahu apakah Anda akan tertarik untuk berkolaborasi dengan proyek open source tetapi saya pikir saya akan memperpanjang undangan.
Chris Travers
Saya terlambat ke pesta, tetapi bisakah Anda menjelaskan - "akan memeriksa komit berdasarkan bidang sistem di atas meja"? Apakah Anda menggunakan bidang sistem xmin untuk mencari tahu baris mana yang dimasukkan? Saya dihadapkan dengan situasi yang tepat ini dan ini adalah satu-satunya utas yang mendekati solusi. Namun, saya tidak tahu.
Penyair Kode
Ya. Kita dapat melihat baris yang dibuat oleh transaksi dan bersikeras bahwa jumlah jumlah di dalamnya adalah 0. Itu berarti, pada dasarnya, memeriksa kolom sistem seperti xmin.
Chris Travers
4

Pendekatan lain adalah untuk mengadopsi posisi bahwa itu adalah transfer jumlah keuangan yang terdiri dari satu catatan.

Dengan demikian Anda mungkin memiliki struktur:

create table ... (
  id                integer,
  debit_account_id  not null REFERENCES accounts (account_id),
  credit_account_id not null REFERENCES accounts (account_id),
  amount            numeric not null);

Batasan pemeriksaan dapat memastikan bahwa akun debit dan kredit berbeda, dan hanya ada satu jumlah untuk disimpan. Dengan demikian ada integritas yang terjamin, yang harus disediakan oleh model data secara alami.

Saya telah bekerja dengan sistem yang mengadopsi pendekatan ini dengan sukses. Ada sedikit efisiensi dalam query untuk catatan terhadap akun tertentu, tetapi tabel lebih kompak dan permintaan untuk akun sebagai debit saja atau karena kredit hanya sedikit lebih efisien.

David Aldridge
sumber