2016-05-22 01:43:49 +02:00
|
|
|
CREATE TABLE Account (
|
|
|
|
ID PRIMARY KEY NOT NULL,
|
|
|
|
type NOT NULL,
|
2016-06-05 12:41:48 +02:00
|
|
|
altId NOT NULL, -- e.g. when type "member", no. in external member table
|
|
|
|
IBAN -- target account for returned payments (set '' to enable
|
|
|
|
-- outgoing bank transfers to commercial partners from that account).
|
2016-05-22 01:43:49 +02:00
|
|
|
);
|
|
|
|
CREATE TABLE Debit (
|
2016-05-22 14:35:43 +02:00
|
|
|
billId PRIMARY KEY NOT NULL,
|
|
|
|
debtor NOT NULL, -- Account charged
|
2016-05-22 01:43:49 +02:00
|
|
|
targetCredit INTEGER, -- record id in Credit table to pay into.
|
|
|
|
-- just understand it as virtual payment
|
|
|
|
-- NULL when debit is a bank transfer from the club account
|
|
|
|
date DATE NOT NULL,
|
|
|
|
purpose NOT NULL, -- description of receipt
|
|
|
|
value INTEGER NOT NULL, -- Euro-Cent
|
|
|
|
paid INTEGER DEFAULT 0, -- Euro-Cent, set and changed automatically (Cache)
|
2016-05-22 14:35:43 +02:00
|
|
|
FOREIGN KEY (debtor) REFERENCES Account(ID),
|
2016-05-22 01:43:49 +02:00
|
|
|
FOREIGN KEY (targetCredit) REFERENCES Credit(Id)
|
|
|
|
);
|
|
|
|
CREATE TABLE Credit (
|
|
|
|
Id INTEGER PRIMARY KEY NOT NULL,
|
|
|
|
account NOT NULL, -- Account des Begünstigten
|
|
|
|
date DATE NOT NULL,
|
|
|
|
purpose NOT NULL, -- as originally indicated in statement of bank account
|
|
|
|
value INTEGER NOT NULL, -- Euro-Cent. Caution, two distinct cases need to be considered:
|
|
|
|
-- Either deposit by bank transfer (>0) or target of internal payments (=0)
|
|
|
|
spent INTEGER DEFAULT 0, -- Euro-Cent, set and changed automatically (Cache)
|
|
|
|
-- for later traceability, necessary when revoking transfers
|
|
|
|
FOREIGN KEY (account) REFERENCES Account(ID)
|
|
|
|
);
|
|
|
|
|
2016-06-05 12:41:48 +02:00
|
|
|
-- Which credit pays/paid down which debt is recorded traceably so as to clarify any case of reminder,
|
|
|
|
-- without ambuiguity about, which debt is actually due yet, or to clarify which transfer is used for
|
|
|
|
-- which debts. The user specifies which credit is intended for which debt, in accordance with the
|
|
|
|
-- purpose if any is indicated in a received bank transfer. Following triggers verify this relation
|
|
|
|
-- and mark a debit as paid and/or a credit as paid.
|
|
|
|
-- Debits to which applies value > paid are meant to be suggested for assignment to newly inserted
|
|
|
|
-- credit records without indicated purpose. Likewise, credits without indicated purpose to which
|
|
|
|
-- applies value > spent are candidates for payment of newly inserted debts.
|
2016-05-22 01:43:49 +02:00
|
|
|
CREATE TABLE Transfer (
|
|
|
|
timestamp DATE DEFAULT CURRENT_TIMESTAMP,
|
2016-05-22 14:35:43 +02:00
|
|
|
billId INTEGER NOT NULL,
|
2016-05-22 01:43:49 +02:00
|
|
|
fromCredit INTEGER NOT NULL,
|
2016-06-05 12:41:48 +02:00
|
|
|
amount INTEGER, -- for later traceability, necessary when revoking transfers
|
2016-05-22 14:35:43 +02:00
|
|
|
FOREIGN KEY (billId) REFERENCES Debit(billId),
|
2016-05-22 01:43:49 +02:00
|
|
|
FOREIGN KEY (fromCredit) REFERENCES Credit(Id),
|
2016-05-22 14:35:43 +02:00
|
|
|
UNIQUE (billId, fromCredit)
|
2016-05-22 01:43:49 +02:00
|
|
|
);
|
|
|
|
|
|
|
|
CREATE TABLE IF NOT EXISTS _temp (d, c, m);
|
|
|
|
CREATE TRIGGER balanceTransfer
|
|
|
|
AFTER INSERT ON Transfer
|
|
|
|
BEGIN
|
|
|
|
|
2016-06-05 12:41:48 +02:00
|
|
|
SELECT RAISE(FAIL, "It is not the debtor who is set to pay")
|
2016-05-22 14:35:43 +02:00
|
|
|
WHERE (SELECT debtor FROM Debit WHERE billId=NEW.billId)
|
2016-05-22 01:43:49 +02:00
|
|
|
!= (SELECT account FROM Credit WHERE Id=NEW.fromCredit)
|
|
|
|
;
|
|
|
|
|
|
|
|
INSERT INTO _temp
|
|
|
|
SELECT remainingDebt, remainingCredit, min(remainingDebt,remainingCredit)
|
|
|
|
FROM (SELECT
|
2016-05-22 14:35:43 +02:00
|
|
|
(SELECT value - paid FROM Debit WHERE billId=NEW.billId) AS remainingDebt,
|
2016-05-22 01:43:49 +02:00
|
|
|
(SELECT value - spent FROM Credit WHERE Id=NEW.fromCredit) AS remainingCredit
|
2016-06-05 12:41:48 +02:00
|
|
|
)
|
2016-05-22 01:43:49 +02:00
|
|
|
;
|
|
|
|
|
|
|
|
UPDATE Debit
|
|
|
|
SET paid = paid + CASE
|
|
|
|
WHEN (SELECT d FROM _temp) <= 0
|
2016-06-05 12:41:48 +02:00
|
|
|
THEN RAISE(FAIL, "Debt is already paid")
|
2016-05-22 01:43:49 +02:00
|
|
|
ELSE
|
|
|
|
(SELECT m FROM _temp)
|
|
|
|
END
|
2016-05-22 14:35:43 +02:00
|
|
|
WHERE billId=NEW.billId;
|
2016-05-22 01:43:49 +02:00
|
|
|
|
|
|
|
UPDATE Credit
|
|
|
|
SET value = value + (SELECT m FROM _temp)
|
|
|
|
WHERE Id = (
|
|
|
|
SELECT targetCredit
|
|
|
|
FROM Debit
|
2016-05-22 14:35:43 +02:00
|
|
|
WHERE billId=NEW.billId
|
2016-05-22 01:43:49 +02:00
|
|
|
);
|
|
|
|
|
|
|
|
UPDATE Credit
|
|
|
|
SET spent = spent + CASE
|
|
|
|
WHEN (SELECT c FROM _temp) <= 0
|
|
|
|
THEN RAISE(FAIL, "Credit is already spent")
|
|
|
|
ELSE
|
|
|
|
(SELECT m FROM _temp)
|
|
|
|
END
|
|
|
|
WHERE Id=NEW.fromCredit;
|
|
|
|
|
|
|
|
UPDATE Transfer
|
|
|
|
SET amount = (SELECT m FROM _temp)
|
2016-05-22 14:35:43 +02:00
|
|
|
WHERE billId=NEW.billId AND fromCredit=NEW.fromCredit
|
2016-05-22 01:43:49 +02:00
|
|
|
;
|
|
|
|
|
|
|
|
DELETE FROM _temp;
|
|
|
|
|
|
|
|
END;
|
|
|
|
|
|
|
|
CREATE TRIGGER revokeTransfer
|
|
|
|
BEFORE DELETE ON Transfer
|
|
|
|
BEGIN
|
|
|
|
|
|
|
|
INSERT INTO _temp VALUES (null,null,OLD.amount);
|
|
|
|
|
|
|
|
UPDATE Debit
|
|
|
|
SET paid = paid - OLD.amount
|
2016-05-22 14:35:43 +02:00
|
|
|
WHERE billId=OLD.billId
|
2016-05-22 01:43:49 +02:00
|
|
|
;
|
|
|
|
|
|
|
|
UPDATE Credit
|
|
|
|
SET value = value - OLD.amount
|
|
|
|
WHERE Id = (
|
|
|
|
SELECT targetCredit
|
|
|
|
FROM Debit
|
2016-05-22 14:35:43 +02:00
|
|
|
WHERE billId=OLD.billId
|
2016-05-22 01:43:49 +02:00
|
|
|
);
|
|
|
|
|
|
|
|
UPDATE Credit
|
|
|
|
SET spent = spent - OLD.amount
|
|
|
|
WHERE Id = OLD.fromCredit;
|
|
|
|
|
|
|
|
DELETE FROM _temp;
|
|
|
|
|
|
|
|
END;
|
|
|
|
|
|
|
|
CREATE TRIGGER enforceImmutableTransfer
|
|
|
|
BEFORE UPDATE ON Transfer
|
|
|
|
WHEN OLD.amount IS NOT NULL
|
|
|
|
BEGIN
|
|
|
|
SELECT RAISE(FAIL, "Transfer cannot be updated, but needs to be revoked and re-inserted to ensure the triggers run");
|
|
|
|
END;
|
|
|
|
|
|
|
|
CREATE TRIGGER enforceiZeroPaidAtStart
|
|
|
|
BEFORE INSERT ON Debit
|
|
|
|
BEGIN
|
|
|
|
SELECT RAISE(FAIL, "debt must be initially unpaid")
|
|
|
|
WHERE NEW.paid <> 0;
|
|
|
|
END;
|
|
|
|
|
|
|
|
-- Prevent modification with paid value outside triggers which must adjust it exclusively
|
|
|
|
-- when new transfer records are inserted
|
|
|
|
CREATE TRIGGER enforceDebtImmutableOutsideTrigger
|
|
|
|
BEFORE UPDATE OF paid ON Debit
|
2016-05-22 14:35:43 +02:00
|
|
|
WHEN NOT EXISTS (SELECT * FROM Transfer t WHERE NEW.billId=t.billId AND amount IS NULL)
|
2016-05-22 01:43:49 +02:00
|
|
|
BEGIN
|
|
|
|
SELECT RAISE(FAIL, "paid is set and adjusted automatically according to added Transfer records")
|
|
|
|
WHERE (NEW.paid + IFNULL((SELECT m FROM _temp WHERE c IS NULL AND d IS NULL),0) ) <> OLD.paid;
|
|
|
|
END;
|
|
|
|
|
|
|
|
CREATE TRIGGER enforceFixedDebits
|
|
|
|
BEFORE UPDATE OF value ON Debit
|
2016-05-22 14:35:43 +02:00
|
|
|
WHEN EXISTS (SELECT * FROM Transfer WHERE billId=NEW.billId)
|
2016-05-22 01:43:49 +02:00
|
|
|
BEGIN
|
|
|
|
SELECT RAISE(FAIL, "Debt is involved in transfers to revoke at first");
|
|
|
|
END;
|
|
|
|
|
|
|
|
CREATE TRIGGER enforceZeroSpentAtStart
|
|
|
|
BEFORE INSERT ON Credit
|
|
|
|
BEGIN
|
|
|
|
SELECT RAISE(FAIL, "credit must be initially unused")
|
|
|
|
WHERE NEW.spent != 0;
|
|
|
|
END;
|
|
|
|
|
2016-06-05 12:41:48 +02:00
|
|
|
-- Prevent modification of spent value outside triggers which must adjust it exclusively
|
2016-05-22 01:43:49 +02:00
|
|
|
-- when new transfer records are inserted
|
|
|
|
CREATE TRIGGER enforceSpentImmutableOutsideTrigger
|
|
|
|
BEFORE UPDATE OF spent ON Credit
|
|
|
|
WHEN NOT EXISTS (SELECT * FROM Transfer t WHERE NEW.Id=t.fromCredit AND amount IS NULL)
|
|
|
|
BEGIN
|
|
|
|
SELECT RAISE(FAIL, "spent is set and adjusted automatically according to added Transfer records")
|
|
|
|
WHERE (NEW.spent + IFNULL((SELECT m FROM _temp WHERE c IS NULL AND d IS NULL),0) ) <> OLD.spent;
|
|
|
|
END;
|
|
|
|
|
|
|
|
CREATE TRIGGER enforceFixedCredit
|
|
|
|
BEFORE UPDATE OF value ON Credit
|
|
|
|
BEGIN
|
|
|
|
SELECT RAISE(FAIL, "Credit involved in transactions to revoke at first")
|
|
|
|
WHERE EXISTS (SELECT * FROM Transfer WHERE fromCredit=NEW.Id);
|
|
|
|
END;
|
|
|
|
|
|
|
|
CREATE TRIGGER checkIBANatTransfer
|
|
|
|
BEFORE INSERT ON Debit
|
|
|
|
WHEN NEW.targetCredit IS NULL
|
|
|
|
BEGIN
|
|
|
|
SELECT RAISE(FAIL, "IBAN used does not match IBAN currently stored in account record")
|
2016-06-05 12:41:48 +02:00
|
|
|
FROM (
|
|
|
|
SELECT instr(NEW.purpose, IBAN) AS fnd
|
|
|
|
FROM Account
|
|
|
|
WHERE ID=NEW.debtor
|
|
|
|
) AS matchingIBAN
|
2016-05-22 01:43:49 +02:00
|
|
|
WHERE fnd IS NULL OR fnd = 0;
|
|
|
|
END;
|
|
|
|
|
|
|
|
CREATE VIEW CurrentDebts AS
|
2016-05-22 14:35:43 +02:00
|
|
|
SELECT debtor,
|
|
|
|
targetCredit,
|
|
|
|
purpose,
|
|
|
|
date,
|
2016-05-22 01:43:49 +02:00
|
|
|
value - paid AS difference
|
|
|
|
FROM Debit
|
|
|
|
WHERE value != paid
|
|
|
|
;
|
|
|
|
|
|
|
|
CREATE VIEW AvailableCredits AS
|
|
|
|
SELECT account, purpose, date,
|
|
|
|
value - spent AS difference
|
|
|
|
FROM Credit
|
|
|
|
WHERE value != spent
|
|
|
|
;
|
|
|
|
|
|
|
|
CREATE VIEW Balance AS
|
2016-05-22 14:35:43 +02:00
|
|
|
SELECT Account.ID AS ID,
|
|
|
|
IFNULL(ac.allCredits,0) AS credit,
|
|
|
|
IFNULL(pr.allPromises, 0) AS promised,
|
|
|
|
IFNULL(cd.allDebts, 0) AS debt
|
2016-05-22 01:43:49 +02:00
|
|
|
FROM Account
|
2016-05-22 14:35:43 +02:00
|
|
|
LEFT OUTER JOIN (
|
|
|
|
SELECT debtor, sum(difference) AS allDebts
|
|
|
|
FROM CurrentDebts
|
|
|
|
GROUP BY debtor
|
|
|
|
) AS cd ON Account.ID=cd.debtor
|
|
|
|
LEFT OUTER JOIN (
|
|
|
|
SELECT account, sum(difference) AS allCredits
|
|
|
|
FROM AvailableCredits
|
|
|
|
GROUP BY account
|
|
|
|
) AS ac ON Account.ID=ac.account
|
|
|
|
LEFT OUTER JOIN (
|
|
|
|
SELECT a.ID AS ID, sum(difference) AS allPromises
|
|
|
|
FROM CurrentDebts cd
|
|
|
|
JOIN Credit c ON cd.targetCredit = c.Id
|
|
|
|
JOIN Account a ON a.ID = c.account
|
|
|
|
GROUP BY a.ID
|
|
|
|
) AS pr ON Account.ID=pr.ID
|
2016-05-22 01:43:49 +02:00
|
|
|
;
|
|
|
|
|
2016-06-05 16:18:49 +02:00
|
|
|
CREATE VIEW ReconstructedBankStatement AS
|
|
|
|
SELECT c.date AS date,
|
|
|
|
c.purpose AS purpose,
|
|
|
|
account,
|
|
|
|
c.value AS credit,
|
|
|
|
NULL AS debit
|
|
|
|
FROM Credit AS c
|
|
|
|
LEFT OUTER JOIN Debit AS d ON c.ID=d.targetCredit
|
|
|
|
GROUP BY c.ID
|
|
|
|
HAVING count(d.billId) == 0 -- exclude internal transfers
|
|
|
|
UNION
|
|
|
|
SELECT date,
|
|
|
|
purpose,
|
|
|
|
debtor AS account,
|
|
|
|
NULL AS credit,
|
|
|
|
value AS debit
|
|
|
|
FROM Debit
|
|
|
|
WHERE targetCredit IS NULL -- exclude internal transfers
|
|
|
|
ORDER BY date ASC
|
|
|
|
;
|