-- Finaccount-like Excel API POC for GL ACCOUNTS FUNCTIONS.
--
-- Scope:
--   - GetAccountBaseAmount
--   - GetAccountRealBaseAmount
--   - GetAccountAmount
--   - GetAccountAbbr
--   - GetAccountName
--   - GetAccountContact
--
-- Design notes:
--   - PostgreSQL lowercases function names; VBA calls these SQL functions.
--   - Balances are computed as of p_value_date.
--   - This POC uses posted account moves only.
--   - p_currency_iso is treated as the transaction currency.
--   - p_company_key accepts either the company party code or company name.
--   - p_currency_iso accepts the currency code/name/symbol/numeric code/id.
--   - If account.move.line.second_currency is empty, the company currency is
--     used as transaction currency and the company amount is reused.

create schema if not exists excel_api;

create or replace view excel_api.gl_account_lines as
select
    aml.id as move_line_id,
    am.id as move_id,
    cc.id as company_id,
    cp.code as company_key,
    cp.name as company_name,
    aa.id as account_id,
    aa.code as account_code,
    aa.name as account_name,
    coalesce(tx_cur.code, base_cur.code) as transaction_currency_code,
    coalesce(aml.second_currency, cc.currency) as transaction_currency_id,
    base_cur.code as base_currency_code,
    cc.currency as base_currency_id,
    am.date as posting_date,
    am.state as posting_status,
    aml.party as party_id,
    party.code as party_code,
    party.name as party_name,
    coalesce(aml.debit, 0) as debit_base_currency,
    coalesce(aml.credit, 0) as credit_base_currency,
    coalesce(aml.debit, 0) - coalesce(aml.credit, 0)
        as balance_base_currency,
    case
        when aml.second_currency is not null then
            coalesce(aml.amount_second_currency, 0)
        else
            coalesce(aml.debit, 0) - coalesce(aml.credit, 0)
    end as balance_transaction_currency
from account_move_line aml
join account_move am on am.id = aml.move
join account_account aa on aa.id = aml.account
join company_company cc on cc.id = am.company
left join party_party cp on cp.id = cc.party
left join currency_currency base_cur on base_cur.id = cc.currency
left join currency_currency tx_cur on tx_cur.id = aml.second_currency
left join party_party party on party.id = aml.party
where aa.type is not null
  and coalesce(aa.closed, false) is false;

create or replace function excel_api.currency_rate_at(
    p_currency_id integer,
    p_value_date date)
returns numeric
language sql
stable
as $$
    select cr.rate
    from currency_currency_rate cr
    where cr.currency = p_currency_id
      and cr.date <= p_value_date
    order by cr.date desc
    limit 1
$$;

create or replace function excel_api.company_matches(
    p_company_key text,
    p_company_input text)
returns boolean
language sql
stable
as $$
    select upper(btrim(coalesce(p_company_key, '')))
            = upper(btrim(coalesce(p_company_input, '')))
        or upper(btrim(coalesce(p_company_input, ''))) = (
            select upper(btrim(coalesce(pp.name, '')))
            from company_company cc
            left join party_party pp on pp.id = cc.party
            where pp.code = p_company_key
            limit 1)
$$;

create or replace function excel_api.currency_matches(
    p_currency_id integer,
    p_currency_input text)
returns boolean
language sql
stable
as $$
    select exists (
        select 1
        from currency_currency cur
        where cur.id = p_currency_id
          and upper(btrim(coalesce(p_currency_input, ''))) in (
              upper(btrim(cur.id::text)),
              upper(btrim(coalesce(cur.code, ''))),
              upper(btrim(coalesce(cur.name, ''))),
              upper(btrim(coalesce(cur.symbol, ''))),
              upper(btrim(coalesce(cur.numeric_code, '')))))
$$;

create or replace function excel_api.get_account_amount(
    p_company_key text,
    p_account_code text,
    p_currency_iso text,
    p_value_date date)
returns numeric
language sql
stable
as $$
    select coalesce(sum(gl.balance_transaction_currency), 0)
    from excel_api.gl_account_lines gl
    where excel_api.company_matches(gl.company_key, p_company_key)
      and gl.account_code = p_account_code
      and excel_api.currency_matches(gl.transaction_currency_id, p_currency_iso)
      and gl.posting_date <= p_value_date
      and gl.posting_status = 'posted'
$$;

create or replace function excel_api.get_account_real_base_amount(
    p_company_key text,
    p_account_code text,
    p_currency_iso text,
    p_value_date date)
returns numeric
language sql
stable
as $$
    select coalesce(sum(gl.balance_base_currency), 0)
    from excel_api.gl_account_lines gl
    where excel_api.company_matches(gl.company_key, p_company_key)
      and gl.account_code = p_account_code
      and excel_api.currency_matches(gl.transaction_currency_id, p_currency_iso)
      and gl.posting_date <= p_value_date
      and gl.posting_status = 'posted'
$$;

create or replace function excel_api.get_account_base_amount(
    p_company_key text,
    p_account_code text,
    p_currency_iso text,
    p_value_date date)
returns numeric
language sql
stable
as $$
    select coalesce(sum(
        case
            when gl.transaction_currency_id = gl.base_currency_id then
                gl.balance_transaction_currency
            else
                gl.balance_transaction_currency
                * excel_api.currency_rate_at(gl.base_currency_id, p_value_date)
                / nullif(
                    excel_api.currency_rate_at(
                        gl.transaction_currency_id, p_value_date),
                    0)
        end), 0)
    from excel_api.gl_account_lines gl
    where excel_api.company_matches(gl.company_key, p_company_key)
      and gl.account_code = p_account_code
      and excel_api.currency_matches(gl.transaction_currency_id, p_currency_iso)
      and gl.posting_date <= p_value_date
      and gl.posting_status = 'posted'
$$;

create or replace function excel_api.get_account_abbr(
    p_company_key text,
    p_account_code text,
    p_currency_iso text,
    p_value_date date)
returns text
language sql
stable
as $$
    select gl.account_code
    from excel_api.gl_account_lines gl
    where excel_api.company_matches(gl.company_key, p_company_key)
      and gl.account_code = p_account_code
      and excel_api.currency_matches(gl.transaction_currency_id, p_currency_iso)
      and gl.posting_date <= p_value_date
    order by gl.posting_date desc, gl.move_line_id desc
    limit 1
$$;

create or replace function excel_api.get_account_name(
    p_company_key text,
    p_account_code text,
    p_currency_iso text,
    p_value_date date)
returns text
language sql
stable
as $$
    select gl.account_name
    from excel_api.gl_account_lines gl
    where excel_api.company_matches(gl.company_key, p_company_key)
      and gl.account_code = p_account_code
      and excel_api.currency_matches(gl.transaction_currency_id, p_currency_iso)
      and gl.posting_date <= p_value_date
    order by gl.posting_date desc, gl.move_line_id desc
    limit 1
$$;

create or replace function excel_api.get_account_contact(
    p_company_key text,
    p_account_code text,
    p_currency_iso text,
    p_value_date date)
returns text
language sql
stable
as $$
    select nullif(string_agg(distinct coalesce(gl.party_code, gl.party_name),
        ', ' order by coalesce(gl.party_code, gl.party_name)), '')
    from excel_api.gl_account_lines gl
    where excel_api.company_matches(gl.company_key, p_company_key)
      and gl.account_code = p_account_code
      and excel_api.currency_matches(gl.transaction_currency_id, p_currency_iso)
      and gl.posting_date <= p_value_date
      and gl.posting_status = 'posted'
      and coalesce(gl.party_code, gl.party_name) is not null
$$;
