Is it a bad thing to allow FA user to select a different Customer during Customer Payment Edition?

27

(10 replies, posted in FA Modifications)

Packing slip was already present in FA.
What was added here was the total weight and volume so YES you can print the Packing Slip.

28

(20 replies, posted in Report Bugs here)

Add parenthesis to the first condition.
Replace:

$reg_type = in_array($trans_type, array(ST_SUPPINVOICE, ST_SUPPCREDIT)) ? TR_OUTPUT
                : in_array($trans_type, array(ST_SALESINVOICE, ST_CUSTCREDIT)) ? TR_INPUT : null;

with:

$reg_type = (in_array($trans_type, array(ST_SUPPINVOICE, ST_SUPPCREDIT)) ? TR_OUTPUT
                : in_array($trans_type, array(ST_SALESINVOICE, ST_CUSTCREDIT))) ? TR_INPUT : null;

I have also noted more deprecation errors on php 7.4. I dont know how FA team intends to maintain compatibility to new php versions.

In that case, I agree with you.
@Joe should consider integrating it to the core.

In my opinion, Journal Entries are meant to affect selected account on either side of debit or credit. Should any side be pre-assumed as default for certain account types?.
You can use Payments module to post expenses and you wont have to worry about wrong posting.

I appreciate your response. The purpose of the theme was a more user friendly menu. Making FA responsive and still maintaining its neat layout would be a huge task which I am not ready to embark on for now.

32

(6 replies, posted in Setup)

Hello,
It will be good to separate add and edit roles i.e. a user can have permissions to add a customer but has no permissions to edit customers. I experienced this need when i have a sales person who is required to create customers and place orders for the customer but cannot edit customer's Credit Status and contact details on a later date. This is because the admin can set credit status to "no more  work until payment received" and only permitted users can reverse.
It would be great to have this in even more modules such as Add/Edit transactions.

Please advice on the right approach and I will embark on the modifications myself. It will be great if this is also included in core FA.

Hello everyone,
FA default theme is very neat and good looking. However it would be good to have user friendly menus but maintaining the same neatness. I have created one which i think is worthwhile.
DEMO HERE
USER: demo
PASS: 1234

If interested please contact me: detkenn@gmail.com

Ok. Lemmi embark on this.. I will update you when i have something.
Thank you

Thank you @kvvaradha.
How about putting a select drop-down to select item tax type instead of a checkbox and pass the value to tax calculations. The default item tax type being the one attached to the item.
As you suggested, this will also necessitate creation of a column in  debtors_trans_details and sales_order_details.

What do you think?

Anyone please?

37

(10 replies, posted in FA Modifications)

@joe Any progress on this issue?

How did this go?
I am interested

Seems like solutions offered to this issue are somehow more of hacks. If we can agree on the proper way of handling this, I would dedicate myself in programming for it to be included in FA.
I will not mind the scope because I really need it.

I invite you to give suggestions on the best approach to this.

40

(10 replies, posted in FA Modifications)

This Modification affects various sections. I have highlighted in red sections that have changed except for the report.

File: \inventory\manage\items.php

...

function clear_data()
{
    unset($_POST['long_description']);
    unset($_POST['description']);
    unset($_POST['category_id']);
    unset($_POST['tax_type_id']);
    unset($_POST['units']);
    unset($_POST['mb_flag']);
    unset($_POST['NewStockID']);
    unset($_POST['dimension_id']);
    unset($_POST['dimension2_id']);
    unset($_POST['no_sale']);
    unset($_POST['no_purchase']);
    unset($_POST['depreciation_method']);
    unset($_POST['depreciation_rate']);
    unset($_POST['depreciation_factor']);
    unset($_POST['depreciation_start']);
    unset($_POST['weight']);
    unset($_POST['weight_uom']);
    unset($_POST['volume']);
    unset($_POST['volume_uom']);

}

//------------------------------------------------------------------------------------

if (isset($_POST['addupdate']))
{

    $input_error = 0;
    if ($upload_file == 'No')
        $input_error = 1;
    if (strlen($_POST['description']) == 0)
    {
        $input_error = 1;
        display_error( _('The item name must be entered.'));
        set_focus('description');
    }
    elseif (strlen($_POST['NewStockID']) == 0)
    {
        $input_error = 1;
        display_error( _('The item code cannot be empty'));
        set_focus('NewStockID');
    }
    elseif (strstr($_POST['NewStockID'], " ") || strstr($_POST['NewStockID'],"'") ||
        strstr($_POST['NewStockID'], "+") || strstr($_POST['NewStockID'], "\"") ||
        strstr($_POST['NewStockID'], "&") || strstr($_POST['NewStockID'], "\t"))
    {
        $input_error = 1;
        display_error( _('The item code cannot contain any of the following characters -  & + OR a space OR quotes'));
        set_focus('NewStockID');

    }
    elseif ($new_item && db_num_rows(get_item_kit($_POST['NewStockID'])))
    {
              $input_error = 1;
              display_error( _("This item code is already assigned to stock item or sale kit."));
            set_focus('NewStockID');
    }
   
  if (get_post('fixed_asset')) {
    if ($_POST['depreciation_rate'] > 100) {
      $_POST['depreciation_rate'] = 100;
    }
    elseif ($_POST['depreciation_rate'] < 0) {
      $_POST['depreciation_rate'] = 0;
    }
    $move_row = get_fixed_asset_move($_POST['NewStockID'], ST_SUPPRECEIVE);
    if (isset($_POST['depreciation_start']) && strtotime($_POST['depreciation_start']) < strtotime($move_row['tran_date'])) {
      display_warning(_('The depracation cannot start before the fixed asset purchase date'));
    }
  }
   
    if ($input_error != 1)
    {
        if (check_value('del_image'))
        {
            $filename = company_path().'/images/'.item_img_name($_POST['NewStockID']).".jpg";
            if (file_exists($filename))
                unlink($filename);
        }
       
        if (!$new_item)
        { /*so its an existing one */
            update_item($_POST['NewStockID'], $_POST['description'],
                $_POST['long_description'], $_POST['category_id'],
                $_POST['tax_type_id'], get_post('units'),
                get_post('fixed_asset') ? 'F' : get_post('mb_flag'), $_POST['sales_account'],
                $_POST['inventory_account'], $_POST['cogs_account'],
                $_POST['adjustment_account'], $_POST['wip_account'],
                $_POST['dimension_id'], $_POST['dimension2_id'],
                check_value('no_sale'), check_value('editable'), check_value('no_purchase'),
                get_post('depreciation_method'), input_num('depreciation_rate'), input_num('depreciation_factor'), get_post('depreciation_start'),
                get_post('fa_class_id'),  input_num("weight"), get_post("weight_uom"),  input_num("volume"), get_post("volume_uom"));

            update_record_status($_POST['NewStockID'], $_POST['inactive'],
                'stock_master', 'stock_id');
            update_record_status($_POST['NewStockID'], $_POST['inactive'],
                'item_codes', 'item_code');
            set_focus('stock_id');
            $Ajax->activate('stock_id'); // in case of status change
            display_notification(_("Item has been updated."));
        }
        else
        { //it is a NEW part

            add_item($_POST['NewStockID'], $_POST['description'],
                $_POST['long_description'], $_POST['category_id'], $_POST['tax_type_id'],
                $_POST['units'], get_post('fixed_asset') ? 'F' : get_post('mb_flag'), $_POST['sales_account'],
                $_POST['inventory_account'], $_POST['cogs_account'],
                $_POST['adjustment_account'], $_POST['wip_account'],
                $_POST['dimension_id'], $_POST['dimension2_id'],
                check_value('no_sale'), check_value('editable'), check_value('no_purchase'),
                get_post('depreciation_method'), input_num('depreciation_rate'), input_num('depreciation_factor'), get_post('depreciation_start'),
                get_post('fa_class_id'),  input_num("weight"), get_post("weight_uom"),  input_num("volume"), get_post("volume_uom"));

            display_notification(_("A new item has been added."));
            $_POST['stock_id'] = $_POST['NewStockID'] =
            $_POST['description'] = $_POST['long_description'] = '';
            $_POST['no_sale'] = $_POST['editable'] = $_POST['no_purchase'] =0;
            set_focus('NewStockID');
        }
        $Ajax->activate('_page_body');
    }
}

if (get_post('clone')) {
    unset($_POST['stock_id']);
    $stock_id = '';
    unset($_POST['inactive']);
    set_focus('NewStockID');
    $Ajax->activate('_page_body');
}

//------------------------------------------------------------------------------------

function check_usage($stock_id, $dispmsg=true)
{
    $msg = item_in_foreign_codes($stock_id);

    if ($msg != '')    {
        if($dispmsg) display_error($msg);
        return false;
    }
    return true;
}

//------------------------------------------------------------------------------------

if (isset($_POST['delete']) && strlen($_POST['delete']) > 1)
{

    if (check_usage($_POST['NewStockID'])) {

        $stock_id = $_POST['NewStockID'];
        delete_item($stock_id);
        $filename = company_path().'/images/'.item_img_name($stock_id).".jpg";
        if (file_exists($filename))
            unlink($filename);
        display_notification(_("Selected item has been deleted."));
        $_POST['stock_id'] = '';
        clear_data();
        set_focus('stock_id');
        $new_item = true;
        $Ajax->activate('_page_body');
    }
}

function item_settings(&$stock_id, $new_item)
{
    global $SysPrefs, $path_to_root, $page_nested, $depreciation_methods;

    start_outer_table(TABLESTYLE2);

    table_section(1);

    table_section_title(_("General Settings"));

    //------------------------------------------------------------------------------------
    if ($new_item)
    {
        $tmpCodeID=null;
        $post_label = null;
        if (!empty($SysPrefs->prefs['barcodes_on_stock']))
        {
            $post_label = '<button class="ajaxsubmit" type="submit" aspect=\'default\'  name="generateBarcode"  id="generateBarcode" value="Generate Barcode EAN8"> '._("Generate EAN-8 Barcode").' </button>';
            if (isset($_POST['generateBarcode']))
            {
                $tmpCodeID=generateBarcode();
                $_POST['NewStockID'] = $tmpCodeID;
            }
        }   
        text_row(_("Item Code:"), 'NewStockID', $tmpCodeID, 21, 20, null, "", $post_label);
        $_POST['inactive'] = 0;
    }
    else
    { // Must be modifying an existing item
        if (get_post('NewStockID') != get_post('stock_id') || get_post('addupdate')) { // first item display

            $_POST['NewStockID'] = $_POST['stock_id'];

            $myrow = get_item($_POST['NewStockID']);

            $_POST['long_description'] = $myrow["long_description"];
            $_POST['description'] = $myrow["description"];
            $_POST['category_id']  = $myrow["category_id"];
            $_POST['tax_type_id']  = $myrow["tax_type_id"];
            $_POST['units']  = $myrow["units"];
            $_POST['mb_flag']  = $myrow["mb_flag"];

            $_POST['depreciation_method'] = $myrow['depreciation_method'];
            $_POST['depreciation_rate'] = number_format2($myrow['depreciation_rate'], 1);
            $_POST['depreciation_factor'] = number_format2($myrow['depreciation_factor'], 1);
            $_POST['depreciation_start'] = sql2date($myrow['depreciation_start']);
            $_POST['depreciation_date'] = sql2date($myrow['depreciation_date']);
            $_POST['fa_class_id'] = $myrow['fa_class_id'];
            $_POST['material_cost'] = $myrow['material_cost'];
            $_POST['purchase_cost'] = $myrow['purchase_cost'];
           
            $_POST['sales_account'] =  $myrow['sales_account'];
            $_POST['inventory_account'] = $myrow['inventory_account'];
            $_POST['cogs_account'] = $myrow['cogs_account'];
            $_POST['adjustment_account']    = $myrow['adjustment_account'];
            $_POST['wip_account']    = $myrow['wip_account'];
            $_POST['dimension_id']    = $myrow['dimension_id'];
            $_POST['dimension2_id']    = $myrow['dimension2_id'];
            $_POST['no_sale']    = $myrow['no_sale'];
            $_POST['no_purchase']    = $myrow['no_purchase'];
            $_POST['del_image'] = 0;
            $_POST['inactive'] = $myrow["inactive"];
            $_POST['editable'] = $myrow["editable"];
            $_POST['weight'] = $myrow["weight"];
            $_POST['weight_uom'] = $myrow["weight_uom"];
            $_POST['volume'] = $myrow["volume"];
            $_POST['volume_uom'] = $myrow["volume_uom"];

        }
        label_row(_("Item Code:"),$_POST['NewStockID']);
        hidden('NewStockID', $_POST['NewStockID']);
        set_focus('description');
    }
    $fixed_asset = get_post('fixed_asset');

    text_row(_("Name:"), 'description', null, 52, 200);

    textarea_row(_('Description:'), 'long_description', null, 42, 3);

    stock_categories_list_row(_("Category:"), 'category_id', null, false, $new_item, $fixed_asset);

    if ($new_item && (list_updated('category_id') || !isset($_POST['units']))) {

        $category_record = get_item_category($_POST['category_id']);

        $_POST['tax_type_id'] = $category_record["dflt_tax_type"];
        $_POST['units'] = $category_record["dflt_units"];
        $_POST['mb_flag'] = $category_record["dflt_mb_flag"];
        $_POST['inventory_account'] = $category_record["dflt_inventory_act"];
        $_POST['cogs_account'] = $category_record["dflt_cogs_act"];
        $_POST['sales_account'] = $category_record["dflt_sales_act"];
        $_POST['adjustment_account'] = $category_record["dflt_adjustment_act"];
        $_POST['wip_account'] = $category_record["dflt_wip_act"];
        $_POST['dimension_id'] = $category_record["dflt_dim1"];
        $_POST['dimension2_id'] = $category_record["dflt_dim2"];
        $_POST['no_sale'] = $category_record["dflt_no_sale"];
        $_POST['no_purchase'] = $category_record["dflt_no_purchase"];
        $_POST['editable'] = 0;

    }
    $fresh_item = !isset($_POST['NewStockID']) || $new_item
        || check_usage($_POST['stock_id'],false);

    // show inactive item tax type in selector only if already set.
  item_tax_types_list_row(_("Item Tax Type:"), 'tax_type_id', null, !$new_item && item_type_inactive(get_post('tax_type_id')));

    if (!get_post('fixed_asset'))
        stock_item_types_list_row(_("Item Type:"), 'mb_flag', null, $fresh_item);

    stock_units_list_row(_('Units of Measure:'), 'units', null, TRUE);

    check_row(_("Editable description:"), 'editable');

    if (get_post('fixed_asset'))
        hidden('no_sale', 0);
    else
        check_row(_("Exclude from sales:"), 'no_sale');

    check_row(_("Exclude from purchases:"), 'no_purchase');

        $result = get_all_item_units();
        while ($unit = db_fetch($result))
            $units[$unit['abbr']] = $unit['name'];
        amount_row(_("Weight:"), "weight", null, null, array_selector("weight_uom", null, $units, array('disabled' => false)));
        amount_row(_("Volume:"), "volume", null, null, array_selector("volume_uom", null, $units, array('disabled' => false)));

        ...

Modifying add and Update db functions
File : inventory\includes\db\items_db.inc

function update_item($stock_id, $description, $long_description, $category_id,
    $tax_type_id, $units='', $mb_flag='', $sales_account, $inventory_account,
    $cogs_account,     $adjustment_account, $wip_account, $dimension_id,
    $dimension2_id, $no_sale, $editable, $no_purchase,
    $depreciation_method = 'D', $depreciation_rate=100, $depreciation_factor=1,
    $depreciation_start=null, $fa_class_id=null,$weight = 0,$weight_uom = '',$volume = 0,$volume_uom = '')
{
    $sql = "UPDATE ".TB_PREF."stock_master SET long_description=".db_escape($long_description).",
        description=".db_escape($description).",
        category_id=".db_escape($category_id).",
        sales_account=".db_escape($sales_account).",
        inventory_account=".db_escape($inventory_account).",
        cogs_account=".db_escape($cogs_account).",
        adjustment_account=".db_escape($adjustment_account).",
        wip_account=".db_escape($wip_account).",
        dimension_id=".db_escape($dimension_id).",
        dimension2_id=".db_escape($dimension2_id).",
        tax_type_id=".db_escape($tax_type_id).",
        no_sale=".db_escape($no_sale).",
        no_purchase=".db_escape($no_purchase).",
        editable=".db_escape($editable).",
        depreciation_method=".db_escape($depreciation_method).",
        depreciation_rate=".db_escape($depreciation_rate).",
        depreciation_factor=".db_escape($depreciation_factor).",
        fa_class_id=".db_escape($fa_class_id);

    if ($units != '')
        $sql .= ", units=".db_escape($units);
       
        if (is_numeric($weight)){
        $sql .= ", weight=".db_escape($weight);
                $sql .= ", weight_uom=".db_escape($weight_uom);
        }
       
        if (is_numeric($volume)){
        $sql .= ", volume=".db_escape($volume);
                $sql .= ", volume_uom=".db_escape($volume_uom);
        }

    if ($mb_flag != '')
        $sql .= ", mb_flag=".db_escape($mb_flag);

    if ($depreciation_start != '') {
        $sql .= ", depreciation_start='".date2sql($depreciation_start)."'"
            .", depreciation_date='".date2sql($depreciation_start)."'";
    }

    $sql .= " WHERE stock_id=".db_escape($stock_id);

    db_query($sql, "The item could not be updated");

    update_item_code(-1, $stock_id, $stock_id, $description, $category_id, 1, 0);
}

function add_item($stock_id, $description, $long_description, $category_id,
    $tax_type_id, $units, $mb_flag,    $sales_account, $inventory_account,
    $cogs_account, $adjustment_account,    $wip_account, $dimension_id,
    $dimension2_id, $no_sale, $editable, $no_purchase,
    $depreciation_method='D', $depreciation_rate=100,  $depreciation_factor=1, $depreciation_start=null,
    $fa_class_id=null,$weight = 0,$weight_uom = '',$volume = 0,$volume_uom = '')
{
    $sql = "INSERT INTO ".TB_PREF."stock_master (stock_id, description, long_description, category_id,
        tax_type_id, units, mb_flag, sales_account, inventory_account, cogs_account,
        adjustment_account, wip_account, dimension_id, dimension2_id, no_sale, no_purchase, editable,
        depreciation_method, depreciation_rate, depreciation_factor, depreciation_start, depreciation_date, fa_class_id, weight, weight_uom, voume, volume_uom)
        VALUES (".db_escape($stock_id).", ".db_escape($description).", ".db_escape($long_description).",
        ".db_escape($category_id).", ".db_escape($tax_type_id).", "
        .db_escape($units).", ".db_escape($mb_flag).",
        ".db_escape($sales_account).", ".db_escape($inventory_account)
        .", ".db_escape($cogs_account).",".db_escape($adjustment_account)
        .", ".db_escape($wip_account).", "
        .db_escape($dimension_id).", ".db_escape($dimension2_id).","
        .db_escape($no_sale).","
        .db_escape($no_purchase).","
        .db_escape($editable).","
        .db_escape($depreciation_method).",".db_escape($depreciation_rate).",".db_escape($depreciation_factor).",'"
        .date2sql($depreciation_start)."','".date2sql($depreciation_start)."',"
        .db_escape($fa_class_id).", ".  db_escape($weight).", ".  db_escape($weight_uom).", ".  db_escape($volume).", ".  db_escape($volume_uom).")";

    db_query($sql, "The item could not be added");

    $sql = "INSERT INTO ".TB_PREF."loc_stock (loc_code, stock_id)
        SELECT loc_code, ".db_escape($stock_id)
        ." FROM ".TB_PREF."locations";

    db_query($sql, "The item locstock could not be added");

    add_item_code($stock_id, $stock_id, $description, $category_id, 1, 0);
}

The Stock master table will change as follows:

ALTER TABLE `0_stock_master`
ADD COLUMN `weight` DOUBLE NULL AFTER `fa_class_id`,
ADD COLUMN `weight_uom` VARCHAR(20) NULL AFTER `weight`,
ADD COLUMN `volume` DOUBLE NULL AFTER `weight_uom`,
ADD COLUMN `volume_uom` VARCHAR(20) NULL AFTER `volume`;

Modifying Payslip Report..
File : rep110

<?php
/**********************************************************************
    Copyright (C) FrontAccounting, LLC.
    Released under the terms of the GNU General Public License, GPL,
    as published by the Free Software Foundation, either version 3
    of the License, or (at your option) any later version.
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
    See the License here <http://www.gnu.org/licenses/gpl-3.0.html>.
***********************************************************************/
$page_security = $_POST['PARAM_0'] == $_POST['PARAM_1'] ?
    'SA_SALESTRANSVIEW' : 'SA_SALESBULKREP';
// ----------------------------------------------------------------
// $ Revision:    2.0 $
// Creator:    Janusz Dobrwolski
// date_:    2008-01-14
// Title:    Print Delivery Notes
// ----------------------------------------------------------------
$path_to_root="..";

include_once($path_to_root . "/includes/session.inc");
include_once($path_to_root . "/includes/date_functions.inc");
include_once($path_to_root . "/includes/data_checks.inc");
include_once($path_to_root . "/sales/includes/sales_db.inc");

//----------------------------------------------------------------------------------------------------

print_deliveries();

//----------------------------------------------------------------------------------------------------

function print_deliveries()
{
    global $path_to_root, $SysPrefs;

    include_once($path_to_root . "/reporting/includes/pdf_report.inc");

    $from = $_POST['PARAM_0'];
    $to = $_POST['PARAM_1'];
    $email = $_POST['PARAM_2'];
    $packing_slip = $_POST['PARAM_3'];
    $comments = $_POST['PARAM_4'];
    $orientation = $_POST['PARAM_5'];

    if (!$from || !$to) return;

    $orientation = ($orientation ? 'L' : 'P');
    $dec = user_price_dec();

    $fno = explode("-", $from);
    $tno = explode("-", $to);
    $from = min($fno[0], $tno[0]);
    $to = max($fno[0], $tno[0]);

    $cols = array(4, 60, 190, 265, 295, 340, 400, 460, 515);

    // $headers in doctext.inc
    $aligns = array('left',    'left',    'right', 'left', 'right', 'right', 'right', 'right');

    $params = array('comments' => $comments, 'packing_slip' => $packing_slip);

    $cur = get_company_Pref('curr_default');

    if ($email == 0)
    {
        if ($packing_slip == 0)
            $rep = new FrontReport(_('DELIVERY'), "DeliveryNoteBulk", user_pagesize(), 9, $orientation);
        else
            $rep = new FrontReport(_('PACKING SLIP'), "PackingSlipBulk", user_pagesize(), 9, $orientation);
    }
    if ($orientation == 'L')
        recalculate_cols($cols);
    for ($i = $from; $i <= $to; $i++)
    {
            if (!exists_customer_trans(ST_CUSTDELIVERY, $i))
                continue;
            $myrow = get_customer_trans($i, ST_CUSTDELIVERY);
            $branch = get_branch($myrow["branch_code"]);
            $sales_order = get_sales_order_header($myrow["order_"], ST_SALESORDER); // ?
            if ($email == 1)
            {
                $rep = new FrontReport("", "", user_pagesize(), 9, $orientation);
                if ($packing_slip == 0)
                {
                    $rep->title = _('DELIVERY NOTE');
                    $rep->filename = "Delivery" . $myrow['reference'] . ".pdf";
                }
                else
                {
                    $rep->title = _('PACKING SLIP');
                    $rep->filename = "Packing_slip" . $myrow['reference'] . ".pdf";
                }
            }
            $rep->currency = $cur;
            $rep->Font();
            $rep->Info($params, $cols, null, $aligns);

            $contacts = get_branch_contacts($branch['branch_code'], 'delivery', $branch['debtor_no'], true);
            $rep->SetCommonData($myrow, $branch, $sales_order, '', ST_CUSTDELIVERY, $contacts);
            $rep->SetHeaderType('Header2');
            $rep->NewPage();

               $result = get_customer_trans_details(ST_CUSTDELIVERY, $i);
            $SubTotal = 0;
                        $TotalWeight = 0;
            while ($myrow2=db_fetch($result))
            {
                if ($myrow2["quantity"] == 0)
                    continue;

                $Net = round2(((1 - $myrow2["discount_percent"]) * $myrow2["unit_price"] * $myrow2["quantity"]),
                   user_price_dec());
                $SubTotal += $Net;
                $DisplayPrice = number_format2($myrow2["unit_price"],$dec);
                $DisplayQty = number_format2($myrow2["quantity"],get_qty_dec($myrow2['stock_id']));
                $DisplayNet = number_format2($Net,$dec);
                        $stock_item = get_item($myrow2['stock_id']);
                        $Weight = $stock_item['weight'] * $myrow2["unit_price"];
                        $DisplayWeight = number_format2($Weight,$dec);
                        $TotalWeight += $Weight;
                if ($myrow2["discount_percent"]==0)
                      $DisplayDiscount ="";
                else
                      $DisplayDiscount = number_format2($myrow2["discount_percent"]*100,user_percent_dec()) . "%";
                $rep->TextCol(0, 1,    $myrow2['stock_id'], -2);
                $oldrow = $rep->row;
                $rep->TextColLines(1, 2, $myrow2['StockDescription'], -2);
                $newrow = $rep->row;
                $rep->row = $oldrow;
                if ($Net != 0.0  || !is_service($myrow2['mb_flag']) || !$SysPrefs->no_zero_lines_amount())
                {
                    $rep->TextCol(2, 3,    $DisplayQty, -2);
                    $rep->TextCol(3, 4,    $myrow2['units'], -2);
                    if ($packing_slip == 0)
                    {
                        $rep->TextCol(4, 5,    $DisplayPrice, -2);
                        $rep->TextCol(5, 6,    $DisplayDiscount, -2);
                                                $rep->TextCol(6, 7,    $DisplayWeight, -2);
                        $rep->TextCol(7, 8,    $DisplayNet, -2);
                    }
                }
                $rep->row = $newrow;
                //$rep->NewLine(1);
                if ($rep->row < $rep->bottomMargin + (15 * $rep->lineHeight))
                    $rep->NewPage();
                               
            }

            $memo = get_comments_string(ST_CUSTDELIVERY, $i);
            if ($memo != "")
            {
                $rep->NewLine();
                $rep->TextColLines(1, 3, $memo, -2);
            }

               $DisplaySubTot = number_format2($SubTotal,$dec);

            $rep->row = $rep->bottomMargin + (15 * $rep->lineHeight);
            $doctype=ST_CUSTDELIVERY;
            if ($packing_slip == 0)
            {
                $rep->TextCol(3, 7, _("Sub-total"), -2);
                $rep->TextCol(7, 8,    $DisplaySubTot, -2);
                $rep->NewLine();
                if ($myrow['ov_freight'] != 0.0)
                {
                    $DisplayFreight = number_format2($myrow["ov_freight"],$dec);
                    $rep->TextCol(3, 7, _("Shipping"), -2);
                    $rep->TextCol(7, 8,    $DisplayFreight, -2);
                    $rep->NewLine();
                }   
                $tax_items = get_trans_tax_details(ST_CUSTDELIVERY, $i);
                $first = true;
                while ($tax_item = db_fetch($tax_items))
                {
                    if ($tax_item['amount'] == 0)
                        continue;
                    $DisplayTax = number_format2($tax_item['amount'], $dec);

                    if ($SysPrefs->suppress_tax_rates() == 1)
                           $tax_type_name = $tax_item['tax_type_name'];
                       else
                           $tax_type_name = $tax_item['tax_type_name']." (".$tax_item['rate']."%) ";

                    if ($myrow['tax_included'])
                    {
                           if ($SysPrefs->alternative_tax_include_on_docs() == 1)
                        {
                            if ($first)
                            {
                                $rep->TextCol(3, 7, _("Total Tax Excluded"), -2);
                                $rep->TextCol(7, 8,    number_format2($tax_item['net_amount'], $dec), -2);
                                $rep->NewLine();
                            }
                            $rep->TextCol(3, 7, $tax_type_name, -2);
                            $rep->TextCol(7, 8,    $DisplayTax, -2);
                            $first = false;
                        }
                        else
                            $rep->TextCol(3, 8, _("Included") . " " . $tax_type_name . _("Amount") . ": " . $DisplayTax, -2);
                    }
                    else
                    {
                        $rep->TextCol(3, 7, $tax_type_name, -2);
                        $rep->TextCol(7, 8,    $DisplayTax, -2);
                    }
                    $rep->NewLine();
                }
                $rep->NewLine();
                $DisplayTotal = number_format2($myrow["ov_freight"] +$myrow["ov_freight_tax"] + $myrow["ov_gst"] +
                    $myrow["ov_amount"],$dec);
                $rep->Font('bold');
                $rep->TextCol(3, 7, _("TOTAL DELIVERY INCL. VAT"), - 2);
                $rep->TextCol(7, 8,    $DisplayTotal, -2);
                $words = price_in_words($myrow['Total'], ST_CUSTDELIVERY);
                if ($words != "")
                {
                    $rep->NewLine(1);
                    $rep->TextCol(1, 8, $myrow['curr_code'] . ": " . $words, - 2);
                }   
                $rep->Font();
            } else {
                            $rep->TextCol(3, 6, _("Total Weight"), -2);
                $rep->TextCol(6, 7, number_format2($TotalWeight,$dec), -2);
                $rep->NewLine();
                        }   
            if ($email == 1)
            {
                $rep->End($email);
            }
    }
    if ($email == 0)
        $rep->End();
}

and Finally doctext.inc

case ST_CUSTDELIVERY:
            $this->title = ($this->params['packing_slip'] ? _("PACKING SLIP") : _("DELIVERY NOTE"));
                    $this->headers = array(_("Item Code"), _("Item Description"), _("Quantity"),
        _("Unit"), _("Price"), _("Discount %"), _("Weight"), _("Total"));

...

@joe
Thank you very much for the inclusion. I was on leave for a while and i was unable to follow through.

It is perfect as it is and have nothing more to add.

Thank you.

42

(10 replies, posted in FA Modifications)

I agree with you. How can we ensure this is integrated in FA..

Using FA for a distribution company, we needed the packing slip to show the total weight of the items. This is to ensure that we don't overload the vehicles. I modified add items form to include weight per unit which is later user to calculate the total weight on the packing slip.

I was wondering if this modification will be of use to other people.

I have added an column under Inventory Adjustments to show the QTY at hand for that item. This will make it easy to calculate the adjustment qty when doing stock adjustments after a stock take.

This is a small modification and would love to contribute to the project if its helpful.

This is helpful but only to some point. In a situation where the government grants tax exemptions to specific items but your invoice contains other taxed items, here, your cannot select multiple branches for the invoice. Also where a customer has actual branches, you will have to duplicate the branches so as to tax differently. Creating duplicate branches also seems to be more of a hack. I would suggest we have a drop down to select Item Tax Type per line item in sales Invoice...

I still think making FA more flexible in that aspect is very desirable. It is also more user friendly.

This will come in handy especially on cases where you supply goods to customers who have been exempted for certain taxes and
your regular customers who you have to apply tax. So one Item might be taxed differently from customer to customer. Also, the government occasionally grants tax exemptions for certain goods for a specified period.

Currently you have to edit an item to change the tax type and later revert it when you are done creating the invoice. It is a desirable feature to be able to change the tax type on fly but still having the default tax selected.

I want to make modifications to make it possible to select Item Tax Type on fly on FA Sales Quote, Order and Invoice.
When successful, i will contribute the code to the main project. What is your suggestions.

Kenn.

Thank you for this inclusion...

Thanks to your direction, i was able to note that all inputs that require ajax sync have a previous value attached to them. e.g. the date inputs have  "_last_val" attribute. I will use this like in the solution above.

I would like to follow this to the end and make sure a proper solution is found.
After I go on this journey successfully, what are the steps if i needed such and more fixes incorporated in the main project?

Upon much thought and investigation, i fount that FA sets "_last" attribute to be the last selected Index of a select element. We can utilize this attribute on the ajax timeout function to reverse the element to the previous selectedIndex and also alert the user of this. See below:

 if(retry){
                    JsHttpRequest._request(trigger, form, tout, retry-1);
           } else {
                    var name = submitObj.getAttribute('name');
                    var SelectElement = document.getElementsByName(name.substring(1,name.length - 7))[0];
                    SelectElement.selectedIndex = SelectElement.getAttribute('_last');
                      alert("Server did not respond successfully.Selected value was not changed");
           }

After doing this, we will need to make sure that the "_last" attribute is only changed after completion of a request and not before the request. Also, only when "select_on_submit" is true. To do this, we change  "e.onchange" option of "_set_combo_select(e)" function in "inserts.js" as follows:

e.onchange = function() {
            var s = this;
                        var sname = '_'+s.name+'_update';
                        var update = document.getElementsByName(sname)[0];
                        if(!update) {
                            this.setAttribute('_last', this.selectedIndex);
                        }
            
            if(string_contains(s.className, 'combo') || string_contains(this.className, 'combo3'))
                _update_box(s);
            if(s.selectedIndex>=0) {
                 if(update) {
                        JsHttpRequest.request(update);
                }
            }
            return true;
        }

Lastly, change "_last" attribute if there is a successful response. In "utils.js" :

 if (result) {
if (trigger.tagName=='SELECT') {
    var name = submitObj.getAttribute('name');
    var SelectElement = document.getElementsByName(name.substring(1,name.length - 7)[0]);
    SelectElement.setAttribute('_last', SelectElement.selectedIndex);
}...

Please let me know if this approach is acceptable and helpful.
Thank you.