You could use a trigger like this to update your column:
Table definition with unique constraint on customer_id, receipt_number:
CREATE TABLE receipts (id serial primary key, customer_id bigint, receipt_number bigint default 1);
CREATE UNIQUE INDEX receipts_idx ON receipts(customer_id, receipt_number);
Function to check for max receipt_number for the client, or 1 if no previous receipts
CREATE OR REPLACE FUNCTION get_receipt_number() RETURNS TRIGGER AS $receipts$
BEGIN
-- This lock will block other transactions from doing anything to table until
-- committed. This may not offer the best performance, but is threadsafe.
LOCK TABLE receipts IN ACCESS EXCLUSIVE MODE;
NEW.receipt_number = (SELECT CASE WHEN max(receipt_number) IS NULL THEN 1 ELSE max(receipt_number) + 1 END FROM receipts WHERE customer_id = new.customer_id);
RETURN NEW;
END;
$receipts$ LANGUAGE 'plpgsql';
Trigger to fire the function on each row insert:
CREATE TRIGGER rcpt_trigger
BEFORE INSERT ON receipts
FOR EACH ROW
EXECUTE PROCEDURE get_receipt_number();
Then, executing the following:
db=> insert into receipts (customer_id) VALUES (1);
INSERT 0 1
db=> insert into receipts (customer_id) VALUES (1);
INSERT 0 1
db=> insert into receipts (customer_id) VALUES (2);
INSERT 0 1
db=> insert into receipts (customer_id) VALUES (2);
INSERT 0 1
db=> insert into receipts (customer_id) VALUES (2);
should yield:
id | customer_id | receipt_number
----+-------------+----------------
14 | 1 | 1
15 | 1 | 2
16 | 2 | 1
17 | 2 | 2
18 | 2 | 3