BEGIN;
SET search_path TO merchant;
CREATE OR REPLACE FUNCTION merchant_insert_deposit_to_transfer (
  IN in_deposit_serial INT8,
  IN in_amount_with_fee taler_amount_currency,
  IN in_execution_time INT8,
  IN in_exchange_sig BYTEA,
  IN in_exchange_pub BYTEA,
  IN in_wtid BYTEA,
  OUT out_wire_pending_cleared BOOL,
  OUT out_conflict BOOL,
  OUT out_no_exchange_pub BOOL)
LANGUAGE plpgsql
AS $$
DECLARE
  my_signkey_serial INT8;
DECLARE
  my_confirmed BOOL;
DECLARE
  my_decose INT8;
DECLARE
  my_order_serial INT8;
DECLARE
  my_merchant_serial INT8;
DECLARE
  my_order_id TEXT;
BEGIN
SELECT signkey_serial
  INTO my_signkey_serial
  FROM merchant_exchange_signing_keys
 WHERE exchange_pub=in_exchange_pub
   ORDER BY start_date DESC
   LIMIT 1;
IF NOT FOUND
THEN
  out_no_exchange_pub=TRUE;
  out_conflict=FALSE;
  out_wire_pending_cleared=FALSE;
  RETURN;
END IF;
out_no_exchange_pub=FALSE;
INSERT INTO merchant_deposit_to_transfer
  (deposit_serial
  ,coin_contribution_value
  ,wtid
  ,execution_time
  ,signkey_serial
  ,exchange_sig
  )
  VALUES
  (in_deposit_serial
  ,in_amount_with_fee
  ,in_wtid
  ,in_execution_time
  ,my_signkey_serial
  ,in_exchange_sig
  )
  ON CONFLICT DO NOTHING;
IF NOT FOUND
THEN
  out_conflict=TRUE;
  out_wire_pending_cleared=FALSE;
  return;
END IF;
out_conflict=FALSE;
PERFORM
  FROM merchant_transfers mt
  JOIN merchant_transfer_to_coin mtc
    USING (credit_serial)
  WHERE mt.wtid=in_wtid
    AND mt.confirmed
    AND mtc.deposit_serial=in_deposit_serial;
IF NOT FOUND
THEN
  out_wire_pending_cleared=FALSE;
  RETURN;
END IF;
RAISE NOTICE 'checking affected deposit confirmation for completion';
SELECT deposit_confirmation_serial
  INTO my_decose
  FROM merchant_deposits
 WHERE deposit_serial=in_deposit_serial;
UPDATE merchant_deposit_confirmations
  SET wire_pending=FALSE
  WHERE (deposit_confirmation_serial=my_decose)
    AND NOT EXISTS
    (SELECT 1
      FROM merchant_deposits md
      LEFT JOIN merchant_deposit_to_transfer mdtt
        USING (wtid)
      WHERE md.deposit_confirmation_serial=my_decose
        AND mdtt.credit_serial IS NULL);
IF NOT FOUND
THEN
  out_wire_pending_cleared=FALSE;
  RETURN;
END IF;
out_wire_pending_cleared=TRUE;
RAISE NOTICE 'checking affected contracts for completion';
SELECT deposit_confirmation_serial
  INTO my_order_serial
  FROM merchant_deposit_confirmations
 WHERE deposit_confirmation_serial=my_decose;
UPDATE merchant_contract_terms
  SET wired=TRUE
  WHERE (order_serial=my_order_serial)
    AND NOT EXISTS
    (SELECT 1
       FROM merchant_deposit_confirmations mdc
      WHERE mdc.wire_pending
        AND mdc.order_serial=my_order_serial);
END $$;
CREATE OR REPLACE FUNCTION merchant_do_insert_product (
  IN in_instance_id TEXT,
  IN in_product_id TEXT,
  IN in_description TEXT,
  IN in_description_i18n BYTEA,
  IN in_unit TEXT,
  IN in_image TEXT,
  IN in_taxes BYTEA,
  IN in_price taler_amount_currency,
  IN in_total_stock INT8,
  IN in_address BYTEA,
  IN in_next_restock INT8,
  IN in_minimum_age INT4,
  IN ina_categories INT8[],
  OUT out_no_instance BOOL,
  OUT out_conflict BOOL,
  OUT out_no_cat INT8)
LANGUAGE plpgsql
AS $$
DECLARE
  my_merchant_id INT8;
  my_product_serial INT8;
  i INT8;
  ini_cat INT8;
BEGIN
SELECT merchant_serial
  INTO my_merchant_id
  FROM merchant_instances
 WHERE merchant_id=in_instance_id;
IF NOT FOUND
THEN
  out_no_instance=TRUE;
  out_conflict=FALSE;
  out_no_cat=NULL;
  RETURN;
END IF;
out_no_instance=FALSE;
INSERT INTO merchant_inventory
 (merchant_serial
 ,product_id
 ,description
 ,description_i18n
 ,unit
 ,image
 ,taxes
 ,price
 ,total_stock
 ,address
 ,next_restock
 ,minimum_age
) VALUES (
  my_merchant_id
 ,in_product_id
 ,in_description
 ,in_description_i18n
 ,in_unit
 ,in_image
 ,in_taxes
 ,in_price
 ,in_total_stock
 ,in_address
 ,in_next_restock
 ,in_minimum_age)
ON CONFLICT (merchant_serial, product_id) DO NOTHING
 RETURNING product_serial
 INTO my_product_serial;
IF NOT FOUND
THEN
  SELECT product_serial
    INTO my_product_serial
    FROM merchant_inventory
  WHERE merchant_serial=my_merchant_id
    AND product_id=in_product_id
    AND description=in_description
    AND description_i18n=in_description_i18n
    AND unit=in_unit
    AND image=in_image
    AND taxes=in_taxes
    AND price=in_price
    AND total_stock=in_total_stock
    AND address=in_address
    AND next_restock=in_next_restock
    AND minimum_age=in_minimum_age;
  IF NOT FOUND
  THEN
    out_conflict=TRUE;
    out_no_cat=NULL;
    RETURN;
  END IF;
  FOR i IN 1..COALESCE(array_length(ina_categories,1),0)
  LOOP
    ini_cat=ina_categories[i];
    PERFORM
      FROM merchant_product_categories
      WHERE product_serial=my_product_serial
        AND category_serial=ini_cat;
    IF NOT FOUND
    THEN
      out_conflict=TRUE;
      out_no_cat=NULL;
      RETURN;
    END IF;
  END LOOP;
  SELECT COUNT(*)
    INTO i
    FROM merchant_product_categories
    WHERE product_serial=my_product_serial;
  IF i != array_length(ina_categories,1)
  THEN
    out_conflict=TRUE;
    out_no_cat=NULL;
    RETURN;
  END IF;
  out_conflict=FALSE;
  out_no_cat=NULL;
  RETURN;
END IF;
out_conflict=FALSE;
FOR i IN 1..COALESCE(array_length(ina_categories,1),0)
LOOP
  ini_cat=ina_categories[i];
  INSERT INTO merchant_product_categories
   (product_serial
   ,category_serial)
  VALUES
   (my_product_serial
   ,ini_cat)
  ON CONFLICT DO NOTHING;
  IF NOT FOUND
  THEN
    out_no_cat=i;
    RETURN;
  END IF;
END LOOP;
out_no_cat=NULL;
END $$;
CREATE OR REPLACE FUNCTION merchant_do_insert_transfer_details (
  IN in_instance_id TEXT,
  IN in_exchange_url TEXT,
  IN in_payto_uri TEXT,
  IN in_wtid BYTEA,
  IN in_execution_time INT8,
  IN in_exchange_pub BYTEA,
  IN in_exchange_sig BYTEA,
  IN in_total_amount taler_amount_currency,
  IN in_wire_fee taler_amount_currency,
  IN ina_coin_values taler_amount_currency[],
  IN ina_deposit_fees taler_amount_currency[],
  IN ina_coin_pubs BYTEA[],
  IN ina_contract_terms BYTEA[],
  OUT out_no_instance BOOL,
  OUT out_no_account BOOL,
  OUT out_no_exchange BOOL,
  OUT out_duplicate BOOL,
  OUT out_conflict BOOL)
LANGUAGE plpgsql
AS $$
DECLARE
  my_merchant_id INT8;
  my_signkey_serial INT8;
  my_credit_serial INT8;
  my_affected_orders RECORD;
  my_merchant_serial INT8;
  my_order_id TEXT;
  i INT8;
  curs CURSOR (arg_coin_pub BYTEA) FOR
    SELECT mcon.deposit_confirmation_serial,
           mcon.order_serial
      FROM merchant_deposits dep
      JOIN merchant_deposit_confirmations mcon
        USING (deposit_confirmation_serial)
      WHERE dep.coin_pub=arg_coin_pub;
  ini_coin_pub BYTEA;
  ini_contract_term BYTEA;
  ini_coin_value taler_amount_currency;
  ini_deposit_fee taler_amount_currency;
BEGIN
SELECT merchant_serial
  INTO my_merchant_id
  FROM merchant_instances
 WHERE merchant_id=in_instance_id;
IF NOT FOUND
THEN
  out_no_instance=TRUE;
  out_no_account=FALSE;
  out_no_exchange=FALSE;
  out_duplicate=FALSE;
  out_conflict=FALSE;
  RETURN;
END IF;
out_no_instance=FALSE;
SELECT credit_serial
  INTO my_credit_serial
  FROM merchant_transfers
 WHERE exchange_url=in_exchange_url
     AND wtid=in_wtid
     AND account_serial=
     (SELECT account_serial
        FROM merchant_accounts
       WHERE payto_uri=in_payto_uri
         AND exchange_url=in_exchange_url
         AND merchant_serial=my_merchant_id);
IF NOT FOUND
THEN
  out_no_account=TRUE;
  out_no_exchange=FALSE;
  out_duplicate=FALSE;
  out_conflict=FALSE;
  RETURN;
END IF;
out_no_account=FALSE;
SELECT signkey_serial
  INTO my_signkey_serial
  FROM merchant_exchange_signing_keys
 WHERE exchange_pub=in_exchange_pub
   ORDER BY start_date DESC
   LIMIT 1;
IF NOT FOUND
THEN
  out_no_exchange=TRUE;
  out_conflict=FALSE;
  out_duplicate=FALSE;
  RETURN;
END IF;
out_no_exchange=FALSE;
INSERT INTO merchant_transfer_signatures
  (credit_serial
  ,signkey_serial
  ,credit_amount
  ,wire_fee
  ,execution_time
  ,exchange_sig)
  VALUES
   (my_credit_serial
   ,my_signkey_serial
   ,in_total_amount
   ,in_wire_fee
   ,in_execution_time
   ,in_exchange_sig)
  ON CONFLICT DO NOTHING;
IF NOT FOUND
THEN
  PERFORM 1
    FROM merchant_transfer_signatures
    WHERE credit_serial=my_credit_serial
      AND signkey_serial=my_signkey_serial
      AND credit_amount=in_total_amount
      AND wire_fee=in_wire_fee
      AND execution_time=in_execution_time
      AND exchange_sig=in_exchange_sig;
  IF FOUND
  THEN
    out_duplicate=TRUE;
    out_conflict=FALSE;
    RETURN;
  END IF;
  out_duplicate=FALSE;
  out_conflict=TRUE;
  RETURN;
END IF;
out_duplicate=FALSE;
out_conflict=FALSE;
FOR i IN 1..array_length(ina_coin_pubs,1)
LOOP
  ini_coin_value=ina_coin_values[i];
  ini_deposit_fee=ina_deposit_fees[i];
  ini_coin_pub=ina_coin_pubs[i];
  ini_contract_term=ina_contract_terms[i];
  INSERT INTO merchant_transfer_to_coin
    (deposit_serial
    ,credit_serial
    ,offset_in_exchange_list
    ,exchange_deposit_value
    ,exchange_deposit_fee)
    SELECT
        dep.deposit_serial
       ,my_credit_serial
       ,i
       ,ini_coin_value
       ,ini_deposit_fee
      FROM merchant_deposits dep
      JOIN merchant_deposit_confirmations dcon
        USING (deposit_confirmation_serial)
      JOIN merchant_contract_terms cterm
        USING (order_serial)
      WHERE dep.coin_pub=ini_coin_pub
        AND cterm.h_contract_terms=ini_contract_term
        AND cterm.merchant_serial=my_merchant_id;
  RAISE NOTICE 'iterating over affected orders';
  OPEN curs (arg_coin_pub:=ini_coin_pub);
  LOOP
    FETCH NEXT FROM curs INTO my_affected_orders;
    EXIT WHEN NOT FOUND;
    RAISE NOTICE 'checking affected order for completion';
    UPDATE merchant_deposit_confirmations
       SET wire_pending=FALSE
     WHERE (deposit_confirmation_serial=my_affected_orders.deposit_confirmation_serial)
       AND NOT EXISTS
       (SELECT 1
          FROM merchant_deposits md
          LEFT JOIN merchant_deposit_to_transfer mdtt
            USING (deposit_serial)
          WHERE md.deposit_confirmation_serial=my_affected_orders.deposit_confirmation_serial
            AND mdtt.wtid IS NULL);
    IF FOUND
    THEN
      UPDATE merchant_contract_terms
         SET wired=TRUE
       WHERE (order_serial=my_affected_orders.order_serial)
         AND NOT EXISTS
         (SELECT 1
            FROM merchant_deposit_confirmations mdc
             WHERE mdc.wire_pending
               AND mdc.order_serial=my_affected_orders.order_serial);
      SELECT merchant_serial, order_id
        INTO my_merchant_serial, my_order_id
        FROM merchant_contract_terms
       WHERE order_serial=my_affected_orders.order_serial;
      INSERT INTO merchant_pending_webhooks
      (merchant_serial
      ,webhook_serial
      ,url
      ,http_method
      ,body)
      SELECT mw.merchant_serial
            ,mw.webhook_serial
            ,mw.url
            ,mw.http_method
            ,replace_placeholder(mw.body_template, 'order_id', my_order_id)::TEXT
      FROM merchant_webhook mw
      WHERE mw.event_type = 'order_settled'
        AND mw.merchant_serial = my_merchant_serial;
    END IF;
  END LOOP; 
  CLOSE curs;
END LOOP; 
END $$;
CREATE OR REPLACE FUNCTION merchant_do_update_product (
  IN in_instance_id TEXT,
  IN in_product_id TEXT,
  IN in_description TEXT,
  IN in_description_i18n BYTEA,
  IN in_unit TEXT,
  IN in_image TEXT,
  IN in_taxes BYTEA,
  IN in_price taler_amount_currency,
  IN in_total_stock INT8,
  IN in_total_lost INT8,
  IN in_address BYTEA,
  IN in_next_restock INT8,
  IN in_minimum_age INT4,
  IN ina_categories INT8[],
  OUT out_no_instance BOOL,
  OUT out_no_product BOOL,
  OUT out_lost_reduced BOOL,
  OUT out_sold_reduced BOOL,
  OUT out_stocked_reduced BOOL,
  OUT out_no_cat INT8)
LANGUAGE plpgsql
AS $$
DECLARE
  my_merchant_id INT8;
  my_product_serial INT8;
  i INT8;
  ini_cat INT8;
  rec RECORD;
BEGIN
out_no_instance=FALSE;
out_no_product=FALSE;
out_lost_reduced=FALSE;
out_sold_reduced=FALSE; 
out_stocked_reduced=FALSE;
out_no_cat=NULL;
SELECT merchant_serial
  INTO my_merchant_id
  FROM merchant_instances
 WHERE merchant_id=in_instance_id;
IF NOT FOUND
THEN
  out_no_instance=TRUE;
  RETURN;
END IF;
SELECT total_stock
      ,total_lost
      ,product_serial
  INTO rec
  FROM merchant_inventory
 WHERE merchant_serial=my_merchant_id
   AND product_id=in_product_id;
IF NOT FOUND
THEN
  out_no_product=TRUE;
  RETURN;
END IF;
my_product_serial = rec.product_serial;
IF rec.total_stock > in_total_stock
THEN
  out_stocked_reduced=TRUE;
  RETURN;
END IF;
IF rec.total_lost > in_total_lost
THEN
  out_lost_reduced=TRUE;
  RETURN;
END IF;
DELETE FROM merchant_product_categories
  WHERE product_serial=my_product_serial;
FOR i IN 1..COALESCE(array_length(ina_categories,1),0)
LOOP
  ini_cat=ina_categories[i];
  INSERT INTO merchant_product_categories
   (product_serial
   ,category_serial)
  VALUES
   (my_product_serial
   ,ini_cat)
  ON CONFLICT DO NOTHING;
  IF NOT FOUND
  THEN
    out_no_cat=i;
    RETURN;
  END IF;
END LOOP;
UPDATE merchant_inventory SET
   description=in_description
  ,description_i18n=in_description_i18n
  ,unit=in_unit
  ,image=in_image
  ,taxes=in_taxes
  ,price=in_price
  ,total_stock=in_total_stock
  ,total_lost=in_total_lost
  ,address=in_address
  ,next_restock=in_next_restock
  ,minimum_age=in_minimum_age
 WHERE merchant_serial=my_merchant_id
   AND product_serial=my_product_serial; 
ASSERT FOUND,'SELECTED it earlier, should UPDATE it now';
END $$;
DROP FUNCTION IF EXISTS merchant_do_account_kyc_set_status;
CREATE FUNCTION merchant_do_account_kyc_set_status (
  IN in_merchant_id TEXT,
  IN in_h_wire BYTEA,
  IN in_exchange_url TEXT,
  IN in_timestamp INT8,
  IN in_exchange_http_status INT4,
  IN in_exchange_ec_code INT4,
  IN in_access_token BYTEA, 
  IN in_jlimits TEXT,
  IN in_aml_active BOOL,
  IN in_kyc_ok BOOL,
  IN in_notify_str TEXT,
  IN in_notify2_str TEXT,
  OUT out_no_instance BOOL,
  OUT out_no_account BOOL)
LANGUAGE plpgsql
AS $$
DECLARE
  my_merchant_id INT8;
  my_account_serial INT8;
BEGIN
out_no_instance=FALSE;
out_no_account=FALSE;
SELECT merchant_serial
  INTO my_merchant_id
  FROM merchant_instances
 WHERE merchant_id=in_merchant_id;
IF NOT FOUND
THEN
  out_no_instance=TRUE;
  RETURN;
END IF;
SELECT account_serial
  INTO my_account_serial
  FROM merchant_accounts
 WHERE merchant_serial=my_merchant_id
   AND h_wire=in_h_wire;
IF NOT FOUND
THEN
  out_no_account=TRUE;
  RETURN;
END IF;
UPDATE merchant_kyc
   SET kyc_timestamp=in_timestamp
      ,kyc_ok=in_kyc_ok
      ,jaccount_limits=in_jlimits
      ,aml_review=in_aml_active
      ,exchange_http_status=in_exchange_http_status
      ,exchange_ec_code=in_exchange_ec_code
      ,access_token=in_access_token
 WHERE account_serial=my_account_serial
   AND exchange_url=in_exchange_url;
IF NOT FOUND
THEN
  INSERT INTO merchant_kyc
    (kyc_timestamp
    ,kyc_ok
    ,account_serial
    ,exchange_url
    ,jaccount_limits
    ,aml_review
    ,exchange_http_status
    ,exchange_ec_code
    ,access_token)
  VALUES
    (in_timestamp
    ,in_kyc_ok
    ,my_account_serial
    ,in_exchange_url
    ,in_jlimits
    ,in_aml_active
    ,in_exchange_http_status
    ,in_exchange_ec_code
    ,in_access_token);
END IF;
EXECUTE FORMAT (
   'NOTIFY %s'
  ,in_notify_str);
EXECUTE FORMAT (
   'NOTIFY %s'
  ,in_notify2_str);
END $$;
DROP FUNCTION IF EXISTS merchant_do_account_kyc_set_failed;
CREATE FUNCTION merchant_do_account_kyc_set_failed (
  IN in_merchant_id TEXT,
  IN in_h_wire BYTEA,
  IN in_exchange_url TEXT,
  IN in_timestamp INT8,
  IN in_exchange_http_status INT4,
  IN in_kyc_ok BOOL,
  IN in_notify_str TEXT,
  IN in_notify2_str TEXT,
  OUT out_no_instance BOOL,
  OUT out_no_account BOOL)
LANGUAGE plpgsql
AS $$
DECLARE
  my_merchant_id INT8;
  my_account_serial INT8;
BEGIN
out_no_instance=FALSE;
out_no_account=FALSE;
SELECT merchant_serial
  INTO my_merchant_id
  FROM merchant_instances
 WHERE merchant_id=in_merchant_id;
IF NOT FOUND
THEN
  out_no_instance=TRUE;
  RETURN;
END IF;
SELECT account_serial
  INTO my_account_serial
  FROM merchant_accounts
 WHERE merchant_serial=my_merchant_id
   AND h_wire=in_h_wire;
IF NOT FOUND
THEN
  out_no_account=TRUE;
  RETURN;
END IF;
UPDATE merchant_kyc
   SET kyc_timestamp=in_timestamp
      ,kyc_ok=in_kyc_ok
      ,exchange_http_status=in_exchange_http_status
      ,exchange_ec_code=0
 WHERE account_serial=my_account_serial
   AND exchange_url=in_exchange_url;
IF NOT FOUND
THEN
  INSERT INTO merchant_kyc
    (kyc_timestamp
    ,kyc_ok
    ,account_serial
    ,exchange_url
    ,exchange_http_status)
  VALUES
    (in_timestamp
    ,in_kyc_ok
    ,my_account_serial
    ,in_exchange_url
    ,in_exchange_http_status);
END IF;
EXECUTE FORMAT (
   'NOTIFY %s'
  ,in_notify_str);
EXECUTE FORMAT (
   'NOTIFY %s'
  ,in_notify2_str);
END $$;
COMMIT;
