

DO $$
  DECLARE
    t_corr record;
    t_inv record;
    t_closed_date date;
  BEGIN
    -- setup
    select max(end_date) into t_closed_date
      from account_checkpoint;

    -- fix the negative sellprice values on assemblies
    -- this fixes the historically incorrectly set values
    -- (the fix in mfg_lot__commit() fixes the calculation
    --  going forward)
    update invoice i
       set sellprice = sellprice * -1
     where qty < 0
       and sellprice < 0
       and exists (select 1
                     from parts p
                    where p.id = i.parts_id
                      and p.assembly);

    IF FOUND THEN
      insert into batch (batch_class_id, control_code, default_date,
                         description)
      values (10, 'UPG-ASSEMBLIES', now(),
              'This batch has been generated by the upgrade procedure; it contains corrective transactions to fix COGS postings for assemblies');

      create temporary sequence mig_ref_id;
      create temporary table correction_lines as
        select ac.*
         from acc_trans ac
         join ar
              on ar.id = ac.trans_id
         join invoice i
              on ac.invoice_id = i.id
         join account a
              on a.id = ac.chart_id
        where a.category in ('A', 'E') -- 'I' exists too, for sales
        and not (ac.amount_bc = 0 and ac.amount_tc = 0)
        and exists (select 1
                      from acc_trans
                             join account
                                 on acc_trans.chart_id = account.id
                     where acc_trans.invoice_id = i.id
                       and  account.category = 'E'
                       and ((ar.reverse and acc_trans.amount_bc < 0)
                            or (acc_trans.amount_bc > 0 and
                                ar.reverse is distinct from true)))
          and exists (select 1
                        from parts p
                       where assembly
                         and p.id = i.parts_id);

      alter table correction_lines
        add column orig_transdate date,
        add column corrected_monthend date,
        add column correction_date date;

      update correction_lines cl
         set corrected_monthend = (select (date_trunc('month', transdate)
                                           + '1 month - 1 day'::interval)::date),
             correction_date =
             CASE WHEN transdate <= t_closed_date
                  THEN (select max(end_date)
                          from account_checkpoint) + '1 day'::interval
                  ELSE (select (date_trunc('month', transdate)
                                + '1 month - 1 day'::interval)::date)
             END;

      alter table correction_lines
        add column correction_shifted boolean; -- true if shifted due to closed period

      update correction_lines
         set correction_shifted = NOT (
           date_trunc('month', corrected_monthend)
           = date_trunc('month', correction_date));

      for t_corr in
        select distinct corrected_monthend, correction_date, correction_shifted
        from correction_lines
      loop
        insert into gl (reference, transdate,
                        approved, trans_type_code,
                        description,
                        notes)
        values ('upg/ac-' || nextval('mig_ref_id'), t_corr.correction_date,
                false, 'up',
                'Upgrade transaction fixing assembly COGS for '
                || extract('year' from t_corr.corrected_monthend)
                || '-' || lpad(extract('month' from t_corr.corrected_monthend)::text, 2, '0'),
                ''
                || CASE
                WHEN t_corr.correction_shifted THEN
                   E'\n\nThe proposed posting date of this transaction was '
                   || 'set to the first day of the current open period; '
                   || 'the latest closing date being ' || t_closed_date
                   || '. By reopening closed periods, the proposed date '
                   || 'can be changed to the month the correction relates to.'
                   || E'\n\nNOTE: Re-opening reported years may not be '
                   || 'allowed in most jurisdictions; please consult an '
                   || 'accountant on how to deal with such situations.'
                ELSE
                   ''
                END);

        insert into workflow (workflow_id, type, state)
        values (nextval('workflow_seq')::int, 'GL', 'SAVED');
        insert into workflow_context (workflow_id, context)
        values (currval('workflow_seq')::int,
                format('{"transdate": "%s", "batch-id": %s}',
                       t_corr.correction_date,
                       currval('batch_id_seq')::int)::jsonb);
        update transactions
           set workflow_id = currval('workflow_seq')::int
         where id = currval('id')::int;

        insert into voucher (trans_id, batch_id, batch_class)
        values (currval('id')::int, currval('batch_id_seq')::int, 10);

        insert into correction_lines (
          trans_id, chart_id, transdate, source, cleared,
          invoice_id, approved, voucher_id, entry_id,
          amount_bc, amount_tc, curr,
          memo)
        select currval('id')::int, chart_id, t_corr.correction_date, null, false,
               invoice_id, false, currval('voucher_id_seq')::int, entry_id,
               -1*amount_bc, -1*amount_tc, curr,
               'Corrected COGS for invoice ' || (select invnumber from ar where ar.id = correction_lines.trans_id)
          from correction_lines
         where corrected_monthend = t_corr.corrected_monthend;

        -- this update works, because the insert above does not add
        -- values for corrected_monthend
        update correction_lines
           set trans_id   = currval('id')::int,
               voucher_id = currval('voucher_id_seq')::int,
               transdate  = t_corr.correction_date,
               approved   = false,
               cleared    = false,
               source     = null,
               amount_bc  = amount_bc * -1,
               amount_tc  = amount_tc * -1,
               memo       = 'Reversing original COGS posting for invoice ' || (select invnumber from ar where ar.id = correction_lines.trans_id)
         where corrected_monthend = t_corr.corrected_monthend;

        for t_inv in
          select distinct invoice_id
            from correction_lines cl
        loop
          insert into invoice (
            trans_id, parts_id, qty, allocated, sellprice, precision,
            fxsellprice, discount, assemblyitem, unit, deliverydate,
            serialnumber, vendor_sku,
            description, notes)
          select currval('id')::int, parts_id, 0, 0, sellprice, precision,
                 fxsellprice, discount, assemblyitem, unit, deliverydate,
                 serialnumber, vendor_sku,
                 'COGS-correction for invoice_id=' || t_inv.invoice_id, null
            from invoice
           where t_inv.invoice_id = invoice.id;

          update correction_lines
             set invoice_id = currval('invoice_id_seq')::int
           where invoice_id = t_inv.invoice_id;
        end loop;
      end loop;

      insert into acc_trans (
        trans_id, chart_id, transdate, source, cleared,
        invoice_id, approved, voucher_id, entry_id,
        amount_bc, amount_tc, curr,
        memo)
      select trans_id, chart_id, transdate, source, cleared,
          invoice_id, approved, voucher_id, nextval('acc_trans_entry_id_seq')::int,
          amount_bc, amount_tc, curr,
          memo
        from correction_lines
       order by invoice_id, memo desc, chart_id;


      perform pg_notify(
        'upgrade.' || current_database(),
        $json${"type":"feedback","content":"Please review: A batch with corrections for Cost of Goods Sold (COGS) for assembly sales needs your (and potentially a licensed accountant's advice and) review under the menu Transaction Approval > Batch: assembly-related-COGS was posted as negative cost instead of regular cost.\n\nThis release fixes that and proposes transactions to correct any historically incorrect acccounting. Each transaction corrects for the assembly-related-COGS postings in a specific month. Transactions relating to postings in closed periods have been proposed for posting on the first day of the current open period, which may not be what you want. You can either Post the batch or Delete it; for other options, please contact the LedgerSMB community through its chat channel or mailing lists. For more information, please consult https://ledgersmb.org/content/cogs-assembly-fix"}$json$ -- '
        );
    END IF;
  END;
$$ LANGUAGE plpgsql;

