443a16ed30072648f7520a7a03c210e19fa000a9
[fa-stable.git] / purchasing / includes / db / invoice_db.inc
1 <?php
2 /**********************************************************************
3     Copyright (C) FrontAccounting, LLC.
4         Released under the terms of the GNU General Public License, GPL, 
5         as published by the Free Software Foundation, either version 3 
6         of the License, or (at your option) any later version.
7     This program is distributed in the hope that it will be useful,
8     but WITHOUT ANY WARRANTY; without even the implied warranty of
9     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
10     See the License here <http://www.gnu.org/licenses/gpl-3.0.html>.
11 ***********************************************************************/
12 include_once($path_to_root . "/purchasing/includes/db/invoice_items_db.inc");
13 include_once($path_to_root . "/admin/db/attachments_db.inc");
14
15 //--------------------------------------------------------------------------------------------------
16
17 function read_supplier_details_to_trans(&$supp_trans, $supplier_id)
18 {
19         $sql = "SELECT supp.supp_name, supp.dimension_id, supp.dimension2_id, supp.tax_included,
20                 supp.tax_group_id, tax_group.name AS tax_group_name,
21                 supp.credit_limit - Sum((ov_amount + ov_gst + ov_discount)) as cur_credit,
22                 supp.curr_code,
23                 terms.*
24                 FROM ".TB_PREF."suppliers supp 
25                         LEFT JOIN ".TB_PREF."supp_trans trans ON supp.supplier_id = trans.supplier_id
26                         LEFT JOIN ".TB_PREF."payment_terms terms ON supp.payment_terms=terms.id
27                         LEFT JOIN ".TB_PREF."tax_groups tax_group ON supp.tax_group_id = tax_group.id
28                 WHERE supp.supplier_id = ".db_escape($supplier_id);
29
30         $result = db_query($sql, "The supplier record selected: " . $supplier_id . " cannot be retrieved");
31
32         $myrow = db_fetch($result);
33
34     if ($supp_trans->tran_date == "")
35     {
36                 $supp_trans->tran_date = Today();
37
38                 if (!is_date_in_fiscalyear($supp_trans->tran_date))
39                         $supp_trans->tran_date = end_fiscalyear();
40         }
41         if ($supp_trans->supplier_id != $supplier_id)
42             get_duedate_from_terms($supp_trans);
43
44     $supp_trans->supplier_id = $supplier_id;
45     $supp_trans->tax_included = $myrow['tax_included'];
46     $supp_trans->supplier_name = $myrow['supp_name'];
47         $supp_trans->terms = array( 
48                 'description' => $myrow['terms'],
49                 'type' => $myrow['type'], 
50                 'days' => $myrow['days'],
51                 'early_discount' => $myrow['early_discount'],
52                 'early_days' => $myrow['early_days'],
53         );
54         $supp_trans->credit = $myrow['cur_credit'];
55
56         $supp_trans->tax_description = $myrow['tax_group_name'];
57         $supp_trans->tax_group_id = $myrow['tax_group_id'];
58         $supp_trans->dimension = $myrow['dimension_id'];
59         $supp_trans->dimension2 = $myrow['dimension2_id'];
60         $supp_trans->currency = $myrow['curr_code'];
61  
62 }
63
64 //-------------------------------------------------------------------------------------------------
65 //      Updates invoiced quantity in PO and GRN line, and act_price in PO
66 //  Returns:
67 // if chg_price is set:  previous act_price, delivery date and unit_price from PO
68 //
69 function update_supp_received_items_for_invoice($id, $po_detail_item, $qty_invoiced, $chg_price=null)
70 {
71         if ($chg_price != null)
72         {
73                 $sql = "SELECT act_price, unit_price FROM ".TB_PREF."purch_order_details WHERE
74                         po_detail_item = ".db_escape($po_detail_item);
75                 $result = db_query($sql, "The old actual price of the purchase order line could not be retrieved");
76                 $row = db_fetch($result);
77                 $ret = $row['act_price'];
78
79                 $unit_price = $row['unit_price']; //Added by Rasmus
80
81                 $sql = "SELECT delivery_date 
82                         FROM ".TB_PREF."grn_batch grn,"
83                                 .TB_PREF."grn_items line
84                         WHERE
85                                 grn.id = line.grn_batch_id AND line.id=".db_escape($id);
86                 $result = db_query($sql, "The old delivery date from the received record cout not be retrieved");
87                 $row = db_fetch($result);
88                 $date = $row['delivery_date'];
89         }
90         else
91         {
92                 $ret = 0;
93                 $date = "";
94                 $unit_price = 0; // Added by Rasmus
95         }
96     $sql = "UPDATE ".TB_PREF."purch_order_details
97                 SET qty_invoiced = qty_invoiced + ".db_escape($qty_invoiced);
98
99         if ($chg_price != null)
100                 $sql .= " , act_price = ".db_escape($chg_price);
101
102         $sql .= " WHERE po_detail_item = ".db_escape($po_detail_item);
103     db_query($sql, "The quantity invoiced of the purchase order line could not be updated");
104
105     $sql = "UPDATE ".TB_PREF."grn_items
106         SET quantity_inv = quantity_inv + ".db_escape($qty_invoiced)."
107         WHERE id = ".db_escape($id);
108         db_query($sql, "The quantity invoiced off the items received record could not be updated");
109         return array($ret, $date, $unit_price);
110 }
111
112 function get_diff_in_home_currency($supplier, $old_date, $date, $amount1, $amount2)
113 {
114         $dec = user_price_dec();
115         price_decimal_format($amount2, $dec);
116         $currency = get_supplier_currency($supplier);
117         $ex_rate = get_exchange_rate_to_home_currency($currency, $old_date);
118         $amount1 = $amount1 / $ex_rate;
119         $ex_rate = get_exchange_rate_to_home_currency($currency, $date);
120         $amount2 = $amount2 / $ex_rate;
121         $diff = $amount2 - $amount1;
122         //return round2($diff, $dec);
123         return $diff;
124 }
125 //----------------------------------------------------------------------------------------
126
127 function add_supp_invoice(&$supp_trans)  //, $already_voided=false, $allocs=null)
128 {
129         global $Refs, $systypes_array;
130
131         $trans_no = $supp_trans->trans_no;
132         $trans_type = $supp_trans->trans_type;
133         $supplier = get_supplier($supp_trans->supplier_id);
134         $dec = user_price_dec();
135
136         begin_transaction(__FUNCTION__, func_get_args());
137         hook_db_prewrite($supp_trans, $trans_type);
138
139         if ($trans_no) {        // void old transaction
140 //       if (!$already_voided) {        // transaction is already voided in case of direct invoice edition, which is needed for proper inventory value handling
141                 $allocs = get_payments_for($trans_no, $trans_type, $supp_trans->supplier_id); // save allocations
142                 void_supp_invoice($trans_type, $trans_no, true);
143                 add_audit_trail($trans_type, $trans_no, Today(), _("Voided."));
144                 add_voided_entry($trans_type, $trans_no, Today(), _("Document reentered."));
145                 $Refs->restore_last($trans_type, $trans_no);
146 //       }
147         } else
148                 $allocs = get_po_prepayments($supp_trans);
149
150         // register exchange rate when used first time on date
151         add_new_exchange_rate($supplier['curr_code'], $supp_trans->tran_date, $supp_trans->ex_rate);
152
153         // store basic transaction info
154         $invoice_id = write_supp_trans($trans_type, 0, $supp_trans->supplier_id, $supp_trans->tran_date, $supp_trans->due_date,
155                 $supp_trans->reference, $supp_trans->supp_reference, 0, 0, 0, $supp_trans->ex_rate, $supp_trans->tax_included);
156
157         if ($trans_no)
158                 move_trans_attachments($trans_type, $trans_no, $invoice_id);
159
160         $supp_trans->trans_no = $invoice_id;
161     $date_ = $supp_trans->tran_date;
162
163         if (!$supp_trans->ex_rate)
164                 $supp_trans->ex_rate = get_exchange_rate_from_home_currency($supplier['curr_code'], $date_);
165
166         // prepare cart for GL postings 
167         $gl_cart = new items_cart($trans_type, $invoice_id);
168         $gl_cart->tran_date = $date_;
169         $gl_cart->set_currency($supplier['curr_code'], $supp_trans->ex_rate);
170
171     $clearing_act = get_company_pref('grn_clearing_act');
172
173         $supp_trans->split_line_values();       // generate amounts for GL postings
174
175         $tax_total = 0;
176         $net_total = 0;
177         foreach($supp_trans->grn_items as $item)
178         {
179                 $item_gl = $item->gl_amounts;
180
181                 $stock_gl_code = get_stock_gl_code($item->item_code);
182                 $dimension = !empty($supp_trans->dimension) ? $supp_trans->dimension :
183                         ($supplier['dimension_id'] ? $supplier['dimension_id'] : $stock_gl_code['dimension_id']);
184                 $dimension2 = !empty($supp_trans->dimension2) ? $supp_trans->dimension2 :
185                         ($supplier['dimension2_id'] ? $supplier['dimension2_id'] : $stock_gl_code['dimension2_id']);
186
187                 $line_tax = 0;
188                 foreach($item_gl as $index => $value)
189                 {
190                          if (is_numeric($index)) // taxes
191                          {
192                                 if ($value['Deductible'])
193                                 {       // GL: VAT deductible
194                                         $gl_cart->add_gl_item($value['purchasing_gl_code'], 0, 0, $value['Deductible']+$value['Adjust']);
195                                         // GL: VAT adjustment due to vat factor
196                                         if ($value['Adjust'])
197                                                 $gl_cart->add_gl_item(get_company_pref('tax_adjustments_act'), 0, 0, -$value['Adjust']);
198                                 }
199                                 if ($value['Payable']) // reverse charge/intracommunity aquisition
200                                 {
201                                         $gl_cart->add_gl_item($value['sales_gl_code'], 0, 0, -$value['Payable']);
202                                 }
203                                 // GL: AP account (vat part)
204                                 if ($value['Value'])
205                                 {
206                                         $gl_cart->add_gl_item($supplier["payable_account"], 0, 0, -$value['Value'] , '', '', $supp_trans->supplier_id);
207                                 }
208                                 if (($item->vat_category == VC_REVERSE) && $supplier['tax_area'] == TA_EU)
209                                         $vat_category = VC_OTHER;
210                                 else
211                                         $vat_category = $item->vat_category;
212
213                                 add_trans_tax_details($trans_type, $invoice_id, 
214                                         $value['tax_type_id'], $value['rate'], $supp_trans->tax_included, $value['Value'],
215                                         $item_gl['Net'], $supp_trans->ex_rate, $date_, $supp_trans->supp_reference, TR_INPUT, $supp_trans->tax_group_id, $vat_category);
216
217                                 $line_tax += $value['Payable'] ? $value['Payable'] : $value['Value'];
218                         }
219                 }
220
221                 // GL: AP account (net)
222                 $gl_cart->add_gl_item($supplier["payable_account"], 0, 0, -$item_gl['Net'], '', '', $supp_trans->supplier_id);
223
224                 $tax_total += $item_gl['Tax'];
225                 $net_total += $item_gl['Net'];
226
227                 // cost line value
228                 $taxfree_line =  $item_gl['Cost'];
229
230                 if (!is_inventory_item($item->item_code))
231                 {
232                         $gl_cart->add_gl_item($supplier["purchase_account"] ? $supplier["purchase_account"] : $stock_gl_code["cogs_account"], $dimension, $dimension2, $taxfree_line);
233
234                 } elseif ($trans_type != ST_SUPPCREDIT) {
235                         $gl_cart->add_gl_item($stock_gl_code["inventory_account"], $dimension, $dimension2, $taxfree_line, _('Return to supplier'));
236                         // we must use invoice value here to preserve proper inventory valuation,
237                         // but if the purchase changed average cost, and any item was sold between invoice and credit,
238                         // the average will not return to the previous one. To fix this additional cost update should be made here to compensate cogs difference on items sold.
239                         update_average_material_cost(null, $item->item_code, $item_gl['Cost']/$item->this_quantity_inv, -$item->this_quantity_inv, $date_);
240                 } else {
241
242                         // calculate difference between clearing cost and actual cost
243                         $diff = $item_gl['Cost'] - $item->std_cost_unit*$item->this_quantity_inv;
244
245                         update_average_material_cost(null, $item->item_code,
246                                 $diff/$item->this_quantity_inv, $item->this_quantity_inv, null, true);
247
248                         if ($clearing_act)
249                         {
250                                 if ($diff != 0) // if value on invoice differs from those posted on GRN receive, post the difference
251                                 {
252
253                                         $gl_cart->add_gl_item($stock_gl_code["inventory_account"],                      // cart is in supplier currency, so need to fix by ex_rate here
254                                                 $dimension, $dimension2, $diff/$gl_cart->rate, _('GRN Provision')); // subject to rounding errors?
255
256                                         $gl_cart->add_gl_item($clearing_act,
257                                                 $dimension, $dimension2, -$diff/$gl_cart->rate);
258
259                                         //Chaitanya
260                                         //If QOH is 0 or negative then update_average_material_cost will be skipped
261                                         //Thus difference in PO and Supp Invoice should be handled separately
262                                         $qoh = get_qoh_on_date($item->item_code);
263                                         if ($qoh <= 0)
264                                         {
265                                                 global $Refs;
266                                                         //Chaitanya : Post a journal entry
267                                                 $id = get_next_trans_no(ST_JOURNAL);
268                                                 $ref = $Refs->get_next(ST_JOURNAL, null, $date_);
269                                                 add_journal(ST_JOURNAL, $id, $diff, $date_, get_company_currency(), $ref);
270                                                 $stock_id = $item->item_code;
271                                                 $stock_gl_code = get_stock_gl_code($stock_id);
272                                                 $memo = _("Supplier invoice adjustment for zero inventory of ").$stock_id." "._("Invoice")." ".$supp_trans->reference;
273                                                 //Reverse the inventory effect if $qoh <=0
274                                                 add_gl_trans_std_cost(ST_JOURNAL, $id, $date_, 
275                                                         $stock_gl_code["inventory_account"],
276                                                         $dimension, $dimension2,
277                                                         $memo, -$diff);
278                                                 //GL Posting to inventory adjustment account
279                                                 add_gl_trans_std_cost(ST_JOURNAL, $id, $date_, 
280                                                         $stock_gl_code["adjustment_account"],
281                                                         $dimension, $dimension2,
282                                                         $memo, $diff);
283                                                         add_audit_trail(ST_JOURNAL, $id, $date_);
284                                                 add_comments(ST_JOURNAL, $id, $date_, $memo);
285                                                 $Refs->save(ST_JOURNAL, $id, $ref);
286                                         }
287                                 }
288                                 $gl_cart->add_gl_item($clearing_act, $dimension, $dimension2, $taxfree_line);
289                         }
290                         else  // no postings on GRN, so post full cost here
291                                 $gl_cart->add_gl_item($stock_gl_code["inventory_account"], $dimension, $dimension2,     $taxfree_line);
292
293                 }
294
295 //        if (is_fa_item($item->item_code)) {
296 //          add_actual_cost($item->order_price, $item->item_code);
297 //        }
298
299         if ($trans_type == ST_SUPPCREDIT)
300                         set_grn_item_credited($item, $supp_trans->supplier_id, $invoice_id, $date_);
301                 else {
302                         add_or_update_purchase_data($supp_trans->supplier_id, $item->item_code, $item->chg_price); 
303
304                         update_supp_received_items_for_invoice($item->id, $item->po_detail_item,
305                                 $item->this_quantity_inv, $item->chg_price);
306                 }
307
308                 $qty = ($trans_type==ST_SUPPCREDIT ? 1 : -1)*$item->this_quantity_inv;
309                 add_supp_invoice_item($trans_type, $invoice_id, $item->item_code,
310                         $item->item_description, 0,     $item->chg_price, $line_tax/$qty, $qty, $item->id, $item->po_detail_item);
311     } // grn_items
312
313
314     /*GL Items are straight forward - just do the debit postings to the GL accounts specified -
315     the credit is to creditors control act */
316     foreach ($supp_trans->gl_codes as $entered_gl_code)
317     {
318                 $memo_ = $entered_gl_code->memo_;
319
320                 $index = is_tax_account($entered_gl_code->gl_code);
321                 if ($index !== false)
322                 {
323                         $gl_cart->add_gl_item($entered_gl_code->gl_code, $entered_gl_code->gl_dim, $entered_gl_code->gl_dim2, $entered_gl_code->amount);
324                         // store tax details if the gl account is a tax account
325                         add_gl_tax_details($entered_gl_code->gl_code, 
326                                 $trans_type, $invoice_id, $entered_gl_code->amount,
327                                 $supp_trans->ex_rate, $date_, $supp_trans->supp_reference, $supp_trans->tax_included, null, $supp_trans->tax_group_id);
328
329                         $tax_total += $entered_gl_code->amount;
330                 } else {
331                         $gl_cart->add_gl_item($entered_gl_code->gl_code, $entered_gl_code->gl_dim, $entered_gl_code->gl_dim2, $entered_gl_code->amount);
332                         $net_total += $entered_gl_code->amount;
333                 }
334                 $gl_cart->add_gl_item($supplier["payable_account"], 0, 0, -$entered_gl_code->amount, '', '', $supp_trans->supplier_id);
335
336                 add_supp_invoice_gl_item($trans_type, $invoice_id, $entered_gl_code->gl_code, $entered_gl_code->amount, $memo_,
337                         $entered_gl_code->gl_dim, $entered_gl_code->gl_dim2);
338     }
339
340         if ($trans_type == ST_SUPPCREDIT) {             // reverse postings if this is credit note
341                 foreach($gl_cart->gl_items as &$line)
342                         $line->amount = -$line->amount;
343                 $net_total = -$net_total;
344                 $tax_total = -$tax_total;
345         }
346         $gl_cart->reduce_gl();  // minimize GL lines
347
348         $gl_cart->write_gl(false); // don't check balances here: we are working on two (maybe unbalanced) carts
349
350         update_supp_trans_sums($trans_type, $invoice_id, round($net_total,2), round($tax_total,2));
351
352         add_comments($trans_type, $invoice_id, $date_, $supp_trans->Comments);
353
354         $Refs->save($trans_type, $invoice_id, $supp_trans->reference);
355
356         if (count($supp_trans->src_docs) == 1)
357         {
358                 $invoice_no = key($supp_trans->src_docs);
359                 $invoice_alloc_balance = get_supp_trans_allocation_balance(ST_SUPPINVOICE, $supp_trans->src_docs);
360                 if ($invoice_alloc_balance > 0) 
361                 {       //the invoice is not already fully allocated 
362
363                         $trans = get_supp_trans($invoice_id, ST_SUPPCREDIT);
364                         $total = -$trans['Total'];
365
366                         $allocate_amount = ($invoice_alloc_balance > $total) ? $total : $invoice_alloc_balance;
367
368                         /*Now insert the allocation record if > 0 */
369                         if ($allocate_amount != 0) 
370                         {
371                                 add_supp_allocation($allocate_amount, ST_SUPPCREDIT, $invoice_id, ST_SUPPINVOICE, $invoice_no,
372                                         $supp_trans->supplier_id, $date_);
373                                 update_supp_trans_allocation(ST_SUPPINVOICE, $invoice_no, $supp_trans->supplier_id);
374                                 update_supp_trans_allocation(ST_SUPPCREDIT, $invoice_id, $supp_trans->supplier_id);
375
376                                 exchange_variation(ST_SUPPCREDIT, $invoice_id, ST_SUPPINVOICE, $supp_trans->src_docs, $date_,
377                                         $allocate_amount, PT_SUPPLIER);
378                         }
379                 }
380         }
381
382         reallocate_payments($invoice_id, ST_SUPPINVOICE, $date_, $net_total+$tax_total, $allocs, $supp_trans->supplier_id);
383
384         hook_db_postwrite($supp_trans, $supp_trans->trans_type);
385     commit_transaction();
386     return $invoice_id;
387 }
388
389 //----------------------------------------------------------------------------------------
390
391 // get all the invoices/credits for a given PO - quite long route to get there !
392
393 function get_po_invoices_credits($po_number)
394 {
395         $sql = "SELECT DISTINCT trans.trans_no, trans.type,     ov_amount+ov_discount+ov_gst AS Total,
396                                 trans.tran_date
397                         FROM ".TB_PREF."supp_trans trans,"
398                                 .TB_PREF."supp_invoice_items line,"
399                                 .TB_PREF."purch_order_details poline,"
400                                 .TB_PREF."purch_orders po
401                 WHERE line.supp_trans_no = trans.trans_no
402                         AND line.supp_trans_type = trans.type
403                         AND line.po_detail_item_id = poline.po_detail_item
404                         AND po.supplier_id = trans.supplier_id
405                         AND po.order_no = poline.order_no
406                         AND poline.order_no = ".db_escape($po_number);
407
408         return db_query($sql, "The invoices/credits for the po $po_number could not be retreived");
409 }
410
411 //----------------------------------------------------------------------------------------
412 //
413 // Retrieve tax ovverides from tax register.
414 //
415 function get_tax_overrides($trans_type, $trans_no)
416 {
417         $tax_overrides = array();
418         $sql = "SELECT amount, tax_type_id as id, rate
419                 FROM ".TB_PREF."trans_tax_details details
420                 WHERE trans_type=".db_escape($trans_type)." AND trans_no=".db_escape($trans_no)
421                 ." AND amount>0"; // skip reposting of nondeductible VAT
422
423         $result = db_query($sql, 'Cannot retrieve tax overrides');
424         while($row = db_fetch($result))
425         {
426                 $tax_overrides[$row['id']] = $row['amount'];
427         }
428
429         return $tax_overrides;
430 }
431 //----------------------------------------------------------------------------------------
432
433 function read_supp_invoice($trans_no, $trans_type, &$supp_trans)
434 {
435         $sql = "SELECT trans.*, supp_name, dimension_id, dimension2_id, curr_code
436                 FROM ".TB_PREF."supp_trans trans,"
437                         .TB_PREF."suppliers sup
438                 WHERE trans_no = ".db_escape($trans_no)." AND type = ".db_escape($trans_type)."
439                 AND sup.supplier_id=trans.supplier_id";
440
441         $result = db_query($sql, "Cannot retreive a supplier transaction");
442
443         if (db_num_rows($result) == 1)
444         {
445                 $trans_row = db_fetch($result);
446
447                 $supp_trans->supplier_id = $trans_row["supplier_id"];
448                 $supp_trans->supplier_name = $trans_row["supp_name"];
449                 $supp_trans->tran_date = sql2date($trans_row["tran_date"]);
450                 $supp_trans->due_date = sql2date($trans_row["due_date"]);
451                 $supp_trans->Comments = get_comments_string($trans_type, $trans_no);
452                 $supp_trans->reference = $trans_row["reference"];
453                 $supp_trans->supp_reference = $trans_row["supp_reference"];
454                 $supp_trans->ov_amount = $trans_row["ov_amount"];
455                 $supp_trans->ov_discount = $trans_row["ov_discount"];
456                 $supp_trans->ov_gst = $trans_row["ov_gst"];
457                 $supp_trans->tax_included = $trans_row["tax_included"];
458                 $supp_trans->dimension = $trans_row["dimension_id"];
459                 $supp_trans->dimension2 = $trans_row["dimension2_id"];
460                 $supp_trans->currency = $trans_row["curr_code"];
461
462                 $id = $trans_row["trans_no"];
463
464                 $result = get_supp_invoice_items($trans_type, $id);
465
466                 if (db_num_rows($result) > 0)
467                 {
468
469             while ($details_row = db_fetch($result))
470             {
471
472                 if ($details_row["gl_code"] == 0)
473                 {
474                         $supp_trans->add_grn_to_trans($details_row["grn_item_id"], $details_row["po_detail_item_id"], $details_row["stock_id"],
475                                         $details_row["description"], 
476                                         $details_row['qty_recd'],
477                                         $details_row['quantity_inv']-$details_row["quantity"],
478                                         $details_row["quantity"], 0, $details_row["FullUnitPrice"],
479                                         0, 0);
480                 }
481                 else
482                 {
483                         $supp_trans->add_gl_codes_to_trans($details_row["gl_code"], get_gl_account_name($details_row["gl_code"]), 
484                                 $details_row["dimension_id"], $details_row["dimension2_id"],
485                                         $details_row["FullUnitPrice"], $details_row["memo_"]);
486                 }
487             }
488                         $supp_trans->tax_overrides = get_tax_overrides($trans_type, $trans_no);
489         }
490         else
491         {
492                         return display_db_error("Invalid supptrans details for supptrans number : $trans_no and type : $trans_type", $sql, true);
493                 }
494
495         }
496         else
497         {
498                 return display_db_error("Invalid supptrans number : $trans_no and type : $trans_type", $sql, true);
499         }
500 }
501
502 //----------------------------------------------------------------------------------------
503
504 function get_matching_invoice_item($stock_id, $po_item_id)
505 {
506         $sql = "SELECT *, tran_date
507                 FROM ".TB_PREF."supp_invoice_items,"
508                         .TB_PREF."supp_trans
509                 WHERE supp_trans_type = ".ST_SUPPINVOICE." AND stock_id = "
510                 .db_escape($stock_id)." AND po_detail_item_id = ".db_escape($po_item_id)."
511                 AND supp_trans_no = trans_no";
512         $result = db_query($sql, "Cannot retreive supplier transaction detail records");
513         return db_fetch($result);  
514 }
515
516 function void_supp_invoice($type, $type_no)
517 {
518         begin_transaction(__FUNCTION__, func_get_args());
519
520         hook_db_prevoid($type, $type_no);
521         $trans = get_supp_trans($type_no, $type);
522
523         void_bank_trans($type, $type_no, true);
524
525         void_gl_trans($type, $type_no, true);
526
527         void_supp_allocations($type, $type_no);
528
529         void_supp_trans($type, $type_no);
530
531         $result = get_supp_invoice_items($type, $type_no);
532
533         // now remove this invoice/credit from any GRNs/POs that it's related to
534         if (db_num_rows($result) > 0)
535         {
536                 $date_ = Today();
537         while ($details_row = db_fetch($result))
538         {
539                 if ((int)$details_row["grn_item_id"] > 0) // it can be empty for GL items
540                 {
541                         // Changed 2008-10-17 by Joe Hunt to get the avg. material cost updated
542                                 $old = update_supp_received_items_for_invoice($details_row["grn_item_id"],
543                                         $details_row["po_detail_item_id"], -$details_row["quantity"], $details_row["FullUnitPrice"]); 
544                                 
545                                 //$diff = $details_row["FullUnitPrice"] - $old[2];
546                                 $old_date = sql2date($old[1]);
547                                 
548                                 $batch = get_grn_batch_from_item($details_row["grn_item_id"]);  
549                                 $grn = get_grn_batch($batch);
550                         if ($type == ST_SUPPCREDIT) // credit note 2009-06-14 Joe Hunt Must restore the po and grn
551                         {       // We must get the corresponding invoice item to check for price chg.
552                                         $match = get_matching_invoice_item($details_row["stock_id"], $details_row["po_detail_item_id"]);
553                                         //Chaitanya : Skipped costing block & handle in void_stock_move
554                                         // We must get the corresponding invoice item to check for price chg.
555                                         /*if ($match !== false)
556                                                 $mat_cost = update_average_material_cost($grn["supplier_id"], $details_row["stock_id"],  
557                                                         $match["unit_price"], -$details_row["quantity"], sql2date($match['tran_date']), $match['tran_date'] !== $trans['tran_date']);
558                                         else            
559                                                 $mat_cost = update_average_material_cost($grn["supplier_id"], $details_row["stock_id"],  
560                                                         $details_row["FullUnitPrice"], -$details_row["quantity"], $old_date, $old[1] !== $trans['tran_date']);*/
561
562                                         $sql = "UPDATE ".TB_PREF."purch_order_details
563                                         SET quantity_ordered = quantity_ordered + ".-$details_row["quantity"].", ";
564                                 if ($match !== false)
565                                         $sql .= "act_price=".$match['unit_price'].", ";
566                                 $sql .= "quantity_received = quantity_received + ".-$details_row["quantity"]."
567                                     WHERE po_detail_item = ".$details_row["po_detail_item_id"];
568                                         db_query($sql, "a purchase order details record could not be updated. This receipt of goods has not been processed ");
569                                         $sql = "UPDATE ".TB_PREF."grn_items SET qty_recd=qty_recd+".-$details_row["quantity"]."
570                                                 WHERE id=".$details_row["grn_item_id"];
571                                         db_query($sql);
572                         }
573                         else
574                         {
575                                         $diff = get_diff_in_home_currency($grn["supplier_id"], $old_date, sql2date($trans['tran_date']), $old[2], 
576                                                 $details_row["FullUnitPrice"]);
577                                         // Only adjust the avg for the diff
578                                         $mat_cost = update_average_material_cost(null, $details_row["stock_id"],
579                                                 $diff, -$details_row["quantity"], $old_date, true);
580                                                 
581                                         //Chaitanya : Reverse effect
582                                         //If QOH is 0 or negative then update_average_material_cost will be skipped
583                                         //Thus difference in PO and Supp Invoice should be handled separately
584                                         
585                                         $qoh = get_qoh_on_date($details_row["stock_id"]);
586                                         if ($diff*$details_row["quantity"] !=0 && $qoh <= 0)
587                                         {
588                                                 global $Refs;
589
590                                                 //Chaitanya : Post a journal entry
591                                                 $id = get_next_trans_no(ST_JOURNAL);
592                                                 $ref = $Refs->get_next(ST_JOURNAL, null, $date_);
593                                                 add_journal(ST_JOURNAL, $id, $details_row["quantity"] * $diff, $old_date, get_company_currency(), $ref);
594                                                 $stock_id = $details_row["stock_id"];
595                                                 $stock_gl_code = get_stock_gl_code($stock_id);
596                                                 $memo = "Reversing Supplier invoice adjustment for zero inventory of ".$stock_id." Invoice: ".$trans['reference'];
597                                                 //Reverse the inventory effect if $qoh <=0
598                                                 add_gl_trans_std_cost(ST_JOURNAL, $id, $old_date, 
599                                                         $stock_gl_code["inventory_account"],
600                                                         $dim, $dim2, $memo, $details_row["quantity"] * $diff);
601                                                 //GL Posting to inventory adjustment account
602                                                 add_gl_trans_std_cost(ST_JOURNAL, $id, $old_date, 
603                                                         $stock_gl_code["adjustment_account"],
604                                                         $dim, $dim2, $memo, -$details_row["quantity"] * $diff);
605                                                 
606                                                 add_audit_trail(ST_JOURNAL, $id, $old_date);
607                                                 add_comments(ST_JOURNAL, $id, $old_date, $memo);
608                                                 $Refs->save(ST_JOURNAL, $id, $ref);
609                                         }
610                                 }
611                 }
612         }
613         }
614
615         if ($type == ST_SUPPCREDIT) // void the credits in stock moves
616                 void_stock_move($type, $type_no); 
617         void_supp_invoice_items($type, $type_no);
618         void_trans_tax_details($type, $type_no);
619
620         commit_transaction();
621 }
622
623 //----------------------------------------------------------------------------------------
624 function get_gl_account_info($acc)
625 {
626         $sql = "SELECT account_code, account_name FROM ".TB_PREF."chart_master WHERE account_code=".db_escape($acc);
627         return db_query($sql,"get account information");
628 }
629
630 function is_reference_already_there($supplier_id, $supp_reference, $trans_no=0)
631 {
632         $sql = "SELECT COUNT(*) FROM ".TB_PREF."supp_trans WHERE supplier_id="
633                 .db_escape($supplier_id) . " AND supp_reference=" 
634                 .db_escape($supp_reference) 
635                 . " AND ov_amount!=0"; // ignore voided invoice references
636         if ($trans_no)
637                 $sql .= " AND trans_no!=$trans_no";
638         $result=db_query($sql,"The sql to check for the previous entry of the same invoice failed");
639
640         $myrow = db_fetch_row($result);
641         return $myrow[0] > 0;
642 }
643
644 function find_src_invoices($cart)
645 {
646         $invoices = $po_ids = array();
647         foreach($cart->grn_items as $item)
648                 $po_ids[] = "'$item->po_detail_item'";  // credit item lines
649
650         if (count($po_ids)) {
651                 $sql = "SELECT DISTINCT trans.trans_no, trans.reference, trans.supp_reference
652                         FROM ".TB_PREF."supp_invoice_items items
653                         LEFT JOIN ".TB_PREF."supp_trans trans ON trans.trans_no=items.supp_trans_no AND trans.`type`=items.supp_trans_type
654                         WHERE items.po_detail_item_id IN (" . implode(',', $po_ids). ")"." AND items.supp_trans_type=20";
655                 $result = db_query($sql, "cannot find source invoice details");
656                 while ($rec = db_fetch($result))
657                 {
658                         $invoices[$rec['trans_no']] = $rec['supp_reference']; // array($rec['reference'], $rec['supp_reference']);
659                 }
660         }
661         return $invoices;
662 }