209 lines
8.5 KiB
PL/PgSQL
209 lines
8.5 KiB
PL/PgSQL
-- =====================================================================
|
|
-- Migration 0037 — Whish receive / payout support.
|
|
--
|
|
-- WHISH_SEND already exists. This adds WHISH_RECEIVE using the same
|
|
-- payout detail table and receive accounting as OMT/WU receive:
|
|
-- cash leaves the drawer, provider float increases because Whish owes
|
|
-- the shop settlement.
|
|
-- =====================================================================
|
|
|
|
set search_path = app, public;
|
|
|
|
insert into app.services(code, name, category) values
|
|
('WHISH_RECEIVE', 'Whish — Receive', 'money_transfer')
|
|
on conflict (code) do update
|
|
set name = excluded.name,
|
|
category = excluded.category;
|
|
|
|
-- Receive details can back OMT, WU, and Whish payout transactions.
|
|
create or replace function app.omt_recv_check()
|
|
returns trigger
|
|
language plpgsql
|
|
as $$
|
|
declare svc text;
|
|
begin
|
|
select service_code into svc from app.transactions where id = new.txn_id;
|
|
if svc not in ('OMT_RECEIVE','WU_RECEIVE','WHISH_RECEIVE') then
|
|
raise exception 'omt_receive_details only valid for receive services (got %)', svc;
|
|
end if;
|
|
return new;
|
|
end;
|
|
$$;
|
|
|
|
-- Map Whish receive to the Whish float pool.
|
|
create or replace function app._money_transfer_provider(p_service text)
|
|
returns app.float_provider
|
|
language sql
|
|
immutable
|
|
as $$
|
|
select case p_service
|
|
when 'OMT_SEND' then 'OMT_CASH'::app.float_provider
|
|
when 'OMT_RECEIVE' then 'OMT_CASH'::app.float_provider
|
|
when 'OMT_BILL' then 'OMT_CASH'::app.float_provider
|
|
when 'WU_SEND' then 'OMT_CASH'::app.float_provider
|
|
when 'WU_RECEIVE' then 'OMT_CASH'::app.float_provider
|
|
when 'WHISH_SEND' then 'WHISH'::app.float_provider
|
|
when 'WHISH_RECEIVE' then 'WHISH'::app.float_provider
|
|
when 'EDL_BILL' then 'OMT_CASH'::app.float_provider
|
|
end;
|
|
$$;
|
|
|
|
-- Re-define receive RPC to include WHISH_RECEIVE.
|
|
create or replace function app.record_omt_receive(
|
|
p_shop uuid, p_till uuid,
|
|
p_payment_method app.payment_method,
|
|
p_gross_usd numeric, p_gross_lbp numeric,
|
|
p_fee_usd numeric, p_fee_lbp numeric,
|
|
p_commission_usd numeric, p_commission_lbp numeric,
|
|
p_fx_rate numeric,
|
|
p_payout_code text,
|
|
p_beneficiary_full_name text,
|
|
p_beneficiary_id_type app.id_doc_type,
|
|
p_beneficiary_id_number text,
|
|
p_beneficiary_phone text,
|
|
p_origin_country text,
|
|
p_kyc_doc_url text,
|
|
p_customer_id uuid,
|
|
p_notes text,
|
|
p_service_code text default 'OMT_RECEIVE'
|
|
) returns uuid
|
|
language plpgsql
|
|
security definer
|
|
set search_path = app, public
|
|
as $$
|
|
declare
|
|
v_txn uuid;
|
|
v_provider_lbl text;
|
|
v_float_prov app.float_provider;
|
|
v_net_usd numeric;
|
|
v_net_lbp numeric;
|
|
begin
|
|
if p_service_code not in ('OMT_RECEIVE','WU_RECEIVE','WHISH_RECEIVE') then
|
|
raise exception 'record_omt_receive: unsupported service %', p_service_code;
|
|
end if;
|
|
|
|
v_provider_lbl := case p_service_code
|
|
when 'OMT_RECEIVE' then 'OMT'
|
|
when 'WU_RECEIVE' then 'WU'
|
|
when 'WHISH_RECEIVE' then 'WHISH'
|
|
end;
|
|
|
|
v_txn := app._insert_txn(p_shop, p_till, p_service_code, p_payment_method,
|
|
p_gross_usd, p_gross_lbp, p_fee_usd, p_fee_lbp,
|
|
p_commission_usd, p_commission_lbp, p_fx_rate,
|
|
v_provider_lbl, p_payout_code,
|
|
p_beneficiary_full_name, p_beneficiary_phone,
|
|
p_customer_id, p_notes);
|
|
|
|
insert into app.omt_receive_details(
|
|
txn_id, payout_code,
|
|
beneficiary_full_name, beneficiary_id_type, beneficiary_id_number,
|
|
beneficiary_phone, origin_country, kyc_doc_url
|
|
) values (
|
|
v_txn, p_payout_code,
|
|
p_beneficiary_full_name, p_beneficiary_id_type, p_beneficiary_id_number,
|
|
p_beneficiary_phone, p_origin_country, p_kyc_doc_url
|
|
);
|
|
|
|
v_net_usd := -coalesce(p_gross_usd,0) + coalesce(p_fee_usd,0);
|
|
v_net_lbp := -coalesce(p_gross_lbp,0) + coalesce(p_fee_lbp,0);
|
|
perform app._post_cash_for_txn(v_txn, p_payment_method, v_net_usd, v_net_lbp);
|
|
|
|
v_float_prov := app._money_transfer_provider(p_service_code);
|
|
perform app._post_float_for_txn(v_txn, v_float_prov, 'USD',
|
|
coalesce(p_gross_usd,0) + coalesce(p_commission_usd,0),
|
|
'receive: provider owes shop gross + commission');
|
|
perform app._post_float_for_txn(v_txn, v_float_prov, 'LBP',
|
|
coalesce(p_gross_lbp,0) + coalesce(p_commission_lbp,0),
|
|
'receive: provider owes shop gross + commission');
|
|
|
|
return v_txn;
|
|
end;
|
|
$$;
|
|
|
|
revoke all on function app.record_omt_receive(uuid, uuid, app.payment_method,
|
|
numeric, numeric, numeric, numeric, numeric, numeric, numeric,
|
|
text, text, app.id_doc_type, text, text, text, text, uuid, text, text) from public;
|
|
grant execute on function app.record_omt_receive(uuid, uuid, app.payment_method,
|
|
numeric, numeric, numeric, numeric, numeric, numeric, numeric,
|
|
text, text, app.id_doc_type, text, text, text, text, uuid, text, text) to authenticated;
|
|
|
|
-- Detail-required check with REFUND preservation from migration 0008.
|
|
create or replace function app.txn_require_detail()
|
|
returns trigger
|
|
language plpgsql
|
|
as $$
|
|
declare ok boolean;
|
|
begin
|
|
if new.status <> 'completed' then return null; end if;
|
|
case new.service_code
|
|
when 'OMT_SEND' then select exists(select 1 from app.omt_send_details where txn_id = new.id) into ok;
|
|
when 'OMT_RECEIVE' then select exists(select 1 from app.omt_receive_details where txn_id = new.id) into ok;
|
|
when 'WU_SEND' then select exists(select 1 from app.omt_send_details where txn_id = new.id) into ok;
|
|
when 'WU_RECEIVE' then select exists(select 1 from app.omt_receive_details where txn_id = new.id) into ok;
|
|
when 'WHISH_SEND' then select exists(select 1 from app.omt_send_details where txn_id = new.id) into ok;
|
|
when 'WHISH_RECEIVE' then select exists(select 1 from app.omt_receive_details where txn_id = new.id) into ok;
|
|
when 'OMT_BILL' then select exists(select 1 from app.bill_payment_details where txn_id = new.id) into ok;
|
|
when 'EDL_BILL' then select exists(select 1 from app.bill_payment_details where txn_id = new.id) into ok;
|
|
when 'ALFA_RECHARGE' then select exists(select 1 from app.recharge_details where txn_id = new.id) into ok;
|
|
when 'TOUCH_RECHARGE' then select exists(select 1 from app.recharge_details where txn_id = new.id) into ok;
|
|
when 'OGERO_RECHARGE' then select exists(select 1 from app.recharge_details where txn_id = new.id) into ok;
|
|
when 'INTERNET_RECHARGE'then select exists(select 1 from app.recharge_details where txn_id = new.id) into ok;
|
|
when 'SIM_SALE' then select exists(select 1 from app.goods_sale_details where txn_id = new.id) into ok;
|
|
when 'PHONE_SALE' then select exists(select 1 from app.goods_sale_details where txn_id = new.id) into ok;
|
|
when 'ACCESSORY_SALE' then select exists(select 1 from app.goods_sale_details where txn_id = new.id) into ok;
|
|
when 'GOODS_SALE' then select exists(select 1 from app.goods_sale_details where txn_id = new.id) into ok;
|
|
when 'REPAIR' then select exists(select 1 from app.repair_details where txn_id = new.id) into ok;
|
|
when 'REFUND' then select exists(select 1 from app.refunds where refund_txn_id = new.id) into ok;
|
|
else ok := true;
|
|
end case;
|
|
if not ok then
|
|
raise exception 'transaction % (service %) is missing its detail/refund row',
|
|
new.id, new.service_code;
|
|
end if;
|
|
return null;
|
|
end;
|
|
$$;
|
|
|
|
create or replace function app._money_transfer_require_movement()
|
|
returns trigger
|
|
language plpgsql
|
|
as $$
|
|
declare
|
|
ok boolean;
|
|
is_money_transfer boolean;
|
|
begin
|
|
if new.status <> 'completed' then return null; end if;
|
|
|
|
is_money_transfer := new.service_code in
|
|
('OMT_SEND','OMT_RECEIVE','OMT_BILL','EDL_BILL',
|
|
'WU_SEND','WU_RECEIVE','WHISH_SEND','WHISH_RECEIVE');
|
|
if not is_money_transfer then return null; end if;
|
|
|
|
if coalesce(new.gross_usd,0) = 0 and coalesce(new.gross_lbp,0) = 0 then
|
|
return null;
|
|
end if;
|
|
|
|
select exists (
|
|
select 1 from app.float_movements
|
|
where ref_txn_id = new.id
|
|
) into ok;
|
|
if not ok then
|
|
raise exception 'money-transfer txn % (service %) has no float_movement leg',
|
|
new.id, new.service_code;
|
|
end if;
|
|
return null;
|
|
end;
|
|
$$;
|
|
|
|
drop index if exists app.ux_txn_external_ref_active;
|
|
create unique index ux_txn_external_ref_active
|
|
on app.transactions (shop_id, external_ref_provider, external_ref)
|
|
where external_ref is not null
|
|
and external_ref_provider is not null
|
|
and status <> 'voided'
|
|
and service_code in (
|
|
'OMT_SEND','OMT_RECEIVE','WU_SEND','WU_RECEIVE',
|
|
'WHISH_SEND','WHISH_RECEIVE','OMT_BILL','EDL_BILL'
|
|
);
|