New tax system implementation.
authorJanusz Dobrowolski <janusz@frontaccounting.eu>
Fri, 12 Jul 2019 18:34:27 +0000 (20:34 +0200)
committerJanusz Dobrowolski <janusz@frontaccounting.eu>
Mon, 19 Aug 2019 10:41:58 +0000 (12:41 +0200)
35 files changed:
gl/gl_journal.php
gl/includes/db/gl_db_trans.inc
includes/session.inc
includes/types.inc
includes/ui/items_cart.inc
includes/ui/ui_lists.inc
inventory/includes/db/items_category_db.inc
inventory/includes/db/items_db.inc
inventory/manage/item_categories.php
inventory/manage/items.php
purchasing/includes/db/grn_db.inc
purchasing/includes/db/invoice_db.inc
purchasing/includes/db/invoice_items_db.inc
purchasing/includes/db/po_db.inc
purchasing/includes/db/supp_trans_db.inc
purchasing/includes/po_class.inc
purchasing/includes/supp_trans_class.inc
purchasing/includes/ui/invoice_ui.inc
purchasing/view/view_grn.php
reporting/rep109.php
reporting/rep111.php
reporting/rep209.php
sales/includes/cart_class.inc
sales/includes/db/sales_credit_db.inc
sales/includes/db/sales_delivery_db.inc
sales/includes/db/sales_invoice_db.inc
sales/includes/db/sales_order_db.inc
sql/alter2.5.sql
sql/en_US-demo.sql
sql/en_US-new.sql
taxes/db/item_tax_types_db.inc
taxes/db/tax_groups_db.inc
taxes/tax_calc.inc
taxes/tax_groups.php
taxes/tax_rules.inc [new file with mode: 0644]

index e8af7d23dbea6d68e4ce5def5e0484a13c35284c..3b8a4b9da3dc6add1b9c7c8b81ef45fc6e0309e5 100644 (file)
@@ -138,9 +138,10 @@ function create_cart($type=0, $trans_no=0)
                while ($detail = db_fetch($taxes))
                {
                        $tax_id = $detail['tax_type_id'];
+                       $cart->vat_category = $tax_info['tax_category'] = $detail['vat_category'];
                        $tax_info['net_amount'][$tax_id] = $detail['net_amount']; // we can two records for the same tax_id, but in this case net_amount is the same
                        $tax_info['tax_date'] = sql2date($detail['tran_date']);
-                       //$tax_info['tax_group'] = $detail['tax_group_id'];
+                       $tax_info['tax_group'] = $detail['tax_group_id'];
 
                }
                if (isset($tax_info['net_amount']))     // guess exempt sales/purchase if any tax has been found
@@ -179,6 +180,8 @@ function create_cart($type=0, $trans_no=0)
 function update_tax_info()
 {
 
+       $_SESSION['journal_items']->vat_category = get_post('tax_category');
+
        if (!isset($_SESSION['journal_items']->tax_info) || list_updated('tax_category'))
                $_SESSION['journal_items']->tax_info = $_SESSION['journal_items']->collect_tax_info();
 
@@ -475,7 +478,8 @@ if (tab_closed('tabs', 'gl'))
 {
        $cart = &$_SESSION['journal_items'];
        $cart->tax_info['tax_date'] = $_POST['tax_date'];
-       //$cart->tax_info['tax_group'] = $_POST['tax_group'];
+       $cart->tax_info['tax_category'] = $_POST['tax_category'];
+       $cart->tax_info['tax_group'] = $_POST['tax_group'];
        $taxes = get_all_tax_types();
        while ($tax = db_fetch($taxes))
        {
@@ -489,6 +493,9 @@ if (tab_opened('tabs', 'gl'))
        $_POST['memo_'] = $_SESSION['journal_items']->memo_;
 } elseif (tab_opened('tabs', 'tax'))
 {
+       $_POST['tax_category'] = $_SESSION['journal_items']->vat_category;
+       $_SESSION['journal_items']->collect_tax_info();
+       $_POST['tax_group'] = $_SESSION['journal_items']->tax_info['tax_group'];
        set_focus('tax_date');
 }
 
@@ -549,7 +556,8 @@ tabbed_content_start('tabs', array(
                        br();
                        start_table(TABLESTYLE2, "width=40%");
                        date_row(_("VAT date:"), 'tax_date', '', "colspan='3'");
-                       //tax_groups_list_row(_("Tax group:"), 'tax_group');
+                       tax_groups_list_row(_("Tax group:"), 'tax_group');
+                       vat_category_list_row(_("VAT category:"), 'tax_category', null, true, true);
                        end_table(1);
 
                        start_table(TABLESTYLE2, "width=60%");
index 60f0093cfcd0d50304f48cc179f29fae90be4c55..55d1ccc427f6242363d15641b5a902202548b056 100644 (file)
@@ -429,7 +429,7 @@ function get_only_budget_trans_from_to($from_date, $to_date, $account, $dimensio
 //--------------------------------------------------------------------------------
 //     Stores journal/bank transaction tax details if applicable
 //
-function add_gl_tax_details($gl_code, $trans_type, $trans_no, $amount, $ex_rate, $date, $memo, $included=0, $net_amount = null)
+function add_gl_tax_details($gl_code, $trans_type, $trans_no, $amount, $ex_rate, $date, $memo, $included=0, $net_amount = null, $tax_group_id=null)
 {
        $tax_type = is_tax_account($gl_code);
        if(!$tax_type) return;  // $gl_code is not tax account
@@ -447,7 +447,7 @@ function add_gl_tax_details($gl_code, $trans_type, $trans_no, $amount, $ex_rate,
                }
        }
        add_trans_tax_details($trans_type, $trans_no, $tax['id'], $tax['rate'], $included, 
-               $amount, $net_amount, $ex_rate, $date, $memo, null);
+               $amount, $net_amount, $ex_rate, $date, $memo, null, $tax_group_id);
 
 }
 
@@ -457,7 +457,7 @@ function add_gl_tax_details($gl_code, $trans_type, $trans_no, $amount, $ex_rate,
 //     actual tax type rate.
 //
 function add_trans_tax_details($trans_type, $trans_no, $tax_id, $rate, $included,
-       $amount, $net_amount, $ex_rate, $tran_date, $memo, $reg_type=null)
+       $amount, $net_amount, $ex_rate, $tran_date, $memo, $reg_type=null, $tax_group_id=null, $vat_category=0)
 {
        // guess tax register if not set
        if (!isset($reg_type))
@@ -466,14 +466,18 @@ function add_trans_tax_details($trans_type, $trans_no, $tax_id, $rate, $included
 
        $sql = "INSERT INTO ".TB_PREF."trans_tax_details 
                (trans_type, trans_no, tran_date, tax_type_id, rate, ex_rate,
-                       included_in_price, net_amount, amount, memo, reg_type)
+                       included_in_price, net_amount, amount, memo, reg_type, tax_group_id, vat_category)
                VALUES (".db_escape($trans_type)."," . db_escape($trans_no).",'"
                                .date2sql($tran_date)."',"
                                .(int)($tax_id).","
                                .(float)($rate).","
                                .(float)($ex_rate).",".($included ? 1:0).","
                                .db_escape($net_amount).","
-                               .db_escape($amount).",".db_escape($memo).",".db_escape($reg_type, true).")";
+                               .db_escape($amount).","
+                               .db_escape($memo).","
+                               .db_escape($reg_type, true).","
+                               .db_escape($tax_group_id).","
+                               .db_escape($vat_category).")";
 
        db_query($sql, "Cannot save trans tax details");
 
@@ -524,27 +528,33 @@ function get_tax_summary($from, $to, $also_zero_purchases=false)
                                SUM(IF(trans_type=".ST_CUSTCREDIT.",-1,1)*
                                IF((reg_type=".TR_OUTPUT.")"
                                        ." || ((trans_type IN(".ST_SALESINVOICE.",".ST_CUSTCREDIT.") OR (trans_type=".ST_JOURNAL." AND reg_type=".TR_INPUT."))
-                                       ), net_amount*ex_rate,0)
+                                               AND (tgroup.tax_area=".TA_EU." OR (tgroup.tax_area=".TA_EXPORT." AND taxrec.vat_category=".VC_SERVICES.")
+                                               OR taxrec.vat_category=".VC_REVERSE."))"
+                                               .($also_zero_purchases ? '': " AND tax_type_id AND taxrec.rate")
+                                       ."), net_amount*ex_rate,0)
                                ) net_output,
 
                                SUM(IF(trans_type=".ST_CUSTCREDIT.",-1,1)*
                                IF((reg_type=".TR_OUTPUT.")"
                                        ." || ((trans_type IN(".ST_SALESINVOICE.",".ST_CUSTCREDIT.") OR (trans_type=".ST_JOURNAL." AND reg_type=".TR_INPUT."))
+                                               AND (tgroup.tax_area=".TA_EU." OR (tgroup.tax_area=".TA_EXPORT." AND taxrec.vat_category=".VC_SERVICES.")
+                                               OR taxrec.vat_category=".VC_REVERSE.")
                                        ), amount*ex_rate,0)) payable,
 
                                SUM(IF(trans_type IN(".ST_SUPPCREDIT."),-1,1)*
                                IF(reg_type=".TR_INPUT
                                        . ($also_zero_purchases ? '': " AND tax_type_id AND taxrec.rate")
-                                       .", net_amount*ex_rate, 0)) net_input,
+                                       ." AND vat_category!=".VC_NONDEDUCT.", net_amount*ex_rate, 0)) net_input,
 
                                SUM(IF(trans_type IN(".ST_SUPPCREDIT."),-1,1)*
                                IF(reg_type=".TR_INPUT
                                        . ($also_zero_purchases ? '': " AND tax_type_id AND taxrec.rate ") 
-                                       .", amount*ex_rate, 0)) collectible,
+                                       ." AND vat_category!=".VC_NONDEDUCT.", amount*ex_rate, 0)) collectible,
                                taxrec.rate,
                                ttype.id,
                                ttype.name
                FROM ".TB_PREF."trans_tax_details taxrec LEFT JOIN ".TB_PREF."tax_types ttype ON taxrec.tax_type_id=ttype.id
+                       LEFT JOIN ".TB_PREF."tax_groups tgroup ON taxrec.tax_group_id=tgroup.id
                WHERE taxrec.trans_type IN (".implode(',',
                        array(ST_SALESINVOICE, ST_CUSTCREDIT, ST_SUPPINVOICE, ST_SUPPCREDIT, ST_JOURNAL)).")
                        AND taxrec.tran_date >= '$fromdate'
index 06aceccea2cf5620fe3f65cb8a8ec51092b9a935..92bf5482a178a12232bb2774c7d3edae792db804 100644 (file)
@@ -460,6 +460,7 @@ include_once($path_to_root . "/includes/access_levels.inc");
 include_once($path_to_root . "/version.php");
 include_once($path_to_root . "/includes/main.inc");
 include_once($path_to_root . "/includes/app_entries.inc");
+include_once($path_to_root . "/taxes/tax_rules.inc");
 
 // Ajax communication object
 $Ajax = new Ajax();
@@ -472,6 +473,7 @@ $Editors = array();
 $Pagehelp = array();
 
 $Refs = new references();
+$TS = new tax_system();
 
 // intercept all output to destroy it in case of ajax call
 register_shutdown_function('end_flush');
index 959c1cd1360a94c58c361d5b46999babe37f42fe..212de03a2792c8ded33689791b3e4bf9292dbfe3 100644 (file)
@@ -246,6 +246,39 @@ define('BO_SUPPLIER', 3);
 
 include_once($path_to_root . '/includes/sysnames.inc');
 
+define('VC_NONE', -1); // for tax adjustemnts
+define('VC_OTHER', 0);
+define('VC_MEDIA', 1);
+define('VC_ASSETS', 2);
+define('VC_NONDEDUCT', 3);
+define('VC_SERVICES', 4);
+define('VC_REVERSE', 5);
+define('VC_PARTIAL', 6);
+
+$vat_categories = array(
+       VC_OTHER => _('Other goods'),
+       VC_MEDIA => _('Continous services'),
+       VC_ASSETS => _('Fixed assets'),
+       VC_NONDEDUCT => _('No VAT deductible'),
+       VC_SERVICES => _('Other services'),
+//     VC_PARTIAL => _('VAT partially deductible'),
+       VC_REVERSE => _('Reverse charge'),
+);
+
+//
+// Tax area categories
+//
+define('TA_DOMESTIC', 0);
+define('TA_EXPORT', 1);
+define('TA_EU', 2);
+
+$tax_area_types = array(
+       TA_DOMESTIC => _('Domestic'),
+       TA_EXPORT => _('Abroad'),
+       TA_EU => _('European Union'),
+);
+
+
 // tax register type
 define('TR_OUTPUT', 0); // sales
 define('TR_INPUT', 1); // purchase
index 44fb14946bd33b86b89a22a127d851691c2b21f7..416719e1f194ed15759482dae462da906744a7b8 100644 (file)
@@ -446,7 +446,7 @@ class items_cart
                                add_trans_tax_details($this->trans_type, $this->order_id,
                                        $tax_id, $this->tax_info['rate'][$tax_id], 0, $tax_nominal, $net, $this->rate,
                                        $this->tran_date,
-                                       $this->source_ref, $reg);
+                                       $this->source_ref, $reg, $this->tax_info['tax_group'], $this->tax_info['tax_category']);
                        }
                }
        }
index 55ef6777d5d004a40c4fbcfa8c82cadde687713c..07202791f53962d019cef82e16d9ba3eeb9b237f 100644 (file)
@@ -2568,6 +2568,26 @@ function payment_services($name)
                ));
 }
 
+function vat_category_list_row($label, $name, $selected_id=null, $enabled = true, $submit_on_change=false, $show_obsolete=false)
+{
+       global $vat_categories;
+
+       echo "<tr>";
+       if ($label != null)
+               echo "<td class='label'>$label</td>\n";
+       echo "<td>";
+        
+       $categories = $vat_categories;
+       if (!$show_obsolete)
+         unset($categories[VC_MEDIA]); // obsolete category removed to avoid mistake usage
+
+       echo array_selector($name, $selected_id, $categories,
+               array( 'select_submit'=> $submit_on_change, 
+                       'disabled' => !$enabled));
+       echo "</td></tr>\n";
+
+}
+
 function tax_algorithm_list($name, $value=null, $submit_on_change = false)
 {
        global $tax_algorithms;
@@ -2722,3 +2742,20 @@ function collations_list_row($label, $name, $selected_id=null)
                array('select_submit'=> false) );
        echo "</td></tr>\n";
 }
+
+function vat_areas_list_row($label, $name, $selected_id=null, $enabled=true)
+{
+       global $tax_area_types;
+
+       echo "<tr>";
+       if ($label != null)
+               echo "<td class='label'>$label</td>\n";
+       echo "<td>";
+
+       echo array_selector($name, $selected_id, $tax_area_types,
+               array(
+                       'select_submit'=> true,
+                       'disabled' => !$enabled) );
+       echo "</td></tr>\n";
+}
+
index 8b6d872aefaec0eb1bd5a9d442ae5c6fcccf3d38..48984bb665a9844c0724166efef9859bfc6071c2 100644 (file)
 ***********************************************************************/
 function add_item_category($description, $tax_type_id, $sales_account, 
        $cogs_account, $inventory_account, $adjustment_account, $wip_account, 
-       $units, $mb_flag, $dim1, $dim2, $no_sale, $no_purchase)
+       $units, $mb_flag, $dim1, $dim2, $no_sale, $no_purchase, $vat_category)
 {
        $sql = "INSERT INTO ".TB_PREF."stock_category (description, dflt_tax_type,
                        dflt_units, dflt_mb_flag, dflt_sales_act, dflt_cogs_act, 
                        dflt_inventory_act, dflt_adjustment_act, dflt_wip_act, 
-                       dflt_dim1, dflt_dim2, dflt_no_sale, dflt_no_purchase)
+                       dflt_dim1, dflt_dim2, dflt_no_sale, dflt_no_purchase, vat_category)
                VALUES ("
                .db_escape($description).","
                .db_escape($tax_type_id).","
@@ -30,14 +30,15 @@ function add_item_category($description, $tax_type_id, $sales_account,
                .db_escape($dim1).","
                .db_escape($dim2).","
                .db_escape($no_sale).","
-               .db_escape($no_purchase).")";
+               .db_escape($no_purchase).","
+               .db_escape($vat_category).")";
 
        db_query($sql,"an item category could not be added");
 }
 
 function update_item_category($id, $description, $tax_type_id, 
        $sales_account, $cogs_account, $inventory_account, $adjustment_account, 
-       $wip_account, $units, $mb_flag, $dim1, $dim2, $no_sale, $no_purchase)
+       $wip_account, $units, $mb_flag, $dim1, $dim2, $no_sale, $no_purchase, $vat_category)
 
 {
        $sql = "UPDATE ".TB_PREF."stock_category SET "
@@ -53,7 +54,8 @@ function update_item_category($id, $description, $tax_type_id,
                ."dflt_dim1 = ".db_escape($dim1).","
                ."dflt_dim2 = ".db_escape($dim2).","
                ."dflt_no_sale = ".db_escape($no_sale).","
-               ."dflt_no_purchase = ".db_escape($no_purchase)
+               ."dflt_no_purchase = ".db_escape($no_purchase).","
+               ."vat_category = ".db_escape($vat_category)
         ."WHERE category_id = ".db_escape($id);
 
        db_query($sql,"an item category could not be updated");
index 07fde0d172768d1872dc80aead3876a361edcb7c..cfbbbdbe579bd0f68097906ecead76ce54957c08 100644 (file)
@@ -14,7 +14,7 @@ function update_item($stock_id, $description, $long_description, $category_id,
        $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)
+       $depreciation_start=null, $fa_class_id=null, $vat_category='')
 {
        $sql = "UPDATE ".TB_PREF."stock_master SET long_description=".db_escape($long_description).",
                description=".db_escape($description).",
@@ -41,6 +41,9 @@ function update_item($stock_id, $description, $long_description, $category_id,
        if ($mb_flag != '')
                $sql .= ", mb_flag=".db_escape($mb_flag);
 
+       if ($vat_category != '')
+               $sql .= ", vat_category=".db_escape($vat_category);
+
        if (isset($depreciation_start)) {
                $sql .= ", depreciation_start='".date2sql($depreciation_start)."'"
                        .", depreciation_date='".date2sql($depreciation_start)."'";
@@ -58,11 +61,11 @@ function add_item($stock_id, $description, $long_description, $category_id,
        $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)
+       $fa_class_id=null, $vat_category=0)
 {
        $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,
+               adjustment_account, wip_account, dimension_id, dimension2_id, no_sale, no_purchase, editable, vat_category,
                depreciation_method, depreciation_rate, depreciation_factor"
                .(isset($depreciation_start) ? ", depreciation_start, depreciation_date, fa_class_id" : "")
                .") VALUES (".db_escape($stock_id).", ".db_escape($description).", ".db_escape($long_description).",
@@ -74,7 +77,7 @@ function add_item($stock_id, $description, $long_description, $category_id,
                .db_escape($dimension_id).", ".db_escape($dimension2_id).","
                .db_escape($no_sale).","
                .db_escape($no_purchase).","
-               .db_escape($editable).","
+               .db_escape($editable).",".db_escape($vat_category).","
                .db_escape($depreciation_method).",".db_escape($depreciation_rate).",".db_escape($depreciation_factor)
                .(isset($depreciation_start) ? ",'".date2sql($depreciation_start)."','".date2sql($depreciation_start)."',".db_escape($fa_class_id) : "")
                .")";
index 136c0a80eeee6f9907dd79a176c4ebcd31be3299..dfa0a786ff63bd4d6055a1d5845da730c3724890 100644 (file)
@@ -57,7 +57,7 @@ if ($Mode=='ADD_ITEM' || $Mode=='UPDATE_ITEM')
                                $_POST['cogs_account'], $_POST['inventory_account'], 
                                $_POST['adjustment_account'], $_POST['wip_account'],
                                $_POST['units'], $_POST['mb_flag'],     $_POST['dim1'], $_POST['dim2'],
-                               check_value('no_sale'), check_value('no_purchase'));
+                               check_value('no_sale'), check_value('no_purchase'), $_POST['vat_category']);
                        display_notification(_('Selected item category has been updated'));
        } 
        else 
@@ -67,7 +67,7 @@ if ($Mode=='ADD_ITEM' || $Mode=='UPDATE_ITEM')
                                $_POST['cogs_account'], $_POST['inventory_account'], 
                                $_POST['adjustment_account'], $_POST['wip_account'], 
                                $_POST['units'], $_POST['mb_flag'],     $_POST['dim1'], 
-                               $_POST['dim2'], check_value('no_sale'), check_value('no_purchase'));
+                               $_POST['dim2'], check_value('no_sale'), check_value('no_purchase'), $_POST['vat_category']);
                        display_notification(_('New item category has been added'));
        }
                $Mode = 'RESET';
@@ -118,7 +118,7 @@ if ($fixed_asset) {
                _("Asset Account"), _("Deprecation Cost Account"),
                _("Depreciation/Disposal Account"), "", "");
 } else {
-       $th = array(_("Name"), _("Tax type"), _("Units"), _("Type"), _("Sales Act"),
+       $th = array(_("Name"), _("Tax type"), _("Units"), _("VAT Category"), _("Type"), _("Sales Act"),
                _("Inventory Account"), _("COGS Account"), _("Adjustment Account"),
                _("Assembly Account"), "", "");
 }
@@ -135,8 +135,10 @@ while ($myrow = db_fetch($result))
        label_cell($myrow["description"]);
        label_cell($myrow["tax_name"]);
        label_cell($myrow["dflt_units"], "align=center");
-       if (!$fixed_asset)
+       if (!$fixed_asset) {
+               label_cell($vat_categories[$myrow["vat_category"]]);
                label_cell($stock_types[$myrow["dflt_mb_flag"]]);
+       }
        label_cell($myrow["dflt_sales_act"], "align=center");
        label_cell($myrow["dflt_inventory_act"], "align=center");
        label_cell($myrow["dflt_cogs_act"], "align=center");
@@ -177,6 +179,7 @@ if ($selected_id != -1)
                $_POST['dim2']  = $myrow["dflt_dim2"];
                $_POST['no_sale']  = $myrow["dflt_no_sale"];
                $_POST['no_purchase']  = $myrow["dflt_no_purchase"];
+               $_POST['vat_category']  = $myrow["vat_category"];
        } 
        hidden('selected_id', $selected_id);
        hidden('category_id');
@@ -218,6 +221,11 @@ else
 
 stock_units_list_row(_("Units of Measure:"), 'units', null);
 
+if (is_fixed_asset($_POST['mb_flag']))
+  hidden('vat_category', VC_ASSETS);
+else
+  vat_category_list_row(_("VAT category:"), 'vat_category',null, true, false, $selected_id!=-1);
+
 if (is_fixed_asset($_POST['mb_flag'])) 
        hidden('no_sale', 0);
 else
index dfaa25a0203834456bbf9db16e70ca2d35acd2e4..352a2fbf6e028b925b529108b1b08015a49ef711 100644 (file)
@@ -238,7 +238,7 @@ if (isset($_POST['addupdate']))
                                $_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', null),
-                               get_post('fa_class_id'));
+                               get_post('fa_class_id'), get_post('vat_category'));
 
                        update_record_status($_POST['NewStockID'], $_POST['inactive'],
                                'stock_master', 'stock_id');
@@ -259,7 +259,7 @@ if (isset($_POST['addupdate']))
                                $_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', null),
-                               get_post('fa_class_id'));
+                               get_post('fa_class_id'), get_post('vat_category'));
 
                        display_notification(_("A new item has been added."));
                        $_POST['stock_id'] = $_POST['NewStockID'] = 
@@ -314,6 +314,44 @@ if (isset($_POST['delete']) && strlen($_POST['delete']) > 1)
        }
 }
 
+function generateBarcode() {
+       $tmpBarcodeID = "";
+       $tmpCountTrys = 0;
+       while ($tmpBarcodeID == "")     {
+               srand ((double) microtime( )*1000000);
+               $random_1  = rand(1,9);
+               $random_2  = rand(0,9);
+               $random_3  = rand(0,9);
+               $random_4  = rand(0,9);
+               $random_5  = rand(0,9);
+               $random_6  = rand(0,9);
+               $random_7  = rand(0,9);
+               //$random_8  = rand(0,9);
+
+                       // http://stackoverflow.com/questions/1136642/ean-8-how-to-calculate-checksum-digit
+               $sum1 = $random_2 + $random_4 + $random_6; 
+               $sum2 = 3 * ($random_1  + $random_3  + $random_5  + $random_7 );
+               $checksum_value = $sum1 + $sum2;
+
+               $checksum_digit = 10 - ($checksum_value % 10);
+               if ($checksum_digit == 10) 
+                       $checksum_digit = 0;
+
+               $random_8  = $checksum_digit;
+
+               $tmpBarcodeID = $random_1 . $random_2 . $random_3 . $random_4 . $random_5 . $random_6 . $random_7 . $random_8;
+
+               // LETS CHECK TO SEE IF THIS NUMBER HAS EVER BEEN USED
+               $query = "SELECT stock_id FROM ".TB_PREF."stock_master WHERE stock_id='" . $tmpBarcodeID . "'";
+               $arr_stock = db_fetch(db_query($query));
+  
+               if (  !$arr_stock['stock_id'] ) {
+                       return $tmpBarcodeID;
+               }
+               $tmpBarcodeID = "";      
+       }
+}
+
 function item_settings(&$stock_id, $new_item) 
 {
        global $SysPrefs, $path_to_root, $page_nested, $depreciation_methods;
@@ -390,6 +428,8 @@ function item_settings(&$stock_id, $new_item)
 
        stock_units_list_row(_('Units of Measure:'), 'units', null, $fresh_item);
 
+    vat_category_list_row(_("VAT category:"), 'vat_category', null, $fresh_item, false, !$new_item);
+
        check_row(_("Editable description:"), 'editable');
 
        if (get_post('fixed_asset'))
@@ -641,44 +681,4 @@ if (get_post('fixed_asset'))
 
 end_form();
 
-//------------------------------------------------------------------------------------
-
 end_page();
-
-function generateBarcode() {
-       $tmpBarcodeID = "";
-       $tmpCountTrys = 0;
-       while ($tmpBarcodeID == "")     {
-               srand ((double) microtime( )*1000000);
-               $random_1  = rand(1,9);
-               $random_2  = rand(0,9);
-               $random_3  = rand(0,9);
-               $random_4  = rand(0,9);
-               $random_5  = rand(0,9);
-               $random_6  = rand(0,9);
-               $random_7  = rand(0,9);
-               //$random_8  = rand(0,9);
-
-                       // http://stackoverflow.com/questions/1136642/ean-8-how-to-calculate-checksum-digit
-               $sum1 = $random_2 + $random_4 + $random_6; 
-               $sum2 = 3 * ($random_1  + $random_3  + $random_5  + $random_7 );
-               $checksum_value = $sum1 + $sum2;
-
-               $checksum_digit = 10 - ($checksum_value % 10);
-               if ($checksum_digit == 10) 
-                       $checksum_digit = 0;
-
-               $random_8  = $checksum_digit;
-
-               $tmpBarcodeID = $random_1 . $random_2 . $random_3 . $random_4 . $random_5 . $random_6 . $random_7 . $random_8;
-
-               // LETS CHECK TO SEE IF THIS NUMBER HAS EVER BEEN USED
-               $query = "SELECT stock_id FROM ".TB_PREF."stock_master WHERE stock_id='" . $tmpBarcodeID . "'";
-               $arr_stock = db_fetch(db_query($query));
-  
-               if (  !$arr_stock['stock_id'] ) {
-                       return $tmpBarcodeID;
-               }
-               $tmpBarcodeID = "";      
-       }
-}
index e66c0910a4fe722d8b8abc21a08c70d6df7d0aaa..886e05fc5f5c2b3cb6082d740a06b0ab0c284172 100644 (file)
@@ -25,7 +25,8 @@ function update_average_material_cost($supplier, $stock_id, $price, $qty, $date,
                $currency = null;
 
        if ($supp['tax_included'])
-               $price = get_tax_free_price_for_item($stock_id, $price, $supp['tax_group_id'],
+               $price = get_tax_free_price_for_item(ST_SUPPINVOICE, // ?
+               $stock_id, $price, $supp['tax_group_id'],
                        $supp['tax_included']);
 
        if ($currency != null)
index 427b73dc0a564127fe32560a580bc4d219e41dd6..e2da684c2f7f167a110100b482b343d85c0629ff 100644 (file)
@@ -123,247 +123,243 @@ function get_diff_in_home_currency($supplier, $old_date, $date, $amount1, $amoun
 }
 //----------------------------------------------------------------------------------------
 
-function add_supp_invoice(&$supp_trans)
+function add_supp_invoice(&$supp_trans)  //, $already_voided=false, $allocs=null)
 {
-       global $Refs;
+       global $Refs, $systypes_array;
 
-       //$company_currency = get_company_currency();
        $trans_no = $supp_trans->trans_no;
        $trans_type = $supp_trans->trans_type;
        $supplier = get_supplier($supp_trans->supplier_id);
+       $dec = user_price_dec();
 
        begin_transaction();
        hook_db_prewrite($supp_trans, $trans_type);
-       $tax_total = 0;
-    $taxes = $supp_trans->get_taxes($supp_trans->tax_group_id);
-       if ($trans_no) {
+
+       if ($trans_no) {        // void old transaction
+//      if (!$already_voided) {        // transaction is already voided in case of direct invoice edition, which is needed for proper inventory value handling
                $allocs = get_payments_for($trans_no, $trans_type, $supp_trans->supplier_id); // save allocations
-               void_transaction($trans_type, $trans_no, Today(), _("Document reentered."));
+               void_supp_invoice($trans_type, $trans_no, true);
+               add_audit_trail($trans_type, $trans_no, Today(), _("Voided."));
+               add_voided_entry($trans_type, $trans_no, Today(), _("Document reentered."));
                $Refs->restore_last($trans_type, $trans_no);
+//      }
        } else
                $allocs = get_po_prepayments($supp_trans);
 
-       add_new_exchange_rate($supp_trans->currency, $supp_trans->tran_date, $supp_trans->ex_rate);
-
-    foreach ($taxes as $n => $taxitem)
-    {
-               $taxes[$n]['Value'] =  round2($taxitem['Value'], user_price_dec());
-       $tax_total += $taxes[$n]['Value'];
-    }
-
-       $invoice_items_total = $supp_trans->get_items_total();
-
-       $item_added_tax = 0;
-    if (!$supp_trans->tax_included)
-    {
-           $taxes = $supp_trans->get_taxes($supp_trans->tax_group_id);
-           foreach ($taxes as $n => $taxitem)
-                       $item_added_tax += isset($taxitem['Override']) ? $taxitem['Override'] : round2($taxitem['Value'], user_price_dec());
-       }
+       // register exchange rate when used first time on date
+       add_new_exchange_rate($supplier['curr_code'], $supp_trans->tran_date, $supp_trans->ex_rate);
 
-       if ($trans_type == ST_SUPPCREDIT)
-       {
-               // let's negate everything because it's a credit note
-               $invoice_items_total = -$invoice_items_total;
-               $tax_total = -$tax_total;
-               $supp_trans->ov_discount = -$supp_trans->ov_discount; // this isn't used at all...
-               $item_added_tax = -$item_added_tax;
-       }
-
-    $date_ = $supp_trans->tran_date;
-       $ex_rate = get_exchange_rate_from_home_currency($supp_trans->currency, $date_);
-
-    /*First insert the invoice into the supp_trans table*/
-       $invoice_id = write_supp_trans($trans_type, 0, $supp_trans->supplier_id, $date_, $supp_trans->due_date,
-               $supp_trans->reference, $supp_trans->supp_reference,
-               $invoice_items_total, $item_added_tax, $supp_trans->ov_discount, "", 0, $supp_trans->tax_included);
+       // store basic transaction info
+       $invoice_id = write_supp_trans($trans_type, 0, $supp_trans->supplier_id, $supp_trans->tran_date, $supp_trans->due_date,
+               $supp_trans->reference, $supp_trans->supp_reference, 0, 0, 0, "", $supp_trans->ex_rate, $supp_trans->tax_included);
 
        if ($trans_no)
                move_trans_attachments($trans_type, $trans_no, $invoice_id);
 
        $supp_trans->trans_no = $invoice_id;
+    $date_ = $supp_trans->tran_date;
 
-       $total = 0;
+       if (!$supp_trans->ex_rate)
+               $supp_trans->ex_rate = get_exchange_rate_from_home_currency($supplier['curr_code'], $date_);
 
-    /* Now the TAX account */
-    $taxes = $supp_trans->get_taxes($supp_trans->tax_group_id, 0, false);
-       $net_diff = 0;
+       // prepare cart for GL postings 
+       $gl_cart = new items_cart($trans_type, $invoice_id);
+       $gl_cart->tran_date = $date_;
+       $gl_cart->set_currency($supplier['curr_code'], $supp_trans->ex_rate);
 
-    foreach ($taxes as $taxitem)
-    {
-               if ($taxitem['Net'] != 0)
-               {
-                       if (isset($taxitem['Override'])) {
-                               if ($supp_trans->tax_included) { // if tax included, fix net amount to preserve overall line price
-                                       $net_diff += $taxitem['Override'] - $taxitem['Value'];
-                                       $taxitem['Net'] += $taxitem['Override'] - $taxitem['Value'];
-                               }
-                               $taxitem['Value'] = $taxitem['Override'];
-                       }
-                       add_trans_tax_details($trans_type, $invoice_id, 
-                               $taxitem['tax_type_id'], $taxitem['rate'], $supp_trans->tax_included, $taxitem['Value'],
-                               $taxitem['Net'], $ex_rate, $date_, $supp_trans->supp_reference, TR_INPUT);
+    $clearing_act = get_company_pref('grn_clearing_act');
 
-                       if (isset($taxitem['purchasing_gl_code']))
-                       {
-                               if ($trans_type == ST_SUPPCREDIT)
-                                       $taxitem['Value'] = -$taxitem['Value'];
-                               $total += add_gl_trans_supplier($trans_type, $invoice_id, $date_,
-                                       $taxitem['purchasing_gl_code'], 0, 0, $taxitem['Value'],
-                                       $supp_trans->supplier_id,
-                                       "A general ledger transaction for the tax amount could not be added");
-                       }
-               }
-    }
-    if ($trans_type == ST_SUPPCREDIT)
-               $net_diff = -$net_diff;
+       $supp_trans->split_line_values();       // generate amounts for GL postings
 
-    /* Now the AP account */
-    $total += add_gl_trans_supplier($trans_type, $invoice_id, $date_, $supplier["payable_account"], 0, 0,
-               -($invoice_items_total +  $item_added_tax + $supp_trans->ov_discount),
-               $supp_trans->supplier_id,
-               "The general ledger transaction for the control total could not be added");
+       $tax_total = 0;
+       $net_total = 0;
+       foreach($supp_trans->grn_items as $item)
+       {
+       if ($trans_type == ST_SUPPCREDIT)
+               {
+                       $item->this_quantity_inv = -$item->this_quantity_inv;
+                       set_grn_item_credited($item, $supp_trans->supplier_id, $invoice_id, $date_);
+               }
+               $item_gl = $item->gl_amounts;
 
-       $to_allocate = ($invoice_items_total + $item_added_tax + $supp_trans->ov_discount);
+               $stock_gl_code = get_stock_gl_code($item->item_code);
+               $dimension = !empty($supp_trans->dimension) ? $supp_trans->dimension :
+                       ($supplier['dimension_id'] ? $supplier['dimension_id'] : $stock_gl_code['dimension_id']);
+               $dimension2 = !empty($supp_trans->dimension2) ? $supp_trans->dimension2 :
+                       ($supplier['dimension2_id'] ? $supplier['dimension2_id'] : $stock_gl_code['dimension2_id']);
 
-    foreach ($supp_trans->gl_codes as $entered_gl_code)
-    {
-           /*GL Items are straight forward - just do the debit postings to the GL accounts specified -
-           the credit is to creditors control act  done later for the total invoice value + tax*/
+               $line_tax = 0;
+               foreach($item_gl as $index => $value)
+               {
+                        if (is_numeric($index)) // taxes
+                        {
+                               if ($value['Deductible'])
+                               {       // GL: VAT deductible
+                               $gl_cart->add_gl_item($value['purchasing_gl_code'], 0, 0, $value['Deductible']+$value['Adjust'],  //FIXME: 'Adjust' ?
+                                               $value['Payable'] ? sprintf(_('Internal invoice %s input tax'), $supp_trans->reference) : '');
+                                       // GL: VAT adjustment due to vat factor
+                                       if ($value['Adjust'])
+                                               $gl_cart->add_gl_item(get_company_pref('tax_adjustments_act'), 0, 0, -$value['Adjust']);
+                               }
+                               if ($value['Payable']) // reverse charge/intracommunity aquisition
+                               {
+                                       $gl_cart->add_gl_item($value['sales_gl_code'], 0, 0, -$value['Payable'],
+                                       sprintf(_('Internal invoice %s input tax'), $supp_trans->reference));
+                               }
+                               // GL: AP account (vat part)
+                               if ($value['Value'])
+                               {
+                                       $gl_cart->add_gl_item($supplier["payable_account"], 0, 0, -$value['Value'] , '', '', $supp_trans->supplier_id);
+                               }
+                               if (($item->vat_category == VC_REVERSE) && $supplier['tax_area'] == TA_EU)
+                                       $vat_category = VC_OTHER;
+                               else
+                                       $vat_category = $item->vat_category;
 
-               if ($trans_type == ST_SUPPCREDIT)
-                       $entered_gl_code->amount = -$entered_gl_code->amount;
+                               add_trans_tax_details($trans_type, $invoice_id, 
+                                       $value['tax_type_id'], $value['rate'], $supp_trans->tax_included, $value['Value'],
+                                       $item_gl['Net'], $supp_trans->ex_rate, $date_, $supp_trans->supp_reference, TR_INPUT, $supp_trans->tax_group_id, $vat_category);
 
-               $memo_ = $entered_gl_code->memo_;
-               $total += add_gl_trans_supplier($trans_type, $invoice_id, $date_, $entered_gl_code->gl_code,
-                       $entered_gl_code->gl_dim, $entered_gl_code->gl_dim2, $entered_gl_code->amount, $supp_trans->supplier_id, "", 0, $memo_);
+                                       // $value['Deductible'], ???
 
-               add_supp_invoice_gl_item($trans_type, $invoice_id, $entered_gl_code->gl_code, $entered_gl_code->amount, $memo_,
-                       $entered_gl_code->gl_dim, $entered_gl_code->gl_dim2);
+                               $tax_total += $value['Value'];
 
-               // store tax details if the gl account is a tax account
-               if ($trans_type == ST_SUPPCREDIT)
-                       $entered_gl_code->amount = -$entered_gl_code->amount;
-               add_gl_tax_details($entered_gl_code->gl_code, 
-                       $trans_type, $invoice_id, $entered_gl_code->amount,
-                       $ex_rate, $date_, $supp_trans->supp_reference, $supp_trans->tax_included);
-    }
+                               $line_tax += $value['Payable'] ? $value['Payable'] : $value['Value'];
+                       }
+               }
+               // GL: AP account (net)
+               $gl_cart->add_gl_item($supplier["payable_account"], 0, 0, -$item_gl['Net'], '', '', $supp_trans->supplier_id);
+               $net_total += $item_gl['Net'];
 
-    $clearing_act = get_company_pref('grn_clearing_act');
-    foreach ($supp_trans->grn_items as $line_no => $entered_grn)
-    {
+               // cost line value
+               $taxfree_line =  $item_gl['Cost'];
 
-       if ($trans_type == ST_SUPPCREDIT)
-       {
-                       $entered_grn->this_quantity_inv = -$entered_grn->this_quantity_inv;
-                       set_grn_item_credited($entered_grn, $supp_trans->supplier_id, $invoice_id, $date_);
-       }
+               $old = update_supp_received_items_for_invoice($item->id, $item->po_detail_item,
+                       $item->this_quantity_inv, $item->chg_price);
 
-               // For tax included pricelist the net price is calculated down from tax_included price.
-               // To avoid rounding errors we have to operate on line value instead of price
-               // Additionally we have to take into account differences in net value
-               // due to variations in tax calculations on supplier side. More over there is no direct relation between
-               // taxes and sales accounts, so we add net_diff just to first posted net value. This is _ugly_hack_
-               // which save us from rewriting whole routine, and works right only for small tax differences.
+               // Since the standard cost is always calculated on basis of the po unit_price,
+               // this is also the price that should be the base of calculating the price diff.
+               // In cases where there is two different po invoices on the same delivery with different unit prices this will not work either
+               $old_price = $old[2];
 
-               $taxfree_line = get_tax_free_price_for_item($entered_grn->item_code, $entered_grn->this_quantity_inv * $entered_grn->chg_price, 
-                         $supp_trans->tax_group_id, $supp_trans->tax_included) - $net_diff; $net_diff = 0;
+               /*
+                       If statement is removed. Should always check for deliveries nomatter if there has been a price change. 
+               */
+               $old_date = sql2date($old[1]);
 
-               $line_tax = get_full_price_for_item($entered_grn->item_code,
-                       $entered_grn->this_quantity_inv * $entered_grn->chg_price, 0, $supp_trans->tax_included) - $taxfree_line;
+               if (!is_inventory_item($item->item_code))
+               {
+                       $gl_cart->add_gl_item($supplier["purchase_account"] ? $supplier["purchase_account"] : $stock_gl_code["cogs_account"], $dimension, $dimension2,  $taxfree_line);
 
-               $stock_gl_code = get_stock_gl_code($entered_grn->item_code);
+               } else {
 
-               $dim = !empty($supp_trans->dimension) ? $supp_trans->dimension :
-                       ($supplier['dimension_id'] ? $supplier['dimension_id'] : $stock_gl_code['dimension_id']);
-               $dim2 = !empty($supp_trans->dimension2) ? $supp_trans->dimension2 :
-                       ($supplier['dimension2_id'] ? $supplier['dimension2_id'] : $stock_gl_code['dimension2_id']);
-       if ($trans_type == ST_SUPPCREDIT)
-               {
-                       $iv_act = (is_inventory_item($entered_grn->item_code) ? $stock_gl_code["inventory_account"] : 
-                               ($supplier["purchase_account"] ? $supplier["purchase_account"] : $stock_gl_code["cogs_account"]));
-                       $total += add_gl_trans_supplier($trans_type, $invoice_id, $date_, $iv_act,
-                               $dim, $dim2, $taxfree_line, $supp_trans->supplier_id);
-               }
-       else
-       {
-               // -------------- if price changed since po received.
-                       $iv_act = is_inventory_item($entered_grn->item_code) ? ($clearing_act ? $clearing_act : $stock_gl_code["inventory_account"]) : 
-                               ($supplier["purchase_account"] ? $supplier["purchase_account"] : $stock_gl_code["cogs_account"]);
-               $old = update_supp_received_items_for_invoice($entered_grn->id, $entered_grn->po_detail_item,
-                       $entered_grn->this_quantity_inv, $entered_grn->chg_price);
-
-                       // Since the standard cost is always calculated on basis of the po unit_price,
-                       // this is also the price that should be the base of calculating the price diff.
-                       // In cases where there is two different po invoices on the same delivery with different unit prices this will not work either
-
-                       $old_price = $old[2];
-
-                       $old_date = sql2date($old[1]);
-                       if (!is_inventory_item($entered_grn->item_code))
-                               $total += add_gl_trans_supplier($trans_type, $invoice_id, $date_, $iv_act,
-                                       $dim, $dim2, $taxfree_line, $supp_trans->supplier_id);
-                       else
-                       {
-                               $ex_rate = get_exchange_rate_from_home_currency($supp_trans->currency, $old_date);
-                               $old_value = get_tax_free_price_for_item($entered_grn->item_code, $entered_grn->this_quantity_inv * $old_price, 
-                                         $supp_trans->tax_group_id, $supp_trans->tax_included);
+                       $val = split_item_price($item->item_code, $item->this_quantity_inv * $old_price, 
+                                 $supp_trans->tax_group_id, $supp_trans->tax_included);
 
-                               $currency = get_supplier_currency($supp_trans->supplier_id);
+                       $GRN_value = $val['Cost'];
 
-                               $total += add_gl_trans_supplier($trans_type, $invoice_id, $date_, $iv_act,
-                                       $dim, $dim2, $old_value, $supp_trans->supplier_id, "", $ex_rate);
-                               $diff = get_diff_in_home_currency($supp_trans->supplier_id, $old_date, $date_, $old_value, 
-                                       $taxfree_line);
+                       $diff = get_diff_in_home_currency($supp_trans->supplier_id, $old_date, $date_, $GRN_value,
+                               $taxfree_line);
 
-                               $mat_cost = update_average_material_cost(null, $entered_grn->item_code,
-                                       $diff/$entered_grn->this_quantity_inv, $entered_grn->this_quantity_inv, null, true);
+                       // update average cost with per item part of difference (between invoice value and value set on po/grn)
+                       update_average_material_cost(null, $item->item_code,
+                               $diff/$item->this_quantity_inv, $item->this_quantity_inv, null, true, "add_supp_invoice $trans_type:$invoice_id");
 
+                       if ($clearing_act)      // no postings on GRN, so post full net value
+                       {
                                //Add GL transaction for GRN Provision in case of difference
+                               // material was already posted due GRN; post differences in value
                                if ($diff != 0)
                                {
-                                       $total += add_gl_trans($trans_type, $invoice_id, $date_, $stock_gl_code["inventory_account"],
-                                               $dim, $dim2, 'GRN Provision', $diff, null, null, null,
-                                               "The general ledger transaction could not be added for the GRN of the inventory item");
 
+                                       $gl_cart->add_gl_item($stock_gl_code["inventory_account"],                      // cart is in supplier currency, so need to fix by ex_rate here
+                                               $dimension, $dimension2, $diff/$gl_cart->rate, _('GRN Provision')); // subject to rounding errors?
+                                       $gl_cart->add_gl_item($clearing_act,
+                                               $dimension, $dimension2, -$diff/$gl_cart->rate, _('GRN Provision'));
+
+                                       //Chaitanya
                                        //If QOH is 0 or negative then update_average_material_cost will be skipped
                                        //Thus difference in PO and Supp Invoice should be handled separately
-                                       $qoh = get_qoh_on_date($entered_grn->item_code);
+                                       $qoh = get_qoh_on_date($item->item_code);
                                        if ($qoh <= 0)
                                        {
                                                global $Refs;
-
+                                                       //Chaitanya : Post a journal entry
                                                $id = get_next_trans_no(ST_JOURNAL);
                                                $ref = $Refs->get_next(ST_JOURNAL, null, $date_);
                                                add_journal(ST_JOURNAL, $id, $diff, $date_, get_company_currency(), $ref);
-                                               $stock_id = $entered_grn->item_code;
+                                               $stock_id = $item->item_code;
                                                $stock_gl_code = get_stock_gl_code($stock_id);
                                                $memo = _("Supplier invoice adjustment for zero inventory of ").$stock_id." "._("Invoice")." ".$supp_trans->reference;
                                                //Reverse the inventory effect if $qoh <=0
                                                add_gl_trans_std_cost(ST_JOURNAL, $id, $date_, 
                                                        $stock_gl_code["inventory_account"],
-                                                       $dim, $dim2, $memo, -$diff);
+                                                       $dimension, $dimension2,
+                                                       $memo, -$diff);
                                                //GL Posting to inventory adjustment account
                                                add_gl_trans_std_cost(ST_JOURNAL, $id, $date_, 
                                                        $stock_gl_code["adjustment_account"],
-                                                       $dim, $dim2, $memo, $diff);
-
-                                               add_audit_trail(ST_JOURNAL, $id, $date_);
+                                                       $dimension, $dimension2,
+                                                       $memo, $diff);
+                                                       add_audit_trail(ST_JOURNAL, $id, $date_);
                                                add_comments(ST_JOURNAL, $id, $date_, $memo);
                                                $Refs->save(ST_JOURNAL, $id, $ref);
                                        }
                                }
+                               $gl_cart->add_gl_item($clearing_act, $dimension, $dimension2, $taxfree_line);
                        }
-                       add_or_update_purchase_data($supp_trans->supplier_id, $entered_grn->item_code, $entered_grn->chg_price); 
+                       else
+                               $gl_cart->add_gl_item($stock_gl_code["inventory_account"], $dimension, $dimension2,     $taxfree_line);
+
                }
-               update_purchase_value($entered_grn->item_code, $entered_grn->chg_price * $ex_rate);
-               add_supp_invoice_item($trans_type, $invoice_id, $entered_grn->item_code,
-                       $entered_grn->item_description, 0,      $entered_grn->chg_price, $line_tax/$entered_grn->this_quantity_inv,
-                       $entered_grn->this_quantity_inv, $entered_grn->id, $entered_grn->po_detail_item, "");
-    } /* end of GRN postings */
 
-       /*Post a balance post if $total != 0 */
-       add_gl_balance($trans_type, $invoice_id, $date_, -$total, PT_SUPPLIER, $supp_trans->supplier_id); // ??
+//        if (is_fa_item($item->item_code)) {
+//          add_actual_cost($item->order_price, $item->item_code);
+//        }
+
+               add_or_update_purchase_data($supp_trans->supplier_id, $item->item_code, $item->chg_price); 
+
+               add_supp_invoice_item($trans_type, $invoice_id, $item->item_code,
+                       $item->item_description, 0,     $item->chg_price, $line_tax/$item->this_quantity_inv,
+                       $item->this_quantity_inv, $item->id, $item->po_detail_item);
+    } // grn_items
+
+    /*GL Items are straight forward - just do the debit postings to the GL accounts specified -
+    the credit is to creditors control act */
+    foreach ($supp_trans->gl_codes as $entered_gl_code)
+    {
+               if ($trans_type == ST_SUPPCREDIT)
+                       $entered_gl_code->amount = -$entered_gl_code->amount;
+
+               $memo_ = $entered_gl_code->memo_;
+
+               $index = is_tax_account($entered_gl_code->gl_code);
+               if ($index !== false)
+               {
+                       $gl_cart->add_gl_item($entered_gl_code->gl_code, $entered_gl_code->gl_dim, $entered_gl_code->gl_dim2, $entered_gl_code->amount);
+                       // store tax details if the gl account is a tax account
+                       if ($trans_type == ST_SUPPCREDIT)
+                               $entered_gl_code->amount = -$entered_gl_code->amount;
+                       add_gl_tax_details($entered_gl_code->gl_code, 
+                               $trans_type, $invoice_id, $entered_gl_code->amount,
+                               $supp_trans->ex_rate, $date_, $supp_trans->supp_reference, $supp_trans->tax_included, null, $supp_trans->tax_group_id);
+
+                       $tax_total += $entered_gl_code->amount;
+               } else {
+                       $gl_cart->add_gl_item($entered_gl_code->gl_code, $entered_gl_code->gl_dim, $entered_gl_code->gl_dim2, $entered_gl_code->amount);
+                       $net_total += $entered_gl_code->amount;
+               }
+               $gl_cart->add_gl_item($supplier["payable_account"], 0, 0, -$entered_gl_code->amount, '', '', $supp_trans->supplier_id);
+
+               add_supp_invoice_gl_item($trans_type, $invoice_id, $entered_gl_code->gl_code, $entered_gl_code->amount, $memo_,
+                       $entered_gl_code->gl_dim, $entered_gl_code->gl_dim2);
+    }
+
+       $gl_cart->reduce_gl();  // minimize GL lines
+
+       $gl_cart->write_gl(false); // don't check balances here: we are working on two (maybe unbalanced) carts
+
+       update_supp_trans_sums($trans_type, $invoice_id, round($net_total,2), round($tax_total,2));
 
        add_comments($trans_type, $invoice_id, $date_, $supp_trans->Comments);
 
@@ -395,11 +391,10 @@ function add_supp_invoice(&$supp_trans)
                }
        }
 
-       reallocate_payments($invoice_id, ST_SUPPINVOICE, $date_, $to_allocate, $allocs, $supp_trans->supplier_id);
-       $supp_trans->trans_no = $invoice_id;
+       reallocate_payments($invoice_id, ST_SUPPINVOICE, $date_, $net_total+$tax_total, $allocs, $supp_trans->supplier_id);
+
        hook_db_postwrite($supp_trans, $supp_trans->trans_type);
     commit_transaction();
-
     return $invoice_id;
 }
 
index a430de515f921284c231715c9c9127e6c9a6ec7a..f090c6d049d47e49bcb8e79b09e83f756ec51fdb 100644 (file)
@@ -12,7 +12,7 @@
 //-------------------------------------------------------------------------------------------------------------
 
 function add_supp_invoice_item($supp_trans_type, $supp_trans_no, $stock_id, $description,
-       $gl_code, $unit_price, $unit_tax, $quantity, $grn_item_id, $po_detail_item_id, $memo_, $dim_id=0, $dim2_id=0)
+       $gl_code, $unit_price, $unit_tax, $quantity, $grn_item_id, $po_detail_item_id, $memo_="", $dim_id=0, $dim2_id=0)
 {
        $sql = "INSERT INTO ".TB_PREF."supp_invoice_items (supp_trans_type, supp_trans_no, stock_id, description, gl_code, unit_price, unit_tax, quantity,
                grn_item_id, po_detail_item_id, memo_, dimension_id, dimension2_id) ";
index a18083ce7a02b49230e9d2897256191564cb7b1f..eb26570f2cc4674c1652f6b40ff8db2e0b6ac806 100644 (file)
@@ -15,10 +15,11 @@ function get_supplier_details_to_order(&$order, $supplier_id)
 {
        $sql = "SELECT curr_code, supp_name, tax_group_id, supp.tax_included, dimension_id, dimension2_id,
                        supp.credit_limit - Sum((ov_amount + ov_gst + ov_discount)) as cur_credit,
-                               terms.terms, terms.days_before_due, terms.day_in_following_month
+                               terms.terms, terms.days_before_due, terms.day_in_following_month, tg.tax_area
                FROM ".TB_PREF."suppliers supp
                         LEFT JOIN ".TB_PREF."supp_trans trans ON supp.supplier_id = trans.supplier_id
                         LEFT JOIN ".TB_PREF."payment_terms terms ON supp.payment_terms=terms.terms_indicator
+                        LEFT JOIN ".TB_PREF."tax_groups tg ON supp.tax_group_id=tg.id
                WHERE supp.supplier_id = ".db_escape($supplier_id)."
                GROUP BY
                          supp.supp_name";
@@ -39,7 +40,7 @@ function get_supplier_details_to_order(&$order, $supplier_id)
        $_POST['dimension2'] = $myrow["dimension2_id"];
 
        $order->set_supplier($supplier_id, $myrow["supp_name"], $myrow["curr_code"], 
-               $myrow["tax_group_id"], $myrow["tax_included"]);
+               $myrow["tax_group_id"], $myrow["tax_included"], $myrow["tax_area"]);
 }
 
 //----------------------------------------------------------------------------------------
@@ -162,9 +163,10 @@ function update_po(&$po_obj)
 
 function read_po_header($order_no, &$order)
 {
-       $sql = "SELECT po.*, supplier.*, loc.location_name 
+       $sql = "SELECT po.*, tg.tax_area, supplier.*, loc.location_name 
                FROM ".TB_PREF."purch_orders po,"
-                       .TB_PREF."suppliers supplier,"
+                       .TB_PREF."suppliers supplier
+                       LEFT JOIN ".TB_PREF."tax_groups tg ON supplier.tax_group_id=tg.id,"
                        .TB_PREF."locations loc
                WHERE po.supplier_id = supplier.supplier_id
                AND loc.loc_code = into_stock_location
@@ -181,7 +183,7 @@ function read_po_header($order_no, &$order)
        $order->order_no = $order_no;
 
        $order->set_supplier($myrow["supplier_id"], $myrow["supp_name"], $myrow["curr_code"],
-               $myrow['tax_group_id'], $myrow["tax_included"]);
+               $myrow['tax_group_id'], $myrow["tax_included"], $myrow["tax_area"]);
 
                $order->credit = get_current_supp_credit($order->supplier_id);
 
index 33a2164f86fa634a8bdfdcdee7bce4ef2755ca1f..618ddeeb1f82f8e2fe2ae140916618bc26ca4c87 100644 (file)
@@ -47,6 +47,19 @@ function write_supp_trans($type, $trans_no, $supplier_id, $date_, $due_date, $re
 
        return $trans_no;
 }
+/*
+       Helper for final supplier trans header summaries update.
+*/
+function update_supp_trans_sums($trans_type, $trans_no, $amount, $tax, $discount=0)
+{
+       $sql = "UPDATE ".TB_PREF."supp_trans 
+               SET     ov_amount=".db_escape($amount).",
+                       ov_gst=".db_escape($tax).",
+                       ov_discount=".db_escape($discount)."
+               WHERE trans_no=".db_escape($trans_no)." AND type=".db_escape($trans_type);
+
+       return db_query($sql, "Cannot update supplier transaction summaries");
+}
 
 //-------------------------------------------------------------------------------------------------------------
 
index 22a9bc65b454d48a3a370fe25387f7ae79b14f05..0053b0b1578cc44cb16d118ee9043ea6baa985a5 100644 (file)
@@ -36,7 +36,6 @@ class purch_order
        var $lines_on_order = 0;
        var $credit;
        var $tax_group_id;
-       var $tax_group_array = null; // saves db queries
        var $terms;
        var $ex_rate;
        var $cash_account;
@@ -52,19 +51,19 @@ class purch_order
 
        function __construct()
        {
-               /*Constructor function initialises a new purchase order object */
                $this->line_items = array();
                $this->lines_on_order = $this->order_no = $this->supplier_id = 0;
+               $this->tax_group_id = find_domestic_tax_group(); // prevent tax errors until supplier is selected
        }
        
-       function set_supplier($supplier_id, $supplier_name, $curr_code, $tax_group_id, $tax_included)
+       function set_supplier($supplier_id, $supplier_name, $curr_code, $tax_group_id, $tax_included, $tax_area)
        {
                $this->supplier_id = $supplier_id;
                $this->supplier_name = $supplier_name;
                $this->curr_code = $curr_code;
                $this->tax_group_id = $tax_group_id;
                $this->tax_included = $tax_included;
-               $this->tax_group_array = get_tax_group_items_as_array($tax_group_id);
+               $this->tax_area = $tax_area;
        }
        
        function add_to_order($line_no, $stock_id, $qty, $item_descr, $price, $uom, $req_del_date, $qty_inv, $qty_recd, $qty_ordered=0, $grn_item_id=0)
@@ -154,8 +153,8 @@ class purch_order
                        $items[] = $ln_itm->stock_id;
                        $prices[] = round($ln_itm->price * $ln_itm->quantity,  user_price_dec());
                }
-               $taxes = get_tax_for_items($items, $prices, $shipping_cost,
-                 $this->tax_group_id, $this->tax_included,  $this->tax_group_array);
+               $taxes = get_tax_for_items($this->trans_type, $items, $prices, $shipping_cost,
+                 $this->tax_group_id, $this->tax_included);
 
        // Adjustment for swiss franken, we always have 5 rappen = 1/20 franken
            if ($this->curr_code == 'CHF') {
@@ -186,8 +185,8 @@ class purch_order
                }
 
                if (!$this->tax_included ) {
-                       $taxes = get_tax_for_items($items, $prices, 0, $this->tax_group_id,
-                       $this->tax_included,  $this->tax_group_array);
+                       $taxes = get_tax_for_items($this->trans_type, $items, $prices, 0, $this->tax_group_id,
+                       $this->tax_included);
 
                        foreach($taxes as $tax)
                                $total += round($tax['Value'], $dec);
@@ -195,6 +194,40 @@ class purch_order
                return $total;
        }
 
+       function split_line_values()
+       {
+               // split nominal line values
+               foreach($this->line_items as $line)
+                       $line->split_item_value($this);
+
+               // Exact tax values are currently entered as tax totals, so we need to move the differences back on line level.
+               // currently first item with given tax type will be fixed with the calculated difference
+               // FIXME: change UI moving tax edit to line level in line edit mode, then this workaround will be obsolete.
+               foreach($this->get_taxes() as $tax_id => $tax)
+               {
+                       if ($tax['Value'] != 0 && ($tax['Value'] != $tax['Override']))
+                       {
+                               foreach($this->line_items as $id => $line)
+                                       if ($line->gl_amounts[0]['tax_type_id'] == $tax_id) // assumed single tax rate on item, so always gl_mount[0] is valid
+                                       {
+                                               $diff = $tax['Override'] - $tax['Value'];
+                                               $this->line_items[$id]->gl_amounts[0]['Value'] += $diff;
+                                               if ($this->vat_category() != VC_NONDEDUCT)
+                                                       $this->line_items[$id]->gl_amounts[0]['Deductible'] += $diff;
+                                               else
+                                                       $this->line_items[$id]->gl_amounts['Cost'] += $diff;
+                                                // when supplier uses net prices the price is exact, so don't fix net, still record exact VAT.
+                                               if ($this->tax_included) 
+                                               {
+                                                       $this->line_items[$id]->gl_amounts['Net'] -= $diff;
+                                                       $this->line_items[$id]->gl_amounts['Cost'] -= $diff;
+                                               }
+                                               break;
+                                       }
+                       }
+               }
+       }
+
 } /* end of class defintion */
 
 class po_line_details 
@@ -216,6 +249,7 @@ class po_line_details
 
        var $unit_cost;
        var $descr_editable;
+       var $vat_category;
 
        function __construct($line_no, $stock_item, $item_descr, $qty, $prc, $uom, $req_del_date, 
                $qty_inv, $qty_recd, $qty_ordered=0, $grn_item_id=0)
@@ -241,6 +275,7 @@ class po_line_details
                $this->qty_inv = $qty_inv;
                $this->unit_cost =0;
                $this->grn_item_id = $grn_item_id;
+               $this->vat_category = $item_row["vat_category"];
                $this->qty_ordered = $qty_ordered;
        }
        
@@ -249,8 +284,20 @@ class po_line_details
        //
        function taxfree_charge_value($po)
        {
-               return get_tax_free_price_for_item($this->stock_id, $this->quantity*$this->price,
-                 $po->tax_group_id, $po->tax_included, $po->tax_group_array);
+               return get_tax_free_price_for_item($po->trans_type, $this->stock_id, $this->quantity*$this->price,
+                 $po->tax_group_id, $po->tax_included);
+       }
+
+       /*
+               Splits item value to parts posted to GL.
+       */
+       function split_item_value($cart)
+       {
+        $vat_factor = 1;
+
+               return $this->gl_amounts = split_item_price($this->stock_id, $this->price*$this->quantity, $cart->tax_group_id, $cart->tax_included, 
+                       ST_SUPPINVOICE, $vat_factor, $cart->tran_date);
        }
+  
 }
 
index be9ec00d3760477bee42b9102f76ee2a4248f935..107890df0692debc201f6da032e2a596cfd614e9 100644 (file)
@@ -41,7 +41,6 @@ class supp_trans
        var $ov_amount;
        var $ov_discount;
        var $ov_gst;
-       var $gl_codes_counter=0;
        var $credit = 0;
        var $currency;
        var $tax_overrides = array();           // array of taxes manually inserted during sales invoice entry
@@ -88,9 +87,7 @@ class supp_trans
 
        function add_gl_codes_to_trans($gl_code, $gl_act_name, $gl_dim, $gl_dim2, $amount, $memo_)
        {
-               $this->gl_codes[$this->gl_codes_counter] = new gl_codes($this->gl_codes_counter, 
-                       $gl_code, $gl_act_name, $gl_dim, $gl_dim2, $amount, $memo_);
-               $this->gl_codes_counter++;
+               $this->gl_codes[] = new gl_codes($gl_code, $gl_act_name, $gl_dim, $gl_dim2, $amount, $memo_);
                unset($this->tax_overrides); // cancel tax overrides after cart change
                return 1;
        }
@@ -142,7 +139,7 @@ class supp_trans
 
         if ($tax_group_id == null)
                $tax_group_id = $this->tax_group_id;
-        $taxes = get_tax_for_items($items, $prices, $shipping_cost, $tax_group_id, 
+        $taxes = get_tax_for_items($this->trans_type, $items, $prices, $shipping_cost, $tax_group_id, 
                $this->tax_included);
 
                if (isset($this->tax_overrides))
@@ -209,6 +206,39 @@ class supp_trans
                }
                return $total;
        }
+
+       function split_line_values()
+       {
+               // split nominal line values
+               foreach($this->grn_items as $line)
+                       $line->split_item_value($this);
+
+               // Exact tax values are currently entered as tax totals, so we need to move the differences back on line level.
+               // currently first item with given tax type will be fixed with the calculated difference
+               // FIXME: change UI moving tax edit to line level in line edit mode, then this workaround will be obsolete.
+               foreach($this->get_taxes() as $tax_id => $tax)
+               {
+                       if ($tax['Value'] != 0 && isset($tax['Override']) && ($tax['Value'] != $tax['Override']))
+                       {
+                               foreach($this->grn_items as $id => $line)
+                                       if ($line->gl_amounts[0]['tax_type_id'] == $tax_id) // assumed single tax rate on item, so always gl_mount[0] is valid
+                                       {
+                                               $diff = $tax['Override'] - $tax['Value'];
+                                               $this->grn_items[$id]->gl_amounts[0]['Value'] += $diff;
+                                               if ($this->vat_category() != VC_NONDEDUCT)
+                                                       $this->grn_items[$id]->gl_amounts[0]['Deductible'] += $diff;
+                                               else
+                                                       $this->grn_items[$id]->gl_amounts['Cost'] += $diff;
+                                                // when supplier uses net prices the price is exact, so don't fix net, still record exact VAT.
+                                               if ($this->tax_included) {
+                                                       $this->grn_items[$id]->gl_amounts['Net'] -= $diff;
+                                                       $this->grn_items[$id]->gl_amounts['Cost'] -= $diff;
+                                               }
+                                               break;
+                                       }
+                       }
+               }
+       }
 } /* end of class defintion */
 
 class grn_item 
@@ -229,6 +259,8 @@ all the info to do the necessary entries without looking up ie additional querie
        var $std_cost_unit;
        var $gl_code;
        var $tax_included;
+       var $gl_amounts;        // splited line value (after call to split_line_value method
+       var $vat_category;
 
        function __construct($id, $po_detail_item, $item_code, $item_description, $qty_recd, 
                $prev_quantity_inv, $this_quantity_inv, $order_price, $chg_price,
@@ -247,26 +279,46 @@ all the info to do the necessary entries without looking up ie additional querie
                $this->std_cost_unit = $std_cost_unit;
                $this->gl_code = $gl_code;
                $this->tax_included = $tax_included;
+
+               $opts = get_item($item_code);
+               $this->vat_category = $opts['vat_category'];
        }
 
-       function full_charge_price($tax_group_id, $tax_group=null)
+       function full_charge_price($tax_group_id, $trans_type=ST_PURCHORDER)
        {
-               return get_full_price_for_item($this->item_code, 
-                 $this->chg_price, $tax_group_id, $this->tax_included, $tax_group);
+               return get_full_price_for_item($trans_type, $this->item_code, 
+                 $this->chg_price, $tax_group_id, $this->tax_included);
        }
 
-       function taxfree_charge_price($tax_group_id, $tax_group=null)
+       function taxfree_charge_price($tax_group_id, $trans_type=ST_PURCHORDER)
        {
-               return get_tax_free_price_for_item($this->item_code, $this->chg_price, 
+               return get_tax_free_price_for_item($trans_type, $this->item_code, $this->chg_price, 
+                 $tax_group_id, $this->tax_included);
+       }
+
+       function taxfree_value($tax_group_id, $trans_type=ST_PURCHORDER)
+       {
+               return get_tax_free_price_for_item($trans_type, $this->item_code, $this->this_quantity_inv * $this->chg_price, 
                  $tax_group_id, $this->tax_included, $tax_group);
        }
+
+       /*
+               Splits item value to parts posted to GL.
+       */
+       function split_item_value($cart)
+       {
+
+        $vat_factor = 1;
+
+               return $this->gl_amounts = split_item_price($this->item_code, $this->chg_price*$this->this_quantity_inv, $cart->tax_group_id, $cart->tax_included, 
+                       ST_SUPPINVOICE, $vat_factor, $cart->tran_date);
+       }
 }
 
 
 class gl_codes 
 {
 
-       var $Counter;
        var $gl_code;
        var $gl_act_name;
        var $gl_dim;
@@ -274,11 +326,8 @@ class gl_codes
        var $amount;
        var $memo_;
 
-       function __construct($Counter, $gl_code, $gl_act_name, $gl_dim, $gl_dim2, $amount, $memo_)
+       function __construct($gl_code, $gl_act_name, $gl_dim, $gl_dim2, $amount, $memo_)
        {
-
-       /* Constructor function to add a new gl_codes object with passed params */
-               $this->Counter = $Counter;
                $this->gl_code = $gl_code;
                $this->gl_act_name = $gl_act_name;
                $this->gl_dim = $gl_dim;
index 39a4d5ba835bb96e37d048b8c479156451415aa3..7dafc8a9f14c8710750aa9f774f67848caa18211 100644 (file)
@@ -292,7 +292,7 @@ function display_gl_items(&$supp_trans, $mode=0)
        if (count($supp_trans->gl_codes) > 0)
        {
 
-               foreach ($supp_trans->gl_codes as $entered_gl_code)
+               foreach ($supp_trans->gl_codes as $n => $entered_gl_code)
                {
 
                        alt_table_row_color($k);
@@ -313,9 +313,9 @@ function display_gl_items(&$supp_trans, $mode=0)
 
                        if ($mode == 1)
                        {
-                               delete_button_cell("Delete2" . $entered_gl_code->Counter, _("Delete"),
+                               delete_button_cell("Delete2" . $n, _("Delete"),
                                          _('Remove line from document'));
-                edit_button_cell("Edit" . $entered_gl_code->Counter, _("Edit"),
+                edit_button_cell("Edit" . $n, _("Edit"),
                       _('Edit line from document'));
                        }       
                        end_row();
index b5159b639ba4eaf6700d2b1eaa405785bf624a28..0efdc27311a37eed86e11ad640bc1845c4755c73 100644 (file)
@@ -51,7 +51,7 @@ foreach ($purchase_order->line_items as $stock_item)
        $line_total = $stock_item->quantity * $stock_item->price;
 
        // if overdue and outstanding quantities, then highlight as so
-       if (date1_greater_date2($purchase_order->orig_order_date, $stock_item->req_del_date))
+       if (date1_greater_date2($purchase_order->tran_date, $stock_item->req_del_date))
        {
        start_row("class='overduebg'");
        $overdue_items = true;
index ba6c86ce17d7fdee6c2996d43b0f6b3a121ddb31..e96af1d4c4022f2202e06d3bda2ccb28df1e1b0f 100644 (file)
@@ -168,7 +168,7 @@ function print_sales_orders()
                        $rep->NewLine();
                }
 
-               $tax_items = get_tax_for_items($items, $prices, $myrow["freight_cost"],
+               $tax_items = get_tax_for_items(ST_SALESINVOICE, $items, $prices, $myrow["freight_cost"],
                  $myrow['tax_group_id'], $myrow['tax_included'],  null);
                $first = true;
                foreach($tax_items as $tax_item)
index 1db74b7c8869b2f9b92fdf05de06a89376284179..23ccc7f1c0c5f8eed1a9186b3f019bca201529e6 100644 (file)
@@ -165,7 +165,7 @@ function print_sales_quotations()
                        $rep->NewLine();
                }
 
-               $tax_items = get_tax_for_items($items, $prices, $myrow["freight_cost"],
+               $tax_items = get_tax_for_items(ST_SALESORDER, $items, $prices, $myrow["freight_cost"],
                  $myrow['tax_group_id'], $myrow['tax_included'],  null);
                $first = true;
                foreach($tax_items as $tax_item)
index f6fabf31e16a728b2b6e344ba56987bf5c9bca39..8b4decbdba3f7528f30f3b1ed6fad285ef1d109b 100644 (file)
@@ -167,7 +167,7 @@ function print_po()
                $rep->TextCol(6, 7,     $DisplaySubTot, -2);
                $rep->NewLine();
 
-               $tax_items = get_tax_for_items($items, $prices, 0,
+               $tax_items = get_tax_for_items(ST_PURCHORDER, $items, $prices, 0,
                  $myrow['tax_group_id'], $myrow['tax_included'],  null, TCA_LINES);
                $first = true;
                foreach($tax_items as $tax_item)
index c6b6f353ef9479925d7af0e73e03880eef00df57..6a7b38533358be9f20c1b2e0e8ba412403232448 100644 (file)
@@ -517,7 +517,7 @@ class Cart
                                $ln_itm->line_price()* (1 - $ln_itm->discount_percent)),  user_price_dec());
                }
 
-               $taxes = get_tax_for_items($items, $prices, $shipping_cost,
+               $taxes = get_tax_for_items($this->trans_type, $items, $prices, $shipping_cost,
                        $this->tax_group_id, $this->tax_included,  $this->tax_group_array);
 
     // Adjustment for swiss franken, we always have 5 rappen = 1/20 franken
index 999624f067ab61696a85d852acb5a7d5c5713aa4..6c3a8f28b739eb6faef3ee2dd55c43dd824af65a 100644 (file)
@@ -108,10 +108,10 @@ function write_credit_note(&$credit_note, $write_off_acc)
                                        -$credit_line->qty_old));
                }
 
-               $line_taxfree_price = get_tax_free_price_for_item($credit_line->stock_id, $credit_line->price,
+               $line_taxfree_price = get_tax_free_price_for_item(ST_CUSTCREDIT, $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,
+               $line_tax = get_full_price_for_item(ST_CUSTCREDIT, $credit_line->stock_id, $credit_line->price,
                  0, $credit_note->tax_included, $credit_note->tax_group_array) - $line_taxfree_price;
 
                $credit_line->unit_cost = get_unit_cost($credit_line->stock_id); 
@@ -153,7 +153,7 @@ function write_credit_note(&$credit_note, $write_off_acc)
                        add_trans_tax_details(ST_CUSTCREDIT, $credit_no, $taxitem['tax_type_id'],
                                $taxitem['rate'], $credit_note->tax_included, $taxitem['Value'],
                                $taxitem['Net'], $ex_rate,
-                               $credit_note->document_date, $credit_note->reference, TR_OUTPUT);
+                               $credit_note->document_date, $credit_note->reference, TR_OUTPUT, $credit_note->tax_group_id);
 
                        $total += add_gl_trans_customer(ST_CUSTCREDIT, $credit_no, $credit_date, $taxitem['sales_gl_code'], 0, 0,
                                $taxitem['Value'], $credit_note->customer_id,
@@ -237,10 +237,10 @@ function add_gl_trans_credit_costs($order, $order_line, $credit_no, $date_,
        if ($order_line->line_price() != 0) {
 
                $line_taxfree_price =
-               get_tax_free_price_for_item($order_line->stock_id, $order_line->price,
+               get_tax_free_price_for_item(ST_CUSTCREDIT, $order_line->stock_id, $order_line->price,
                  0, $order->tax_included, $order->tax_group_array);
 
-               $line_tax = get_full_price_for_item($order_line->stock_id, $order_line->price,
+               $line_tax = get_full_price_for_item(ST_CUSTCREDIT, $order_line->stock_id, $order_line->price,
                  0, $order->tax_included, $order->tax_group_array) - $line_taxfree_price;
 
 
index e5ef67565b38407a09781308c96f9d9ddb185a4c..f1ae7a8acceeb5dfb0b78347776e4d1ada277e7a 100644 (file)
@@ -72,11 +72,11 @@ function write_sales_delivery(&$delivery,$bo_policy)
 
                $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,
+               $line_taxfree_price = get_tax_free_price_for_item(ST_CUSTDELIVERY, $delivery_line->stock_id,
+                       $delivery_line->price*$qty, $delivery->tax_group_id, $delivery->tax_included,
                        $delivery->tax_group_array);
 
-               $line_tax = get_full_price_for_item($delivery_line->stock_id,
+               $line_tax = get_full_price_for_item(ST_CUSTDELIVERY, $delivery_line->stock_id,
                                $delivery_line->price * $qty, 0, $delivery->tax_included, $delivery->tax_group_array) - $line_taxfree_price;
 
                $delivery_line->unit_cost = get_unit_cost($delivery_line->stock_id);
@@ -188,7 +188,7 @@ function write_sales_delivery(&$delivery,$bo_policy)
                                $ex_rate = get_exchange_rate_from_home_currency(get_customer_currency($delivery->customer_id), $delivery->document_date);
                    add_trans_tax_details(ST_CUSTDELIVERY, $delivery_no, $taxitem['tax_type_id'],
                                $taxitem['rate'], $delivery->tax_included, $taxitem['Value'],
-                               $taxitem['Net'], $ex_rate, $delivery->document_date, $delivery->reference, null);
+                               $taxitem['Net'], $ex_rate, $delivery->document_date, $delivery->reference, null, $delivery->tax_group_id);
                }
        }
 
index 901cdd0840d9279a2b2ad46cd4991ed339958a7e..376a6c84cb896664fb090194522d86567b256cdf 100644 (file)
@@ -104,11 +104,11 @@ function write_sales_invoice(&$invoice)
 
        foreach ($invoice->line_items as $line_no => $invoice_line) {
                $qty = $invoice_line->qty_dispatched;
-               $line_taxfree_price = get_tax_free_price_for_item($invoice_line->stock_id,
-                       $invoice_line->price * $qty, 0, $invoice->tax_included,
+               $line_taxfree_price = get_tax_free_price_for_item(ST_SALESINVOICE, $invoice_line->stock_id,
+                       $invoice_line->price * $qty, $invoice->tax_group_id, $invoice->tax_included,
                        $invoice->tax_group_array);
 
-               $line_tax = get_full_price_for_item($invoice_line->stock_id,
+               $line_tax = get_full_price_for_item(ST_SALESINVOICE, $invoice_line->stock_id,
                        $invoice_line->price * $qty, 0, $invoice->tax_included,
                        $invoice->tax_group_array) - $line_taxfree_price;
 
@@ -175,7 +175,7 @@ function write_sales_invoice(&$invoice)
                        $ex_rate = get_exchange_rate_from_home_currency(get_customer_currency($invoice->customer_id), $date_);
                        add_trans_tax_details(ST_SALESINVOICE, $invoice_no, $taxitem['tax_type_id'],
                                $taxitem['rate'], $invoice->tax_included, $prepaid_factor*$taxitem['Value'],
-                                $taxitem['Net'], $ex_rate, $date_, $invoice->reference, TR_OUTPUT);
+                                $taxitem['Net'], $ex_rate, $date_, $invoice->reference, TR_OUTPUT, $invoice->tax_group_id);
                        if (isset($taxitem['sales_gl_code']) && !empty($taxitem['sales_gl_code']) && $taxitem['Value'] != 0)
                                $total += add_gl_trans_customer(ST_SALESINVOICE, $invoice_no, $date_, $taxitem['sales_gl_code'], 0, 0,
                                        (-$taxitem['Value'])*$prepaid_factor, $invoice->customer_id,
@@ -271,7 +271,6 @@ function void_sales_invoice($type, $type_no)
        // do this last because other voidings can depend on it - especially voiding
        // DO NOT MOVE THIS ABOVE VOIDING or we can end up with trans with alloc < 0
        void_customer_trans($type, $type_no);
-
        commit_transaction();
 }
 
index c918890b073f10970cf7ac737e6e7b891ea6895b..9a8272a0a353d9b65cc93abf2431b80f6dc6bf3d 100644 (file)
@@ -642,3 +642,14 @@ function last_sales_order_detail($order, $field)
         return $row[0];
 }
 
+function get_sales_vat_category($trans_no, $type=ST_SALESINVOICE)
+{
+       $sql = "SELECT vat_category
+                       FROM ".TB_PREF."debtor_trans_details line
+                       LEFT JOIN ".TB_PREF."stock_master stock ON line.stock_id=stock.stock_id
+               WHERE debtor_trans_type=".db_escape($type)." AND debtor_trans_no=".db_escape($trans_no);
+       $result = db_query($sql, 'cannot check invoice category');
+  $line = db_fetch($result);
+       return $line['vat_category'];
+}
+
index 215b18ed999b6b5d0f0f1676641c2e8e18514bdc..c92046e05c9ee3daaf5ce9159121a8342b5aa77e 100644 (file)
@@ -8,4 +8,19 @@ ALTER TABLE `0_purch_orders` CHANGE COLUMN `requisition_no` `supp_reference` tin
 # cleanups in work orders
 ALTER TABLE  `0_workorders` DROP INDEX `wo_ref`;
 ALTER TABLE  `0_workorders` ADD KEY `wo_ref` (`wo_ref`);
-ALTER TABLE  `0_workorders` DROP COLUMN `additional_costs`;
\ No newline at end of file
+ALTER TABLE  `0_workorders` DROP COLUMN `additional_costs`;
+
+# improvements in tax systems support
+ALTER TABLE `0_stock_category` ADD COLUMN  `vat_category` tinyint(1) NOT NULL DEFAULT '0' AFTER `dflt_no_purchase`;
+ALTER TABLE `0_stock_master` ADD COLUMN  `vat_category` tinyint(1) NOT NULL DEFAULT '0' AFTER `fa_class_id`;
+ALTER TABLE `0_trans_tax_details` ADD COLUMN  `vat_category` tinyint(1) NOT NULL DEFAULT '0' AFTER `reg_type`;
+ALTER TABLE `0_trans_tax_details` ADD COLUMN `tax_group_id` int(11) DEFAULT NULL AFTER `vat_category`;
+
+UPDATE `0_trans_tax_details` tax
+       LEFT JOIN `0_supp_trans` purch ON tax.trans_no=purch.trans_no AND tax.trans_type=purch.type
+       LEFT JOIN `0_suppliers` supp ON purch.supplier_id=supp.supplier_id
+       LEFT JOIN `0_debtor_trans` sales ON tax.trans_no=sales.trans_no AND tax.trans_type=sales.type
+       LEFT JOIN `0_cust_branch` cust ON sales.branch_code=cust.branch_code
+ SET tax.tax_group_id = IFNULL(supp.tax_group_id, cust.tax_group_id);
+
+ALTER TABLE `0_tax_groups` ADD COLUMN `tax_area` tinyint(1) NOT NULL DEFAULT '0' AFTER `name`;
index 20d7d2e720a5ec3893b360e827dc2fe4c490ee04..1d7184cbfef6dad61529af0f29733021cbd99c60 100644 (file)
@@ -1615,6 +1615,7 @@ CREATE TABLE `0_stock_category` (
   `inactive` tinyint(1) NOT NULL DEFAULT '0',
   `dflt_no_sale` tinyint(1) NOT NULL DEFAULT '0',
   `dflt_no_purchase` tinyint(1) NOT NULL DEFAULT '0',
+  `vat_category` tinyint(1) NOT NULL DEFAULT '0',
   PRIMARY KEY (`category_id`),
   UNIQUE KEY `description` (`description`)
 ) ENGINE=InnoDB AUTO_INCREMENT=5 ;
@@ -1622,10 +1623,10 @@ CREATE TABLE `0_stock_category` (
 -- Data of table `0_stock_category` --
 
 INSERT INTO `0_stock_category` VALUES
-('1', 'Components', '1', 'each', 'B', '4010', '5010', '1510', '5040', '1530', '0', '0', '0', '0', '0'),
-('2', 'Charges', '1', 'each', 'D', '4010', '5010', '1510', '5040', '1530', '0', '0', '0', '0', '0'),
-('3', 'Systems', '1', 'each', 'M', '4010', '5010', '1510', '5040', '1530', '0', '0', '0', '0', '0'),
-('4', 'Services', '1', 'hr', 'D', '4010', '5010', '1510', '5040', '1530', '0', '0', '0', '0', '0');
+('1', 'Components', '1', 'each', 'B', '4010', '5010', '1510', '5040', '1530', '0', '0', '0', '0', '0', '0'),
+('2', 'Charges', '1', 'each', 'D', '4010', '5010', '1510', '5040', '1530', '0', '0', '0', '0', '0', '0'),
+('3', 'Systems', '1', 'each', 'M', '4010', '5010', '1510', '5040', '1530', '0', '0', '0', '0', '0', '0'),
+('4', 'Services', '1', 'hr', 'D', '4010', '5010', '1510', '5040', '1530', '0', '0', '0', '0', '0', '0');
 
 -- Structure of table `0_stock_fa_class` --
 
@@ -1676,18 +1677,19 @@ CREATE TABLE `0_stock_master` (
   `depreciation_start` date NOT NULL DEFAULT '0000-00-00',
   `depreciation_date` date NOT NULL DEFAULT '0000-00-00',
   `fa_class_id` varchar(20) NOT NULL DEFAULT '',
+  `vat_category` tinyint(1) NOT NULL DEFAULT '0',
   PRIMARY KEY (`stock_id`)
 ) ENGINE=InnoDB ;
 
 -- Data of table `0_stock_master` --
 
 INSERT INTO `0_stock_master` VALUES
-('101', '1', '1', 'iPad Air 2 16GB', '', 'each', 'B', '4010', '5010', '1510', '5040', '1530', '0', '0', '0', '200', '0', '0', '0', '0', '0', '0', 'S', '0', '1', '0000-00-00', '0000-00-00', ''),
-('102', '1', '1', 'iPhone 6 64GB', '', 'each', 'B', '4010', '5010', '1510', '5040', '1530', '0', '0', '150', '150', '0', '0', '0', '0', '0', '0', 'S', '0', '1', '0000-00-00', '0000-00-00', ''),
-('103', '1', '1', 'iPhone Cover Case', '', 'each', 'B', '4010', '5010', '1510', '5040', '1530', '0', '0', '0', '10', '0', '0', '0', '0', '0', '0', 'S', '0', '1', '0000-00-00', '0000-00-00', ''),
-('201', '3', '1', 'AP Surf Set', '', 'each', 'M', '4010', '5010', '1510', '5040', '1530', '0', '0', '0', '360', '0', '0', '0', '0', '0', '0', 'S', '0', '1', '0000-00-00', '0000-00-00', ''),
-('202', '4', '1', 'Maintenance', '', 'hr', 'D', '4010', '5010', '1510', '5040', '1530', '0', '0', '0', '0', '0', '0', '0', '0', '0', '1', 'S', '0', '1', '0000-00-00', '0000-00-00', ''),
-('301', '4', '1', 'Support', '', 'hr', 'D', '4010', '5010', '1510', '5040', '1530', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', 'S', '0', '1', '0000-00-00', '0000-00-00', '');
+('101', '1', '1', 'iPad Air 2 16GB', '', 'each', 'B', '4010', '5010', '1510', '5040', '1530', '0', '0', '0', '200', '0', '0', '0', '0', '0', '0', 'S', '0', '1', '0000-00-00', '0000-00-00', '', '0'),
+('102', '1', '1', 'iPhone 6 64GB', '', 'each', 'B', '4010', '5010', '1510', '5040', '1530', '0', '0', '150', '150', '0', '0', '0', '0', '0', '0', 'S', '0', '1', '0000-00-00', '0000-00-00', '', '0'),
+('103', '1', '1', 'iPhone Cover Case', '', 'each', 'B', '4010', '5010', '1510', '5040', '1530', '0', '0', '0', '10', '0', '0', '0', '0', '0', '0', 'S', '0', '1', '0000-00-00', '0000-00-00', '', '0'),
+('201', '3', '1', 'AP Surf Set', '', 'each', 'M', '4010', '5010', '1510', '5040', '1530', '0', '0', '0', '360', '0', '0', '0', '0', '0', '0', 'S', '0', '1', '0000-00-00', '0000-00-00', '', '0'),
+('202', '4', '1', 'Maintenance', '', 'hr', 'D', '4010', '5010', '1510', '5040', '1530', '0', '0', '0', '0', '0', '0', '0', '0', '0', '1', 'S', '0', '1', '0000-00-00', '0000-00-00', '', '0'),
+('301', '4', '1', 'Support', '', 'hr', 'D', '4010', '5010', '1510', '5040', '1530', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', 'S', '0', '1', '0000-00-00', '0000-00-00', '', '0');
 
 -- Structure of table `0_stock_moves` --
 
@@ -1990,6 +1992,7 @@ DROP TABLE IF EXISTS `0_tax_groups`;
 CREATE TABLE `0_tax_groups` (
   `id` int(11) NOT NULL AUTO_INCREMENT,
   `name` varchar(60) NOT NULL DEFAULT '',
+  `tax_area` tinyint(1) NOT NULL DEFAULT '0',
   `inactive` tinyint(1) NOT NULL DEFAULT '0',
   PRIMARY KEY (`id`),
   UNIQUE KEY `name` (`name`)
@@ -1998,8 +2001,8 @@ CREATE TABLE `0_tax_groups` (
 -- Data of table `0_tax_groups` --
 
 INSERT INTO `0_tax_groups` VALUES
-('1', 'Tax', '0'),
-('2', 'Tax Exempt', '0');
+('1', 'Tax', '0', '0'),
+('2', 'Export/Import', '1', '0');
 
 -- Structure of table `0_tax_types` --
 
@@ -2037,6 +2040,8 @@ CREATE TABLE `0_trans_tax_details` (
   `amount` double NOT NULL DEFAULT '0',
   `memo` tinytext,
   `reg_type` tinyint(1) DEFAULT NULL,
+  `vat_category` int(11) NOT NULL DEFAULT '0',
+  `tax_group_id` tinyint(2) DEFAULT NULL,
   PRIMARY KEY (`id`),
   KEY `Type_and_Number` (`trans_type`,`trans_no`),
   KEY `tran_date` (`tran_date`)
@@ -2045,16 +2050,16 @@ CREATE TABLE `0_trans_tax_details` (
 -- Data of table `0_trans_tax_details` --
 
 INSERT INTO `0_trans_tax_details` VALUES
-('1', '13', '1', '2018-05-10', '1', '5', '1', '1', '5942.86', '297.14', 'auto', NULL),
-('2', '10', '1', '2018-05-10', '1', '5', '1', '1', '5942.86', '297.14', '001/2018', '0'),
-('3', '20', '1', '2018-05-05', '1', '5', '1', '0', '3000', '150', 'rr4', '1'),
-('4', '13', '2', '2018-05-07', '1', '5', '1', '1', '285.71', '14.29', 'auto', NULL),
-('5', '10', '2', '2018-05-07', '1', '5', '1', '1', '285.71', '14.29', '002/2018', '0'),
-('6', '13', '3', '2018-05-07', '0', '0', '1.123', '1', '267.14', '0', 'auto', NULL),
-('7', '10', '3', '2018-05-07', '0', '0', '1.123', '1', '267.14', '0', '003/2018', '0'),
-('8', '13', '5', '2019-01-21', '1', '5', '1', '1', '1190.48', '59.52', 'auto', NULL),
-('9', '10', '5', '2019-01-21', '1', '5', '1', '1', '1190.48', '59.52', '001/2019', '0'),
-('10', '20', '2', '2019-01-21', '1', '5', '1', '0', '900', '45', 'asd5', '1');
+('1', '13', '1', '2018-05-10', '1', '5', '1', '1', '5942.86', '297.14', 'auto', NULL, '0', '1'),
+('2', '10', '1', '2018-05-10', '1', '5', '1', '1', '5942.86', '297.14', '001/2018', '0', '0', '1'),
+('3', '20', '1', '2018-05-05', '1', '5', '1', '0', '3000', '150', 'rr4', '1', '0', '1'),
+('4', '13', '2', '2018-05-07', '1', '5', '1', '1', '285.71', '14.29', 'auto', NULL, '0', '1'),
+('5', '10', '2', '2018-05-07', '1', '5', '1', '1', '285.71', '14.29', '002/2018', '0', '0', '1'),
+('6', '13', '3', '2018-05-07', '0', '0', '1.123', '1', '267.14', '0', 'auto', NULL, '0', '1'),
+('7', '10', '3', '2018-05-07', '0', '0', '1.123', '1', '267.14', '0', '003/2018', '0', '0', '1'),
+('8', '13', '5', '2019-01-21', '1', '5', '1', '1', '1190.48', '59.52', 'auto', NULL, '0', '1'),
+('9', '10', '5', '2019-01-21', '1', '5', '1', '1', '1190.48', '59.52', '001/2019', '0', '0', '1'),
+('10', '20', '2', '2019-01-21', '1', '5', '1', '0', '900', '45', 'asd5', '1', '0', '1');
 
 -- Structure of table `0_useronline` --
 
index 3dbb1de7521198daea71a864fc6b46e68103ebb6..6e2fba329b75bec2c15bbf3736ff3dd3a71afe1f 100644 (file)
@@ -1327,6 +1327,7 @@ CREATE TABLE `0_stock_category` (
   `inactive` tinyint(1) NOT NULL DEFAULT '0',
   `dflt_no_sale` tinyint(1) NOT NULL DEFAULT '0',
   `dflt_no_purchase` tinyint(1) NOT NULL DEFAULT '0',
+  `vat_category` tinyint(1) NOT NULL DEFAULT '0',
   PRIMARY KEY (`category_id`),
   UNIQUE KEY `description` (`description`)
 ) ENGINE=InnoDB AUTO_INCREMENT=5 ;
@@ -1334,10 +1335,10 @@ CREATE TABLE `0_stock_category` (
 -- Data of table `0_stock_category` --
 
 INSERT INTO `0_stock_category` VALUES
-('1', 'Components', '1', 'each', 'B', '4010', '5010', '1510', '5040', '1530', '0', '0', '0', '0', '0'),
-('2', 'Charges', '1', 'each', 'D', '4010', '5010', '1510', '5040', '1530', '0', '0', '0', '0', '0'),
-('3', 'Systems', '1', 'each', 'M', '4010', '5010', '1510', '5040', '1530', '0', '0', '0', '0', '0'),
-('4', 'Services', '1', 'hr', 'D', '4010', '5010', '1510', '5040', '1530', '0', '0', '0', '0', '0');
+('1', 'Components', '1', 'each', 'B', '4010', '5010', '1510', '5040', '1530', '0', '0', '0', '0', '0', '0'),
+('2', 'Charges', '1', 'each', 'D', '4010', '5010', '1510', '5040', '1530', '0', '0', '0', '0', '0', '0'),
+('3', 'Systems', '1', 'each', 'M', '4010', '5010', '1510', '5040', '1530', '0', '0', '0', '0', '0', '0'),
+('4', 'Services', '1', 'hr', 'D', '4010', '5010', '1510', '5040', '1530', '0', '0', '0', '0', '0', '0');
 
 -- Structure of table `0_stock_fa_class` --
 
@@ -1388,6 +1389,7 @@ CREATE TABLE `0_stock_master` (
   `depreciation_start` date NOT NULL DEFAULT '0000-00-00',
   `depreciation_date` date NOT NULL DEFAULT '0000-00-00',
   `fa_class_id` varchar(20) NOT NULL DEFAULT '',
+  `vat_category` tinyint(1) NOT NULL DEFAULT '0',
   PRIMARY KEY (`stock_id`)
 ) ENGINE=InnoDB;
 
@@ -1663,6 +1665,7 @@ DROP TABLE IF EXISTS `0_tax_groups`;
 CREATE TABLE `0_tax_groups` (
   `id` int(11) NOT NULL AUTO_INCREMENT,
   `name` varchar(60) NOT NULL DEFAULT '',
+  `tax_area` tinyint(1) NOT NULL DEFAULT '0',
   `inactive` tinyint(1) NOT NULL DEFAULT '0',
   PRIMARY KEY (`id`),
   UNIQUE KEY `name` (`name`)
@@ -1671,8 +1674,8 @@ CREATE TABLE `0_tax_groups` (
 -- Data of table `0_tax_groups` --
 
 INSERT INTO `0_tax_groups` VALUES
-('1', 'Tax', '0'),
-('2', 'Tax Exempt', '0');
+('1', 'Tax', '0', '0'),
+('2', 'Export/Import', '1', '0');
 
 -- Structure of table `0_tax_types` --
 
@@ -1710,6 +1713,8 @@ CREATE TABLE `0_trans_tax_details` (
   `amount` double NOT NULL DEFAULT '0',
   `memo` tinytext,
   `reg_type` tinyint(1) DEFAULT NULL,
+  `vat_category` int(11) NOT NULL DEFAULT '0',
+  `tax_group_id` tinyint(2) DEFAULT NULL,
   PRIMARY KEY (`id`),
   KEY `Type_and_Number` (`trans_type`,`trans_no`),
   KEY `tran_date` (`tran_date`)
index 446a5dc7cb99ffe5839ccebe0f0929070206c1da..8b37ddf4d31d4db435b32821f3ef89baea5a15ef 100644 (file)
@@ -62,15 +62,16 @@ function get_item_tax_type($id)
 
 function get_item_tax_type_for_item($stock_id)
 {
-       $sql = "SELECT item_tax_type.*
-               FROM ".TB_PREF."item_tax_types item_tax_type,"
+       $sql = "SELECT tax_type.*, item.vat_category
+               FROM ".TB_PREF."item_tax_types tax_type,"
                        .TB_PREF."stock_master item
-               WHERE item.stock_id=".db_escape($stock_id)."
-               AND item_tax_type.id=item.tax_type_id";
-       
+               WHERE 
+                       item.stock_id=".db_escape($stock_id)."
+                       AND tax_type.id=item.tax_type_id";
+
        $result = db_query($sql, "could not get item tax type");
-       
-       return db_fetch($result);       
+
+       return db_fetch($result);
 }
 
 function delete_item_tax_type($id)
index fb40bf7de1dccd349c24fd98ddd727bd682d64ed..dc269fdc8018c4cc5ab012ec47ba8ebd891b9724 100644 (file)
@@ -9,25 +9,25 @@
     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
     See the License here <http://www.gnu.org/licenses/gpl-3.0.html>.
 ***********************************************************************/
-function add_tax_group($name, $taxes, $tax_shippings)
+function add_tax_group($name, $taxes, $tax_shippings, $tax_area)
 {
        begin_transaction();
 
-       $sql = "INSERT INTO ".TB_PREF."tax_groups (name) VALUES (".db_escape($name).")";
+       $sql = "INSERT INTO ".TB_PREF."tax_groups (name, tax_area) VALUES (".db_escape($name).",".db_escape($tax_area).")";
        db_query($sql, "could not add tax group");
        
        $id = db_insert_id();
        
-       add_tax_group_items($id, $taxes, $tax_shippings);       
+       add_tax_group_items($id, $taxes, $tax_shippings);
        
        commit_transaction();   
 }
 
-function update_tax_group($id, $name, $taxes, $tax_shippings)
+function update_tax_group($id, $name, $taxes, $tax_shippings, $tax_area)
 {
        begin_transaction();    
 
-    $sql = "UPDATE ".TB_PREF."tax_groups SET name=".db_escape($name)." WHERE id=".db_escape($id);
+    $sql = "UPDATE ".TB_PREF."tax_groups SET name=".db_escape($name).",tax_area=".db_escape($tax_area)." WHERE id=".db_escape($id);
        db_query($sql, "could not update tax group");
        
        delete_tax_group_items($id);
@@ -49,7 +49,7 @@ function get_tax_group($type_id)
        $sql = "SELECT * FROM ".TB_PREF."tax_groups WHERE id=".db_escape($type_id);
        
        $result = db_query($sql, "could not get tax group");
-       
+
        return db_fetch($result);
 }
 
@@ -125,6 +125,33 @@ function get_tax_group_items_as_array($id)
        return $ret_tax_array;
 }
 
+function get_tax_group_data($id)
+{
+       $data = get_tax_group($id);
+
+       $data['taxes'] = get_tax_group_items_as_array($id);
+
+       return $data;
+}
+
+/*
+       Heuristic function to find default domestic tax_group.
+*/
+function find_domestic_tax_group()
+{
+
+       $sql =  "SELECT id, cnt FROM 
+               (SELECT g.id, count(*) cnt 
+                       FROM ".TB_PREF."tax_group_items i
+                       LEFT JOIN ".TB_PREF."tax_groups g ON g.id=i.tax_group_id
+               WHERE tax_shipping=0 AND tax_area=0 AND !inactive
+               GROUP BY g.id) cnts
+               ORDER by cnt DESC";
+       $result = db_query($sql, "cannot get domestic group id");
+       $group = db_fetch($result);
+       return $group['id'];
+}
+
 function get_shipping_tax_as_array($id=null)
 {
        $ret_tax_array = array();
index 3ef685aef1cda24d34d4ea9c5f58d34c3cd0edb1..9f6664ceeb84fd77dc8b9e5133b018a8e5c798f4 100644 (file)
@@ -14,227 +14,288 @@ include_once($path_to_root . "/taxes/db/tax_types_db.inc");
 include_once($path_to_root . "/taxes/db/item_tax_types_db.inc");
 
 //---------------------------------------------------------------------------------
+// Returns basic fiscal parameters for transaction item which depend on stock type and customer/supplier tax area.
+//
+// vat_category => stock tax category 
+// tax_area => cust/supp tax area
+// taxes => all taxes applicable:
+//     tax_type_id1 => (tax_type_id, tax_type_name, sales_gl_code, purchasing_gl_code, rate)
+//     tax_type_id2 => ...
 
-// returns the price of a given item minus any included taxes
-// for item $stock_id with line price $price,
-// with applicable tax rates $tax_group_array or group id $tax_group
-
-function get_tax_free_price_for_item($stock_id, $price, $tax_group, $tax_included, $tax_group_array=null)
+function get_base_taxdata($stock_id, $group_id)
 {
-       // if price is zero, then can't be taxed !
-       if ($price == 0)
-               return 0;
-
-       if ($tax_included==0) return $price;
-       
-       // if array already read, then make a copy and use that
-       if ($tax_group_array)
-               $ret_tax_array = $tax_group_array;
-       else
-               $ret_tax_array = get_tax_group_items_as_array($tax_group);
-       
-       $tax_array = get_taxes_for_item($stock_id, $ret_tax_array);
-
-       // if no exemptions or taxgroup is empty, then no included/excluded taxes
-       if ($tax_array == null)
-               return $price;
-
-       // to avoid rounding errors we have to just subtract taxes from tax_included price.
-       $tax_multiplier = 0;
-       foreach ($tax_array as $taxitem) 
-       {
-               $tax_multiplier += $taxitem["rate"];
-       }
+       static $last_group = null, $group_data;
 
-       $tax = 0;
-       foreach ($tax_array as $taxitem)
-       {
-               $tax += round($price*$taxitem['rate']/(100+$tax_multiplier), user_price_dec());
+       if ($last_group != $group_id) { // cache group data for better performance
+               $last_group = $group_id;
+               $group_data = get_tax_group_data($group_id);
        }
-       return $price-$tax;
-}
-//
-//     Full price (incl. VAT) for item $stock_id with line price $price,
-//     with tax rates $tax_group_array or applicable group $tax_group
-//
-function get_full_price_for_item($stock_id, $price, $tax_group, $tax_included, $tax_group_array=null)
-{
-       // if price is zero, then can't be taxed !
-       if ($price == 0)
-               return 0;
-
-       if ($tax_included==1) return $price;
-
-       // if array already read, then make a copy and use that
-       if ($tax_group_array)
-               $ret_tax_array = $tax_group_array;
-       else
-               $ret_tax_array = get_tax_group_items_as_array($tax_group);
-       
-       //print_r($ret_tax_array);
-
-       $tax_array = get_taxes_for_item($stock_id, $ret_tax_array);
-       // if no exemptions or taxgroup is empty, then no included/excluded taxes
-       if ($tax_array == null)
-               return $price;
-       
-       $tax_multiplier = 0;
 
-       // loop for all items
+       $taxdata = array('tax_area' => $group_data['tax_area'], 'taxes' => null);
 
-       foreach ($tax_array as $taxitem) 
+       if ($stock_id===null) // shipping special case
        {
-                       $tax_multiplier += $taxitem["rate"];
+               $taxdata['vat_category'] = VC_SERVICES;
+               $taxdata['taxes'] = get_tax_group_items_as_array(null);
+               return $taxdata;
        }
-       
-       return round($price * (1 + ($tax_multiplier / 100)),  user_price_dec());
-}
 
-//---------------------------------------------------------------------------------
-// return an array of (tax_type_id, tax_type_name, sales_gl_code, purchasing_gl_code, rate)
+       $item_tax_type = get_item_tax_type_for_item($stock_id); // get item tax data
+       $taxdata['vat_category'] = $item_tax_type['vat_category'];
 
-function get_taxes_for_item($stock_id, $tax_group_items_array)
-{
-       $item_tax_type = get_item_tax_type_for_item($stock_id);
-       
-       // if the item is exempt from all taxes then return 0
+       // if the item is exempt from all taxes thats all
        if ($item_tax_type["exempt"])
-               return null;
-               
+               return $taxdata;
+
+       $taxdata['taxes'] = array();
+
        // get the exemptions for this item tax type
        $item_tax_type_exemptions_db = get_item_tax_type_exemptions($item_tax_type["id"]);
-       
+
        // read them all into an array to minimize db querying
        $item_tax_type_exemptions = array();
        while ($item_tax_type_exemp = db_fetch($item_tax_type_exemptions_db)) 
        {
                $item_tax_type_exemptions[] = $item_tax_type_exemp["tax_type_id"];
        }
-       
-       $ret_tax_array = array();
-       
+
+       $tax_group_items_array = $group_data['taxes'];
        // if any of the taxes of the tax group are in the exemptions, then skip
        foreach ($tax_group_items_array as $tax_group_item) 
-       { 
-               
+       {
+
                $skip = false;
-               
+
                // if it's in the exemptions, skip
-               foreach ($item_tax_type_exemptions as $exemption) 
-               {
-                       if (($tax_group_item['tax_type_id'] == $exemption)) 
+               if (is_null($tax_group_item['rate']))
+                       $skip = true;
+               else
+                       foreach ($item_tax_type_exemptions as $exemption) 
                        {
-                       $skip = true;
-                       break;
+                               if (($tax_group_item['tax_type_id'] == $exemption)) 
+                               {
+                               $skip = true;
+                               break;
+                               }
                        }
-               }
-               
+
                if (!$skip) 
                {
                        $index = $tax_group_item['tax_type_id'];
-                       $ret_tax_array[$index] = $tax_group_item;
+                       $taxdata['taxes'][$index] = $tax_group_item;
                }
        }
-       
-       return $ret_tax_array;
+
+       return $taxdata;
 }
-//-----------------------------------------------------------------------------------
-// return an array of (tax_type_id, tax_type_name, sales_gl_code, purchasing_gl_code, rate, included_in_price, Value) 
 
-function get_tax_for_items($items, $prices, $shipping_cost, $tax_group, $tax_included=null, $tax_items_array=null, $tax_algorithm = null)
+/*
+       Main tax procedure splitting transaction item value according to item tax rules applicable:
+               $stock_id - stock item code; special case is '' for shipping
+               $amount - price/value to be splitted
+               $tax_group - entity tax group
+               $tax_included - whether value includes all taxes
+               $vat_factor - 0-1; tax deduction factor
+               $allow_reverse - option for invoice - whether to honour reverse charging
+
+       Returned array contains calculated values for GL postings and tax registration:
+               'Net' - value without tax,
+               'Tax' - tax sum,
+               'Cost' - cost value (can be higher then Net value) used in parallel postings,
+               'vat_category' -  stock tax category
+       and detailed info for any tax applicable tax (any array with numeric key):
+               'tax_type_id' - tax type id
+               'Value' - charged tax value
+               'Deductible' - tax deductible (can be lower than Value for special goods)
+               'Payable' - tax payable
+               'Adjust' - additional adjustemnt to deductible tax due to sales structure factor
+               'rate' - tax rate
+               'sales_gl_code' - sales tax GL account
+               'purchasing_gl_code' - purchase tax GL account
+               'tax_type_name' - name of tax type
+*/
+function split_item_price($stock_id, $amount, $group_id, $tax_included=false, $trans_type=ST_SUPPINVOICE, $vat_factor = 1, 
+       $allow_reverse=true, $date=null)
 {
-       if (!$tax_algorithm)
-               $tax_algorithm = get_company_pref('tax_algorithm');
-       // first create and set an array with all the tax types of the tax group
-       if($tax_items_array!=null)
-               $ret_tax_array = $tax_items_array;
-       else
-               $ret_tax_array = get_tax_group_items_as_array($tax_group);
+       global $TS;
 
        $dec = user_price_dec();
 
-       $fully_exempt = true;
-       foreach($ret_tax_array as $k=>$t)
+       $itemdata = get_base_taxdata($stock_id, $group_id);
+       $vat_category = $itemdata['vat_category'];
+       $item_taxes = $itemdata['taxes'];
+
+       $taxopt = $TS->options($trans_type, $itemdata['tax_area'], $vat_category, $allow_reverse);
+
+       if (empty($item_taxes))
        {
-               if ($t['rate'] !== null)
-                       $fully_exempt = false;
-               $ret_tax_array[$k]['Net'] = 0;
+                 $ret_array['Net'] = $amount;
+                 $ret_array['Cost'] = $amount;
+                 $ret_array['Tax'] = 0;
+                 if (!is_null($item_taxes))    // register empty taxes only for not fully exempt items
+                               $ret_array[] = array('Value'=>0, 'rate' => null, 'tax_type_id' => null, 'Deductible'=>0, 'Adjust' => 0, 'Payable' => 0);
        }
-       
-       $ret_tax_array['exempt'] = array('Value'=>0, 'Net'=>0, 'rate' => null, 'tax_type_id' => '', 'sales_gl_code' => '');
-       $dec = user_price_dec();
-       // loop for all items
-       for ($i = 0; $i < count($items); $i++)
+       else
        {
-               $item_taxes = get_taxes_for_item($items[$i], $ret_tax_array);
-               if ($item_taxes == null || $fully_exempt) 
-               {
-                         $ret_tax_array['exempt']['Value'] += round2(0, $dec);
-                         $ret_tax_array['exempt']['Net'] += $prices[$i];
-               }
-               else
-               {
-                       $tax_multiplier = 0;
+               $ret_array['Net'] = $ret_array['Cost'] = $ret_array['Tax'] = 0;
+
+               $tax_multiplier = 0;
+
+               if ($taxopt&TAX_CHARGED)        // divide tax for net and tax only if charged on document
                        foreach ($item_taxes as $taxitem) 
-                       {
                                $tax_multiplier += $taxitem['rate'];
-                       }
-                       foreach ($item_taxes as $item_tax) 
-                       {
-                               if ($item_tax['rate'] !== null) {
-                                       $index = $item_tax['tax_type_id'];
-                                       if ($tax_included == 1) {
-                                               $ret_tax_array[$index]['Value'] += round2($prices[$i]*$item_tax['rate']/(100+$tax_multiplier), $dec);
-                                               $ret_tax_array[$index]['Net'] += round2($prices[$i]*100/(100+$tax_multiplier), $dec);
+
+               $partial_vat_percent = get_company_pref('partial_vat_percent');
+
+               foreach ($item_taxes as $tax_id => $item_tax) 
+               {
+                               if ($item_tax['rate'] !== null)
+                               {
+
+                                       // effective vat for some special purchases is lower than nominal
+                                       $factor = $vat_category == VC_NONDEDUCT ? 0 : ($vat_category==VC_PARTIAL ? $partial_vat_percent/100.0 : 1);
+
+                                       $net_value = $amount;
+                                       if ($tax_included == true) {
+                                               $vat_value = round($amount*$item_tax['rate']/(100+$tax_multiplier), 2);
+
+                                               if ($taxopt&TAX_CHARGED)
+                                                       $net_value -= $vat_value;
+
                                        } else {
-                                               $ret_tax_array[$index]['Value'] += round2($prices[$i] * $item_tax['rate'] / 100, $dec);
-                                               $ret_tax_array[$index]['Net'] += $prices[$i];
+
+                                               $vat_value = round($amount * $item_tax['rate'] / 100, 2);
                                        }
+
+                                       $ret_array['Net'] = round2($net_value, $dec);
+                               $ret_array['Cost'] = $ret_array['Net'];
+
+                                       $tax = array('Value' => 0, 'Deductible' => 0, 'Adjust' => 0, 'Payable' => 0);
+
+                               $tax['purchasing_gl_code'] = $item_tax['purchasing_gl_code'];
+                               $tax['sales_gl_code'] = $item_tax['sales_gl_code'];
+                               $tax['rate'] = $item_tax['rate'];
+                                       $tax['tax_type_id'] = $item_tax['tax_type_id'];
+                                       $tax['tax_type_name'] = $item_tax['tax_type_name'];
+
+                                       if ($taxopt & TAX_CHARGED)                                                      // tax is charged on document
+                                               $tax['Value'] =  round2($vat_value, $dec);
+
+                                       if ($taxopt & TAX_PAYABLE)                                                       // transaction is taxable
+                                               $tax['Payable'] =  round2($vat_value, $dec);
+
+                               if ($taxopt & TAX_DEDUCTIBLE) // tax is deductible
+                               {
+                                               $tax['Deductible'] = round2($vat_value*$factor, 2); // [4815] To avoid rounding issues if $dec > 2 decimal places
+                                               $tax['Adjust'] = round2(-(1-$vat_factor)*$factor*$vat_value, $dec); // adjustment due to mixed taxed/exmpt sales activity
+                                   } else {
+                                               $tax['Deductible'] = 0;
+                                               $tax['Adjust'] = 0;
+                                       }
+                               $ret_array['Cost'] += $tax['Value'] + ($tax['Payable'] - $tax['Deductible']);// - $tax['Adjust'];
+
+                               $ret_array[] = $tax;
+                                       $ret_array['Tax'] += $tax['Value'];
                                }
-                       }
                }
        }
-       // add the shipping taxes, only if non-zero, and only if tax group taxes shipping
-       if ($shipping_cost != 0) 
+    $ret_array['vat_category'] = $vat_category;
+       return $ret_array;
+}
+
+//-----------------------------------------------------------------------------------
+// return an array of (tax_type_id, tax_type_name, sales_gl_code, purchasing_gl_code, rate, included_in_price, Value, Net)
+//
+// $vat_factors - effective part of vat values included in tax; calculated but not included vat is added to net value
+//
+function get_tax_for_items($trans_type, $items, $prices, $shipping_cost, $tax_group, $tax_included=null,
+       $tax_algorithm = null, $vat_factors = null, $allow_reverse = true)
+{
+
+       // if shipping cost is passed, just add to the prices/items tables
+       if ($shipping_cost != 0)
+       {
+               $items[] = null;
+               $prices[] = $shipping_cost;
+               if ($vat_factors)
+                       $vat_factors[] = 1;
+       }
+
+       // calculate tax sums
+       $ret_tax_array = array();
+       foreach($items as $i => $stock_id)
        {
-               $item_taxes = get_shipping_tax_as_array($tax_group);
-               if ($item_taxes != null) 
+               $taxdata = split_item_price($stock_id, $prices[$i], $tax_group, $tax_included, $trans_type,
+                        $vat_factors ? $vat_factors[$i] : 1, $allow_reverse, $date=null); // FIXME: $date
+
+               foreach ($taxdata as $key => $data)
                {
-                       if ($tax_included == 1)
+                       if (is_numeric($key))
                        {
-                               $tax_rate = 0;
-                               foreach ($item_taxes as $item_tax)
+                               $tax_id = isset($data['tax_type_id']) ? $data['tax_type_id'] : 'exempt';
+
+                               if (!isset($ret_tax_array[$tax_id]))
                                {
-                                       $index = $item_tax['tax_type_id'];
-                                       if(isset($ret_tax_array[$index])) {
-                                               $tax_rate += $item_tax['rate'];
-                                       }
+                                       $ret_tax_array[$tax_id] = $data;
+                                       $ret_tax_array[$tax_id]['Net'] = $taxdata['Net'];
+                                       $ret_tax_array[$tax_id]['vat_category'] = $taxdata['vat_category'];
                                }
-                               $shipping_net = round2($shipping_cost*100/(100+$tax_rate), $dec);
-                       }
-                       foreach ($item_taxes as $item_tax) 
-                       {
-                               $index = $item_tax['tax_type_id'];
-                               if ($item_tax['rate'] !== null && $ret_tax_array[$index]['rate'] !== null) {
-                                       if($tax_included==1) {
-                                               $ret_tax_array[$index]['Value'] += round2($shipping_cost*$item_tax['rate']/(100+$tax_rate), $dec);
-                                               $ret_tax_array[$index]['Net'] += $shipping_net;
-                                       } else {
-                                               $ret_tax_array[$index]['Value'] += round2($shipping_cost * $item_tax['rate'] / 100, $dec);
-                                               $ret_tax_array[$index]['Net'] += $shipping_cost;
-                                       }
+                               else
+                               {
+                                       foreach(array('Value', 'Payable', 'Deductible', 'Adjust') as $amt)
+                                               $ret_tax_array[$tax_id][$amt] += $data[$amt];
+                                       $ret_tax_array[$tax_id]['Net'] += $taxdata['Net'];
                                }
                        }
                }
        }
 
-       if ($tax_algorithm == TCA_TOTALS ) {
+       if (!$tax_algorithm)
+               $tax_algorithm = get_company_pref('tax_algorithm');
+
+       if ($tax_algorithm == TCA_TOTALS) { // ?
+               $dec = user_price_dec();
                // update taxes with 
                foreach($ret_tax_array as $index => $item_tax) {
-                       $ret_tax_array[$index]['Value'] = round2($item_tax['Net'] * $item_tax['rate'] / 100, $dec);
+                       if ($ret_tax_array[$index]['Value'])
+                               $ret_tax_array[$index]['Value'] = round2($item_tax['Net'] * $item_tax['rate'] / 100, $dec);
                }
        }
 
        return $ret_tax_array;
 }
 
+
+//---------------------------------------------------------------------------------
+
+// returns the price of a given item minus any included taxes
+// for item $stock_id with line price $price and supplier/customer group_id $tax_group
+
+function get_tax_free_price_for_item($trans_type, $stock_id, $price, $tax_group, $tax_included, $allow_reverse = true)
+{
+       // if price is zero, then can't be taxed !
+       if ($price == 0)
+               return 0;
+
+       if ($tax_included==0) return $price;
+
+       $taxdata = split_item_price($stock_id, $price, $tax_group, $tax_included, $trans_type, 1, $allow_reverse);
+
+       return $taxdata['Net'];
+}
+//
+//     Full price (incl. VAT) for item $stock_id
+//     calculated for line price $price, and applicable group $tax_group
+//
+function get_full_price_for_item($trans_type, $stock_id, $price, $tax_group, $tax_included, $allow_reverse = true)
+{
+       // if price is zero, then can't be taxed !
+       if ($price == 0)
+               return 0;
+
+       if ($tax_included==1) return $price;
+
+       $taxdata = split_item_price($stock_id, $price, $tax_group, $tax_included, $trans_type, 1, $allow_reverse);
+
+       return $taxdata['Net'] + $taxdata['Tax'];
+}
index 16873e04ecf2724e849795ea1a2ef1a522827365..451c344aaecc4894ab4917fe57d0a598560ffea1 100644 (file)
@@ -59,12 +59,12 @@ if ($Mode=='ADD_ITEM' || $Mode=='UPDATE_ITEM')
                }
        if ($selected_id != -1) 
        {
-                       update_tax_group($selected_id, $_POST['name'], $taxes, $tax_shippings);
+                       update_tax_group($selected_id, $_POST['name'], $taxes, $tax_shippings, get_post('tax_area'));
                        display_notification(_('Selected tax group has been updated'));
        } 
        else 
        {
-                       add_tax_group($_POST['name'], $taxes, $tax_shippings);
+                       add_tax_group($_POST['name'], $taxes, $tax_shippings, get_post('tax_area'));
                        display_notification(_('New tax group has been added'));
        }
 
@@ -123,7 +123,7 @@ $result = get_all_tax_groups(check_value('show_inactive'));
 start_form();
 
 start_table(TABLESTYLE);
-$th = array(_("Description"), "", "");
+$th = array(_("Description"), _("Tax Area Type"), "", "");
 inactive_control_column($th);
 
 table_header($th);
@@ -135,6 +135,7 @@ while ($myrow = db_fetch($result))
        alt_table_row_color($k);
 
        label_cell($myrow["name"]);
+       label_cell($tax_area_types[$myrow['tax_area']]);
 
        inactive_control_cell($myrow["id"], $myrow["inactive"], 'tax_groups', 'id');
        edit_button_cell("Edit".$myrow["id"], _("Edit"));
@@ -157,12 +158,14 @@ if ($selected_id != -1)
        $group = get_tax_group($selected_id);
 
        $_POST['name']  = $group["name"];
+       $_POST['tax_area']  = $group["tax_area"];
 
        }
        hidden('selected_id', $selected_id);
 
 }
 text_row_ex(_("Description:"), 'name', 40);
+vat_areas_list_row(_("Tax Area Type:"), 'tax_area');
 
 end_table();
 
diff --git a/taxes/tax_rules.inc b/taxes/tax_rules.inc
new file mode 100644 (file)
index 0000000..73f30b6
--- /dev/null
@@ -0,0 +1,160 @@
+<?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>.
+***********************************************************************/
+/*
+       FA tax rules basic reimplementation. Final version would involve changes in database scheme,
+       and/or tax system class selection according to site requirements.
+
+       TODO:
+       . changes in Sales module
+       . change all tax related methods in supp_trans to use split_item_price instead of other functions like get_taxes_for_item
+       . all tax types selections in tax group table (beside domestic group) are currently ignored (see FIXME in split_item_price())
+*/
+define('TAX_NONE', 0); // none option
+define('TQ_NONE', 0); // none option
+
+// implemented taxation options
+// basic:
+define('TAX_PAYABLE', 1);
+define('TAX_DEDUCTIBLE', 2);
+define('TAX_CHARGED', 4);
+// category depenedent:
+define('TAX_DUEDATE', 8);
+define('TAX_PARTIAL', 16);
+
+// quirks dependent on vat_categories
+define('TQ_NONDEDUCT', 1);     // never deductible
+define('TQ_PARTIAL', 2);       // partially deductible (using global factor)
+define('TQ_DUEDATE', 4);       // taxable on due date instead of transaction date
+define('TQ_REVERSE', 8);       // tax is reverse charged
+define('TQ_IMPDEDUCTIBLE', 16);        // import tax is deductible
+
+class tax_system
+{
+       //-------------------------- entity taxation rules ----------------------------
+       // general rules: who and when pays taxes
+       //
+       // FIXME: credit notes
+       //
+       var     $description;
+       var $ts_basic_rules;
+       var $ts_category_quirks;
+
+       function __construct()
+       {
+               $this->ts_basic_rules = array(
+                       TA_DOMESTIC => array(
+                               ST_SALESQUOTE => TAX_CHARGED,
+                               ST_SALESORDER => TAX_CHARGED,
+                               ST_CUSTDELIVERY => TAX_CHARGED,
+                               ST_SALESINVOICE => TAX_PAYABLE | TAX_CHARGED,
+                               ST_CUSTCREDIT => TAX_PAYABLE | TAX_CHARGED,
+                               ST_PURCHORDER => TAX_CHARGED,
+                               ST_SUPPRECEIVE => TAX_CHARGED,
+                               ST_SUPPINVOICE => TAX_DEDUCTIBLE | TAX_CHARGED,
+                               ST_SUPPCREDIT => TAX_DEDUCTIBLE | TAX_CHARGED,
+                       ),
+                       TA_EXPORT => array(
+                               ST_SALESQUOTE => TAX_NONE,
+                               ST_SALESORDER => TAX_NONE,
+                               ST_CUSTDELIVERY => TAX_NONE,
+                               ST_SALESINVOICE => TAX_NONE,
+                               ST_CUSTCREDIT => TAX_NONE,
+                               ST_PURCHORDER => TAX_NONE,
+                               ST_SUPPRECEIVE => TAX_NONE,
+                               ST_SUPPINVOICE => TAX_NONE,
+                               ST_SUPPCREDIT => TAX_NONE,
+                       ),
+                       TA_EU => array(
+                               ST_SALESQUOTE => TAX_NONE,
+                               ST_SALESORDER => TAX_NONE,
+                               ST_CUSTDELIVERY => TAX_NONE,
+                               ST_SALESINVOICE => TAX_NONE,
+                               ST_CUSTCREDIT => TAX_NONE,
+                               ST_PURCHORDER => TAX_NONE,
+                               ST_SUPPRECEIVE => TAX_NONE,
+                               ST_SUPPINVOICE => TAX_DEDUCTIBLE | TAX_PAYABLE,
+                               ST_SUPPCREDIT => TAX_DEDUCTIBLE | TAX_PAYABLE,
+                       ),
+               );
+
+               //-------------------------- special goods dependent rules  ----------------------------
+               //
+               $this->ts_category_quirks = array(
+                       VC_OTHER => TQ_NONE,
+                       VC_MEDIA => TQ_DUEDATE,                 // is no longer used ?
+                       VC_ASSETS => TQ_NONE,                   // just separate category in tax reg
+                       VC_NONDEDUCT => TQ_NONDEDUCT,
+                       VC_SERVICES => TQ_IMPDEDUCTIBLE,
+                       VC_PARTIAL => TQ_PARTIAL,
+                       VC_REVERSE => TQ_REVERSE,
+               );
+       }
+       /*
+               Returns tax options applicable for the arguments set
+
+               $allow_reverse - decides whether reverse charging option is honoured for sales invoice
+               (this depends on 'continue transaction' value as defined by law, so have to be set by case).
+       */
+       function options($trans_type, $tax_area, $vat_category, $allow_reverse=true)
+       {
+               if (!isset($vat_category)) // exempt goods has really no category
+                       return TAX_NONE;
+
+               $options = $this->ts_basic_rules[$tax_area][$trans_type];
+
+               // per vat category quirks
+               $quirks = $this->ts_category_quirks[$vat_category];
+
+               if ($quirks & TQ_DUEDATE)
+                       $options |= TAX_DUEDATE;
+
+               if ($quirks & TQ_NONDEDUCT)
+                       $options &= ~TAX_DEDUCTIBLE;
+
+               if ($quirks & TQ_PARTIAL)
+                       if ($options & TAX_DEDUCTIBLE)
+                               $options |= TAX_PARTIAL;
+
+               if ($quirks & TQ_IMPDEDUCTIBLE)
+                       if ($tax_area == TA_EXPORT && in_array($trans_type, array(ST_SUPPINVOICE, ST_SUPPCREDIT)))
+                       {
+                               $options |= TAX_DEDUCTIBLE | TAX_PAYABLE;
+                       }
+
+               if (($quirks & TQ_REVERSE) && (!in_array($trans_type,array(ST_SALESINVOICE, ST_CUSTCREDIT)) || $allow_reverse))
+                       if ($tax_area == TA_DOMESTIC)
+                       {
+                               $options ^= TAX_PAYABLE;
+                               $options &= ~TAX_CHARGED;
+                       }
+
+               return $options;
+       }
+};
+
+function dbg_tax($options)
+{
+       $dbg_rules = array(
+               TAX_PAYABLE => 'payable',
+               TAX_DEDUCTIBLE => 'deductible',
+               TAX_CHARGED => 'charged',
+               TAX_DUEDATE => 'due_date',
+               TAX_PARTIAL => 'partial'
+       );
+
+       $opts = array();
+       foreach($dbg_rules as $key => $name)
+               if ($options & $key)
+                       $opts[] = $name;
+
+       _vd(implode($opts, ','));
+}