<?php

function update_fifo_costs($delivery) {
    $delivery_no = key($delivery->trans_no);
    if (TRUE) {
        commit_transaction();
        begin_transaction();
        void_stock_move(ST_CUSTDELIVERY, $delivery_no);
        void_customer_trans_details(ST_CUSTDELIVERY, $delivery_no);
        void_fifo_allocs(ST_CUSTDELIVERY, $delivery_no, TRUE);
        foreach ($delivery->line_items as $line_no => $delivery_line) {

            $qty = $delivery_line->qty_dispatched;

            $line_price = $delivery_line->line_price();
            $line_taxfree_price = get_tax_free_price_for_item($delivery_line->stock_id, $delivery_line->price * $qty, 0, $delivery->tax_included, $delivery->tax_group_array);

            $line_tax = get_full_price_for_item($delivery_line->stock_id, $delivery_line->price * $qty, 0, $delivery->tax_included, $delivery->tax_group_array) - $line_taxfree_price;

            //$delivery_line->standard_cost = get_unit_cost($delivery_line->stock_id);
            $delivery_line->standard_cost = get_fifo_cost($delivery_line->stock_id, $delivery_line->qty_dispatched, $delivery->document_date);

            /* add delivery details for all lines */
            write_customer_trans_detail_item(ST_CUSTDELIVERY, $delivery_no, $delivery_line->stock_id, $delivery_line->item_description, $delivery_line->qty_dispatched, $delivery_line->line_price(), $qty ? $line_tax / $qty : 0, $delivery_line->discount_percent, $delivery_line->standard_cost, $delivery_line->src_id, 0);
            $detail_id = db_insert_id();

            if ($delivery_line->qty_dispatched != 0) {
                $stock_move_id = add_stock_move(ST_CUSTDELIVERY, $delivery_line->stock_id, $delivery_no, $delivery->Location, $delivery->document_date, $delivery->reference, -$delivery_line->qty_dispatched, $delivery_line->standard_cost, $line_price * (1 - $delivery_line->discount_percent));
                update_fifo_cleared_qty($delivery_no, $delivery->trans_type, $delivery_line->stock_id, $delivery_line->qty_dispatched, $delivery->document_date, $stock_move_id, $detail_id);

                $stock_gl_code = get_stock_gl_code($delivery_line->stock_id);
                /* insert gl_trans to credit stock and debit cost of sales at standard cost */
                $customer = get_customer($delivery->customer_id);
                $dim = ($delivery->dimension_id != $customer['dimension_id'] ? $delivery->dimension_id :
                        ($customer['dimension_id'] != 0 ? $customer["dimension_id"] : $stock_gl_code["dimension_id"]));
                $dim2 = ($delivery->dimension2_id != $customer['dimension2_id'] ? $delivery->dimension2_id :
                        ($customer['dimension2_id'] != 0 ? $customer["dimension2_id"] : $stock_gl_code["dimension2_id"]));

                if (is_inventory_item($delivery_line->stock_id)) {
                    // Invetory Items
                    if ($delivery_line->standard_cost != 0) {
                        /* first the cost of sales entry */
                        void_account_gl_trans(ST_CUSTDELIVERY, $delivery_no, $stock_gl_code["cogs_account"], true);
                        void_account_gl_trans(ST_CUSTDELIVERY, $delivery_no, $stock_gl_code["inventory_account"], true);

                        //display_warning("cost : ".($delivery_line->standard_cost * $delivery_line->qty_dispatched));
                        add_gl_trans_std_cost(ST_CUSTDELIVERY, $delivery_no, $delivery->document_date, $stock_gl_code["cogs_account"], $dim, $dim2, "", $delivery_line->standard_cost * $delivery_line->qty_dispatched, PT_CUSTOMER, $delivery->customer_id, "The cost of sales GL posting could not be inserted");
                        /* now the stock entry */
                        add_gl_trans_std_cost(ST_CUSTDELIVERY, $delivery_no, $delivery->document_date, $stock_gl_code["inventory_account"], 0, 0, "", (-$delivery_line->standard_cost * $delivery_line->qty_dispatched), PT_CUSTOMER, $delivery->customer_id, "The stock side of the cost of sales GL posting could not be inserted");
                    }
                } /* end of if GL and stock integrated and standard cost !=0 */
            } /* quantity dispatched is more than 0 */
        }
        commit_transaction();
    }
}

function stock_move_fifo_costing($type, $trans_no) {
    begin_transaction();
    $result = get_stock_moves($type, $trans_no);
    while ($myrow = db_fetch($result)) {
        if($myrow['qty'] < 0)
            update_fifo_cleared_qty($trans_no, $type, $myrow['stock_id'], ($myrow['qty'] * -1), sql2date ($myrow['tran_date']), $myrow['trans_id'], $myrow['trans_id']);
    }
    commit_transaction();
}

function check_credit_note($type, $credit_note) {
    if (is_array($credit_note->src_docs)) {
        $docs = array_keys($credit_note->src_docs);
        $credit_invoice = reset($docs);
    } else
        $credit_invoice = $credit_note->src_docs;

    $credit_type = $_POST['WriteOffGLCode'] == 0 ? 'Return' : 'WriteOff';
    if (!$credit_invoice && $credit_type === "Return") {
        return false;
    } else {
        return true;
    }
}

function cust_credit_note_fifo_costing($type, $credit_note) {
    $total = 0;
    if (is_array($credit_note->src_docs)) {
        $docs = array_keys($credit_note->src_docs);
        $credit_invoice = reset($docs);
    } else
        $credit_invoice = $credit_note->src_docs;

    $trans_no = $credit_no = is_array($credit_note->trans_no) ? key($credit_note->trans_no) : $credit_note->trans_no;
    $stock_moves = get_stock_moves($type, $trans_no);
    $credit_type = db_num_rows($stock_moves) ? "Return" : "WriteOff";
    if ($credit_invoice && $credit_type === "Return") {
        commit_transaction();
        begin_transaction();
        void_stock_move($type, $trans_no);
        void_customer_trans_details($type, $trans_no);
        foreach ($credit_note->line_items as $credit_line) {
            if (!$credit_line->quantity)
                continue;
            $line_taxfree_price = get_tax_free_price_for_item($credit_line->stock_id, $credit_line->price,
                    0, $credit_note->tax_included, $credit_note->tax_group_array);

            $line_tax = get_full_price_for_item($credit_line->stock_id, $credit_line->price,
                            0, $credit_note->tax_included, $credit_note->tax_group_array) - $line_taxfree_price;

            $credit_line->standard_cost = get_trans_details_cost($type, $trans_no, $credit_line->src_id);

            write_customer_trans_detail_item(ST_CUSTCREDIT, $credit_no, $credit_line->stock_id,
                    $credit_line->item_description, $credit_line->qty_dispatched,
                    $credit_line->line_price(), $line_tax, $credit_line->discount_percent,
                    $credit_line->standard_cost, $credit_line->src_id, 0);

            if ($credit_type == 'Return')
                add_fifo_credit_movements_item($credit_note, $credit_line,
                        $credit_type, ($line_taxfree_price + $line_tax) * (1 - $credit_line->discount_percent), $credit_invoice);

            $branch_data = get_branch_accounts($credit_note->Branch);
            $total += add_fifo_gl_trans_credit_costs($credit_note, $credit_line, $credit_no,
                    $credit_note->document_date, $credit_type, "", $branch_data);
        } /* end of credit_line loop */
        commit_transaction();
    }
}

function void_account_gl_trans($type, $trans_id, $account, $nested = false) {
    if (!$nested)
        begin_transaction();

    $sql = "UPDATE " . TB_PREF . "gl_trans SET amount=0 WHERE type=" . db_escape($type)
            . " AND type_no=" . db_escape($trans_id) . " AND account=" . db_escape($account);

    db_query($sql, "could not void gl transactions for type=$type , trans_no=$trans_id and account=$account ");

    if (!$nested)
        commit_transaction();
}

function get_trans_details_cost($type, $trans_no, $detail_id) {
    $sql = 'SELECT AVG(standard_cost) as cost FROM ' . TB_PREF . 'debtor_trans_details 
            where id in (select src_id from ' . TB_PREF . 'debtor_trans_details where id=' . db_escape($detail_id) . ')';
    //display_warning($sql);
    $result = db_query($sql);
    $row = db_fetch($result);
    return $row['cost'];
}

function get_fifo_cost($stock_id, $qty, $date) {
    $cost = 0;
    if ($qty > 0) {
        $sql = "set @param:=" . db_escape($qty);
        db_query($sql, "FIFO calculations failed.");
        $sql = "select purchasing.trans_id, purchasing.qty,purchasing.price,
            case when @param <= purchasing.qty then @param else purchasing.qty end  this_qty,
            case when @param <= purchasing.qty then @param else purchasing.qty end * IF(purchasing.type=".ST_INVADJUST.",purchasing.standard_cost, purchasing.price) this_value,
            @param := @param - purchasing.qty as remaining
            from ( select trans_id,sum(not_cleared) qty,IF(type=".ST_INVADJUST.",standard_cost, price) as price, type,standard_cost
                    from " . TB_PREF . "stock_moves where stock_id =" . db_escape($stock_id) . " AND type in (" . ST_SUPPRECEIVE . "," . ST_CUSTCREDIT . "," . ST_INVADJUST . "," . ST_WORKORDER . ") AND not_cleared>0 AND qty>0 AND tran_date<='" . date2sql($date) . "' group by price order by tran_date,trans_id
                 ) purchasing having (this_value > 0)";

        $result = db_query($sql, "FIFO calculations failed.");
        while ($myrow = db_fetch($result)) {
            $cost += ($myrow['price'] * $myrow['this_qty']);
            //display_warning("cost: $cost += price : ".$myrow['price']." * thisqty : ".$myrow['this_qty']);
        }
        try {
            $cost = $cost / $qty;
        } catch (Exception $ex) {
            $cost = 0;
        }
    }
    return $cost;
}

function update_fifo_cleared_qty($trans_no, $type, $stock_id, $qty, $date, $to_id, $detail_id) {
    $updated = true;
    $sql = "set @param:=" . db_escape($qty);
    db_query($sql, "FIFO calculations failed.");
    $sql = "select purchasing.trans_id, purchasing.type,purchasing.trans_no, purchasing.qty,purchasing.price,
            case when @param <= purchasing.qty then @param else purchasing.qty end  this_qty,
            case when @param <= purchasing.qty then @param else purchasing.qty end * IF(purchasing.type=".ST_INVADJUST.",purchasing.standard_cost, purchasing.price) this_value,
            @param := @param - purchasing.qty as remaining
            from ( select trans_id,not_cleared qty,IF(type=".ST_INVADJUST.",standard_cost, price) as price,trans_no,type,standard_cost
                    from " . TB_PREF . "stock_moves where stock_id =" . db_escape($stock_id) . " AND type in (" . ST_SUPPRECEIVE . "," . ST_CUSTCREDIT . "," . ST_INVADJUST . "," . ST_WORKORDER . ") AND not_cleared>0 AND qty>0 AND tran_date<='" . date2sql($date) . "' order by tran_date,trans_id
                 ) purchasing having (this_value > 0)";

    $result = db_query($sql, "FIFO calculations failed.");

    while ($myrow = db_fetch($result)) {
        $updated = db_query("UPDATE " . TB_PREF . "stock_moves set not_cleared=(not_cleared-" . ( $myrow['this_qty']) . ") WHERE trans_id=" . db_escape($myrow['trans_id']), "FIFO qty update failed.");
        add_fifo_alloc($trans_no, $type, $to_id, $myrow['type'], $myrow['trans_no'], $myrow['trans_id'], $myrow['this_qty'], $myrow['price'], $detail_id);
    }
    return $updated;
}

function add_fifo_alloc($trans_no, $type, $to_id, $from_type, $from_trans_no, $from_id, $qty_cleared, $cost, $detail_id) {

    $sql = "INSERT INTO " . TB_PREF . "fifo_allocs (trans_no,type,to_id,from_type,from_trans_no,from_id, qty_cleared, cost, detail_id)
			VALUES (" . db_escape($trans_no) . ", " . db_escape($type) . "," . db_escape($to_id) . ", " . db_escape($from_type) . ", " . db_escape($from_trans_no) . ", " . db_escape($from_id) . ", " . db_escape($qty_cleared) . ", " . db_escape($cost) . ", " . db_escape($detail_id) . ")";

    return db_query($sql, "Fifo Allocs could not be written");
}

function db_has_fifo_allocs($type, $trans_no) {
    return check_empty_result("SELECT COUNT(*) FROM " . TB_PREF . "fifo_allocs a "
            . "LEFT JOIN " . TB_PREF . "stock_moves m on a.from_id=m.trans_id where m.type=" . db_escape($type) . " and m.trans_no=" . db_escape($trans_no));
}

function void_fifo_allocs($type, $trans_no, $nested = false) {
    if (!$nested)
        begin_transaction();
    $cleared = TRUE;
    $sql = "SELECT * from " . TB_PREF . "fifo_allocs WHERE type=" . db_escape($type). " AND trans_no=" . db_escape($trans_no);
    $result = db_query($sql, "could not select fifo allocs for void ");
    while ($myrow = db_fetch($result)) {
        if (!db_query("UPDATE " . TB_PREF . "stock_moves set not_cleared=(not_cleared+" . $myrow['qty_cleared'] . ") WHERE trans_id=" . db_escape($myrow['from_id']), "FIFO qty void failed.")) {
            $cleared = FALSE;
        }
    }

    if ($cleared) {
        db_query("DELETE from " . TB_PREF . "fifo_allocs WHERE type=" . db_escape($type). " AND trans_no=" . db_escape($trans_no), "FIFO qty void failed.");
    }
    if (!$nested)
        commit_transaction();
}

function add_fifo_credit_movements_item(&$credit_note, &$credit_line,
        $credit_type, $price, $credited_invoice = 0) {
    //Chaitanya : Stamp current cost in stock moves $credit_line does not fetch cost
    $curr_std_cost = $credit_line->standard_cost;

    $reference = _("Return");
    if ($credited_invoice)
        $reference .= ' ' . _("Ex Inv:") . ' ' . $credited_invoice;

    $id = add_stock_move(ST_CUSTCREDIT, $credit_line->stock_id,
            key($credit_note->trans_no), $credit_note->Location,
            $credit_note->document_date, $reference, $credit_line->qty_dispatched,
            $curr_std_cost, $price);
    $sql = "UPDATE " . TB_PREF . "stock_moves set not_cleared=qty where trans_id=" . db_escape($id);
    db_query($sql, "could not update stock_moves not cleared qty");
}

function add_fifo_gl_trans_credit_costs($order, $order_line, $credit_no, $date_,
        $credit_type, $write_off_gl_code, &$branch_data) {
    $stock_gl_codes = get_stock_gl_code($order_line->stock_id);
    $customer = get_customer($order->customer_id);
    // If there is a Customer Dimension, then override with this,
    // else take the Item Dimension (if any)
    $dim = ($order->dimension_id != $customer['dimension_id'] ? $order->dimension_id :
            ($customer['dimension_id'] != 0 ? $customer["dimension_id"] : $stock_gl_codes["dimension_id"]));
    $dim2 = ($order->dimension2_id != $customer['dimension2_id'] ? $order->dimension2_id :
            ($customer['dimension2_id'] != 0 ? $customer["dimension2_id"] : $stock_gl_codes["dimension2_id"]));

    $total = 0;
    /* insert gl_trans to credit stock and debit cost of sales at standard cost */
    $unit_cost = $order_line->standard_cost;

    /* now the stock entry */
    if ($credit_type == "WriteOff") {
        $stock_entry_account = $write_off_gl_code;
    } else {
        $stock_gl_code = get_stock_gl_code($order_line->stock_id);
        $stock_entry_account = $stock_gl_code["inventory_account"];

        $g = 0;
        $old_cost = get_unit_cost($order_line->stock_id);
        $sql = "SELECT * FROM " . TB_PREF . "gl_trans WHERE type=" . db_escape($order->trans_type)
                . " AND type_no=" . db_escape($credit_no) . " AND account IN (" . db_escape($stock_gl_codes["cogs_account"]) . "," . db_escape($stock_entry_account) . ") AND amount=" . db_escape($old_cost);
        //display_error($sql);
        $gl_trans = db_query($sql);

        while ($trans = db_fetch($gl_trans)) {
            if ($g < 2) {
                $void_sql = "UPDATE " . TB_PREF . "gl_trans SET amount=0 WHERE counter=" . db_escape($trans['counter']);
                db_query($void_sql);
                $g++;
            }
        }

        if ($stock_gl_codes['mb_flag'] != 'D' && $unit_cost != 0) {
            /* first the cost of sales entry */

            $total += add_gl_trans_std_cost(ST_CUSTCREDIT, $credit_no, $date_, $stock_gl_codes["cogs_account"],
                    $dim, $dim2, "", -($unit_cost * $order_line->qty_dispatched),
                    PT_CUSTOMER, $order->customer_id,
                    "The cost of sales GL posting could not be inserted");
        }

        $total += add_gl_trans_std_cost(ST_CUSTCREDIT, $credit_no, $date_, $stock_entry_account, 0, 0,
                "", ($unit_cost * $order_line->qty_dispatched),
                PT_CUSTOMER, $order->customer_id,
                "The stock side (or write off) of the cost of sales GL posting could not be inserted");
    } /* end of if GL and stock integrated and standard cost !=0 */
    return $total;
}
