A couple of minor bugs in tax report when displaying supplier credit notes
[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
14 //--------------------------------------------------------------------------------------------------
15
16 function read_supplier_details_to_trans(&$supp_trans, $supplier_id)
17 {
18         $sql = "SELECT ".TB_PREF."suppliers.supp_name, ".TB_PREF."payment_terms.terms, ".TB_PREF."payment_terms.days_before_due,
19                 ".TB_PREF."payment_terms.day_in_following_month,
20                 ".TB_PREF."suppliers.tax_group_id, ".TB_PREF."tax_groups.name As tax_group_name
21                 From ".TB_PREF."suppliers, ".TB_PREF."payment_terms, ".TB_PREF."tax_groups
22                 WHERE ".TB_PREF."suppliers.tax_group_id = ".TB_PREF."tax_groups.id
23                 AND ".TB_PREF."suppliers.payment_terms=".TB_PREF."payment_terms.terms_indicator
24                 AND ".TB_PREF."suppliers.supplier_id = '" . $supplier_id . "'";
25
26         $result = db_query($sql, "The supplier record selected: " . $supplier_id . " cannot be retrieved");
27
28         $myrow = db_fetch($result);
29
30     $supp_trans->supplier_id = $supplier_id;
31     $supp_trans->supplier_name = $myrow['supp_name'];
32         $supp_trans->terms_description = $myrow['terms'];
33
34         if ($myrow['days_before_due'] == 0)
35         {
36                 $supp_trans->terms = "1" . $myrow['day_in_following_month'];
37         }
38         else
39         {
40                 $supp_trans->terms = "0" . $myrow['days_before_due'];
41         }
42         $supp_trans->tax_description = $myrow['tax_group_name'];
43         $supp_trans->tax_group_id = $myrow['tax_group_id'];
44
45     if ($supp_trans->tran_date == "")
46     {
47                 $supp_trans->tran_date = Today();
48                 if (!is_date_in_fiscalyear($supp_trans->tran_date))
49                         $supp_trans->tran_date = end_fiscalyear();
50         }
51     //if ($supp_trans->due_date=="") {
52     //  get_duedate_from_terms($supp_trans);
53     //}
54     get_duedate_from_terms($supp_trans);
55 }
56
57 //--------------------------------------------------------------------------------------------------
58
59 function update_supp_received_items_for_invoice($id, $po_detail_item, $qty_invoiced, $chg_price=null)
60 {
61         if ($chg_price != null)
62         {
63                 $sql = "SELECT act_price, unit_price FROM ".TB_PREF."purch_order_details WHERE
64                         po_detail_item = $po_detail_item";
65                 $result = db_query($sql, "The old actual price of the purchase order line could not be retrieved");
66                 $row = db_fetch_row($result);
67                 $ret = $row[0];
68
69                 $unit_price = $row[1]; //Added by Rasmus
70
71                 $sql = "SELECT delivery_date FROM ".TB_PREF."grn_batch,".TB_PREF."grn_items WHERE
72                         ".TB_PREF."grn_batch.id = ".TB_PREF."grn_items.grn_batch_id AND ".TB_PREF."grn_items.id=$id";
73                 $result = db_query($sql, "The old delivery date from the received record cout not be retrieved");
74                 $row = db_fetch_row($result);
75                 $date = $row[0];
76         }
77         else
78         {
79                 $ret = 0;
80                 $date = "";
81                 $unit_price = 0; // Added by Rasmus
82         }
83     $sql = "UPDATE ".TB_PREF."purch_order_details
84                 SET qty_invoiced = qty_invoiced + $qty_invoiced ";
85
86         if ($chg_price != null)
87                 $sql .= " , act_price = $chg_price ";
88
89         $sql .= " WHERE po_detail_item = $po_detail_item";
90     db_query($sql, "The quantity invoiced of the purchase order line could not be updated");
91
92     $sql = "UPDATE ".TB_PREF."grn_items
93         SET quantity_inv = quantity_inv + $qty_invoiced
94         WHERE id = $id";
95         db_query($sql, "The quantity invoiced off the items received record could not be updated");
96         return array($ret, $date, $unit_price);
97 }
98
99 function get_deliveries_between($stock_id, $from, $to)
100 {
101         $from = date2sql($from);
102         $to = date2sql($to);
103         $sql = "SELECT SUM(-qty), SUM(-qty*standard_cost) FROM ".TB_PREF."stock_moves
104                 WHERE type=13 AND stock_id='$stock_id' AND
105                         tran_date>='$from' AND tran_date<='$to' GROUP BY stock_id";
106         $result = db_query($sql, "The deliveries could not be updated");
107         return db_fetch_row($result);
108 }
109
110 function get_diff_in_home_currency($supplier, $old_date, $date, $amount1, $amount2)
111 {
112         $currency = get_supplier_currency($supplier);
113         $amount1 = to_home_currency($amount1, $currency, $old_date);
114         $amount2 = to_home_currency($amount2, $currency, $date);
115         return $amount2 - $amount1;
116 }
117 //----------------------------------------------------------------------------------------
118
119 function add_supp_invoice($supp_trans, $invoice_no=0) // do not receive as ref because we change locally
120 {
121         //$company_currency = get_company_currency();
122         /*Start an sql transaction */
123         begin_transaction();
124
125         $tax_total = 0;
126     $taxes = $supp_trans->get_taxes($supp_trans->tax_group_id);
127
128     foreach ($taxes as $taxitem)
129     {
130                 $taxitem['Value'] =  round2($taxitem['Value'], user_price_dec());
131         $tax_total += $taxitem['Value'];
132     }
133
134     $invoice_items_total = $supp_trans->get_total_charged($supp_trans->tax_group_id);
135
136         if ($supp_trans->is_invoice)
137                 $trans_type = 20;
138         else
139         {
140                 $trans_type = 21;
141                 // let's negate everything because it's a credit note
142                 $invoice_items_total = -$invoice_items_total;
143                 $tax_total = -$tax_total;
144                 $supp_trans->ov_discount = -$supp_trans->ov_discount; // this isn't used at all...
145         }
146
147     $date_ = $supp_trans->tran_date;
148         $ex_rate = get_exchange_rate_from_home_currency(get_supplier_currency($supp_trans->supplier_id), $date_);
149
150     /*First insert the invoice into the supp_trans table*/
151         $invoice_id = add_supp_trans($trans_type, $supp_trans->supplier_id, $date_, $supp_trans->due_date,
152                 $supp_trans->reference, $supp_trans->supp_reference,
153                 $invoice_items_total, $tax_total, $supp_trans->ov_discount);
154
155         $total = 0;
156     /* Now the control account */
157     $supplier_accounts = get_supplier_accounts($supp_trans->supplier_id);
158     $total += add_gl_trans_supplier($trans_type, $invoice_id, $date_, $supplier_accounts["payable_account"], 0, 0,
159                 -($invoice_items_total +  $tax_total + $supp_trans->ov_discount),
160                 $supp_trans->supplier_id,
161                 "The general ledger transaction for the control total could not be added");
162
163     /*Loop through the GL Entries and create a debit posting for each of the accounts entered */
164
165     /*the postings here are a little tricky, the logic goes like this:
166     if its a general ledger amount it goes straight to the account specified
167
168     if its a GRN amount invoiced then :
169
170     The cost as originally credited to GRN suspense on arrival of items is debited to GRN suspense. Any difference
171     between the std cost and the currency cost charged as converted at the ex rate of of the invoice is written off
172     to the purchase price variance account applicable to the item being invoiced.
173     */
174     foreach ($supp_trans->gl_codes as $entered_gl_code)
175     {
176
177             /*GL Items are straight forward - just do the debit postings to the GL accounts specified -
178             the credit is to creditors control act  done later for the total invoice value + tax*/
179
180                 if (!$supp_trans->is_invoice)
181                         $entered_gl_code->amount = -$entered_gl_code->amount;
182
183                 $memo_ = $entered_gl_code->memo_;
184                 $total += add_gl_trans_supplier($trans_type, $invoice_id, $date_, $entered_gl_code->gl_code,
185                         $entered_gl_code->gl_dim, $entered_gl_code->gl_dim2, $entered_gl_code->amount, $supp_trans->supplier_id, "", 0, $memo_);
186
187                 add_supp_invoice_gl_item($trans_type, $invoice_id, $entered_gl_code->gl_code,
188                         $entered_gl_code->amount, $memo_);
189
190                 // store tax details if the gl account is a tax account
191                 if (!$supp_trans->is_invoice)
192                         $entered_gl_code->amount = -$entered_gl_code->amount;
193                 add_gl_tax_details($entered_gl_code->gl_code, 
194                         $trans_type, $invoice_id, $entered_gl_code->amount,
195                         $ex_rate, $date_, $supp_trans->supp_reference);
196     }
197     foreach ($supp_trans->grn_items as $entered_grn)
198     {
199
200         if (!$supp_trans->is_invoice)
201         {
202                         $entered_grn->this_quantity_inv = -$entered_grn->this_quantity_inv;
203                         set_grn_item_credited($entered_grn, $supp_trans->supplier_id, $invoice_id, $date_);
204         }
205
206                 $line_taxfree = $entered_grn->taxfree_charge_price($supp_trans->tax_group_id);
207                 $line_tax = $entered_grn->full_charge_price($supp_trans->tax_group_id) - $line_taxfree;
208                 $stock_gl_code = get_stock_gl_code($entered_grn->item_code);
209
210                 $total += add_gl_trans_supplier($trans_type, $invoice_id, $date_, $stock_gl_code["inventory_account"],
211                         $stock_gl_code['dimension_id'], $stock_gl_code['dimension2_id'],
212                         $entered_grn->this_quantity_inv * $line_taxfree, $supp_trans->supplier_id);
213         // -------------- if price changed since po received. 16 Aug 2008 Joe Hunt
214         if($supp_trans->is_invoice)
215         {
216                 $old = update_supp_received_items_for_invoice($entered_grn->id, $entered_grn->po_detail_item,
217                         $entered_grn->this_quantity_inv, $entered_grn->chg_price);
218                         // Since the standard cost is always calculated on basis of the po unit_price,
219                         // this is also the price that should be the base of calculating the price diff.
220                         // In cases where there is two different po invoices on the same delivery with different unit prices this will not work either
221
222                         //$old_price = $old[0];
223                          
224                         $old_price = $old[2];
225
226                         /*
227                         If statement is removed. Should always check for deliveries nomatter if there has been a price change. 
228                         */
229                         //if ($old_price != $entered_grn->chg_price) // price-change, so update
230                         //{
231                         //$diff = $entered_grn->chg_price - $old_price;
232                         $old_date = sql2date($old[1]);
233                         $diff = get_diff_in_home_currency($supp_trans->supplier_id, $old_date, $date_, $old_price, 
234                                 $entered_grn->chg_price);
235                         // always return due to change in currency.
236                         $mat_cost = update_average_material_cost(null, $entered_grn->item_code,
237                                 $diff, $entered_grn->this_quantity_inv, $old_date, true);
238                         // added 2008-12-08 Joe Hunt. Update the purchase data table
239                         add_or_update_purchase_data($supp_trans->supplier_id, $entered_grn->item_code, $entered_grn->chg_price); 
240                         $deliveries = get_deliveries_between($entered_grn->item_code, $old_date, Today()); // extend the period, if invoice is before any deliveries.
241                         if ($deliveries[0] != 0) // have deliveries been done during the period?
242                         {
243                                 $deliveries[1] /= $deliveries[0];
244                                 $amt = ($mat_cost - $deliveries[1]) * $deliveries[0]; // $amt in home currency
245                                 if ($amt != 0.0)
246                                 {
247                                         add_gl_trans($trans_type, $invoice_id, $date_,  $stock_gl_code["cogs_account"],
248                                                 $stock_gl_code['dimension_id'], $stock_gl_code['dimension2_id'], _("Cost diff."),
249                                                 $amt, null, null, null,
250                                                 "The general ledger transaction could not be added for the price variance of the inventory item");
251                                         add_gl_trans($trans_type, $invoice_id, $date_,  $stock_gl_code["inventory_account"],
252                                                 0, 0, _("Cost diff."), -$amt, null, null, null,
253                                                 "The general ledger transaction could not be added for the price variance of the inventory item");
254                                 }               
255                                 update_stock_move_pid(13, $entered_grn->item_code, $old_date, $date_, 0, $mat_cost);
256                         }
257                         update_stock_move_pid(25, $entered_grn->item_code, $old_date, $old_date, $supp_trans->supplier_id, $mat_cost);
258                 //}
259                 }
260         // ----------------------------------------------------------------------
261
262                 add_supp_invoice_item($trans_type, $invoice_id, $entered_grn->item_code,
263                         $entered_grn->item_description, 0,      $line_taxfree, $line_tax,
264                         $entered_grn->this_quantity_inv, $entered_grn->id, $entered_grn->po_detail_item, "");
265     } /* end of GRN postings */
266     /* Now the TAX account */
267     $taxes = $supp_trans->get_taxes($supp_trans->tax_group_id, 0, false); // 2009.08-18 Joe Hunt. We have already got the gl lines
268     foreach ($taxes as $taxitem)
269     {
270         if ($taxitem['Net'] != 0)
271         {
272
273                 if (!$supp_trans->is_invoice)
274                 {
275                         $taxitem['Net'] = -$taxitem['Net'];
276                         $taxitem['Value'] = -$taxitem['Value'];
277                 }       
278                 // here we suppose that tax is never included in price (we are company customer).
279                         add_trans_tax_details($trans_type, $invoice_id, 
280                                 $taxitem['tax_type_id'], $taxitem['rate'], 0, $taxitem['Value'],
281                                 $taxitem['Net'], $ex_rate, $date_, $supp_trans->supp_reference);
282
283                 if (!$supp_trans->is_invoice)
284                         $taxitem['Value'] = -$taxitem['Value'];
285                 $total += add_gl_trans_supplier($trans_type, $invoice_id, $date_,
286                         $taxitem['purchasing_gl_code'], 0, 0, $taxitem['Value'],
287                         $supp_trans->supplier_id,
288                         "A general ledger transaction for the tax amount could not be added");
289         }
290     }
291         
292         /*Post a balance post if $total != 0 */
293         add_gl_balance($trans_type, $invoice_id, $date_, -$total, payment_person_types::supplier(), $supp_trans->supplier_id);  
294
295         add_comments($trans_type, $invoice_id, $date_, $supp_trans->Comments);
296
297         references::save_last($supp_trans->reference, $trans_type);
298
299         if ($invoice_no != 0)
300         {
301                 $invoice_alloc_balance = get_supp_trans_allocation_balance(20, $invoice_no);
302                 if ($invoice_alloc_balance > 0) 
303                 {       //the invoice is not already fully allocated 
304
305                         $trans = get_supp_trans($invoice_no, 20);
306                         $total = $trans['Total'];
307
308                         $allocate_amount = ($invoice_alloc_balance > $total) ? $total : $invoice_alloc_balance;
309                         /*Now insert the allocation record if > 0 */
310                         if ($allocate_amount != 0) 
311                         {
312                                 update_supp_trans_allocation(20, $invoice_no, $allocate_amount);
313                                 update_supp_trans_allocation(21, $invoice_id, $allocate_amount); // ***
314                                 add_supp_allocation($allocate_amount, 21, $invoice_id, 20, $invoice_no,
315                                         $date_);
316                                 // Exchange Variations Joe Hunt 2008-09-20 ////////////////////////////////////////
317
318                                 exchange_variation(21, $invoice_id, 20, $invoice_no, $date_,
319                                         $allocate_amount, payment_person_types::supplier());
320
321                                 ///////////////////////////////////////////////////////////////////////////
322                         }
323                 }
324         }
325         
326
327     commit_transaction();
328
329     return $invoice_id;
330 }
331
332 //----------------------------------------------------------------------------------------
333
334 // get all the invoices/credits for a given PO - quite long route to get there !
335
336 function get_po_invoices_credits($po_number)
337 {
338         $sql = "SELECT DISTINCT ".TB_PREF."supp_trans.trans_no, ".TB_PREF."supp_trans.type,
339                 ov_amount+ov_discount+ov_gst AS Total,
340                 ".TB_PREF."supp_trans.tran_date
341                 FROM ".TB_PREF."supp_trans, ".TB_PREF."supp_invoice_items, ".TB_PREF."purch_order_details, ".TB_PREF."purch_orders
342                 WHERE ".TB_PREF."supp_invoice_items.supp_trans_no = ".TB_PREF."supp_trans.trans_no
343                 AND ".TB_PREF."supp_invoice_items.po_detail_item_id = ".TB_PREF."purch_order_details.po_detail_item
344                 AND ".TB_PREF."purch_orders.supplier_id = ".TB_PREF."supp_trans.supplier_id
345                 AND ".TB_PREF."purch_order_details.order_no = $po_number";
346
347         return db_query($sql, "The invoices/credits for the po $po_number could not be retreived");
348 }
349
350 //----------------------------------------------------------------------------------------
351
352 function read_supp_invoice($trans_no, $trans_type, &$supp_trans)
353 {
354         $sql = "SELECT ".TB_PREF."supp_trans.*, supp_name FROM ".TB_PREF."supp_trans,".TB_PREF."suppliers
355                 WHERE trans_no = $trans_no AND type = $trans_type
356                 AND ".TB_PREF."suppliers.supplier_id=".TB_PREF."supp_trans.supplier_id";
357         $result = db_query($sql, "Cannot retreive a supplier transaction");
358
359         if (db_num_rows($result) == 1)
360         {
361                 $trans_row = db_fetch($result);
362
363                 $supp_trans->supplier_id = $trans_row["supplier_id"];
364                 $supp_trans->supplier_name = $trans_row["supp_name"];
365                 $supp_trans->tran_date = sql2date($trans_row["tran_date"]);
366                 $supp_trans->due_date = sql2date($trans_row["due_date"]);
367                 //$supp_trans->Comments = $trans_row["TransText"];
368                 $supp_trans->Comments = "";
369                 $supp_trans->reference = $trans_row["reference"];
370                 $supp_trans->supp_reference = $trans_row["supp_reference"];
371                 $supp_trans->ov_amount = $trans_row["ov_amount"];
372                 $supp_trans->ov_discount = $trans_row["ov_discount"];
373                 $supp_trans->ov_gst = $trans_row["ov_gst"];
374
375                 $id = $trans_row["trans_no"];
376
377                 $result = get_supp_invoice_items($trans_type, $id);
378
379                 if (db_num_rows($result) > 0)
380                 {
381
382             while ($details_row = db_fetch($result))
383             {
384
385                 if ($details_row["gl_code"] == 0)
386                 {
387                         $supp_trans->add_grn_to_trans($details_row["grn_item_id"], $details_row["po_detail_item_id"], $details_row["stock_id"],
388                                         $details_row["description"], 0, 0, $details_row["quantity"], 0, $details_row["FullUnitPrice"],
389                                         false, 0, 0);
390                 }
391                 else
392                 {
393                         $supp_trans->add_gl_codes_to_trans($details_row["gl_code"], get_gl_account_name($details_row["gl_code"]), 0, 0,
394                                         $details_row["FullUnitPrice"], $details_row["memo_"]);
395                 }
396             }
397         }
398         else
399         {
400                         return display_db_error("Invalid supptrans details for supptrans number : $trans_no and type : $trans_type", $sql, true);
401                 }
402
403         }
404         else
405         {
406                 return display_db_error("Invalid supptrans number : $trans_no and type : $trans_type", $sql, true);
407         }
408 }
409
410 //----------------------------------------------------------------------------------------
411
412 function get_matching_invoice_item($stock_id, $po_item_id)
413 {
414         $sql = "SELECT *, tran_date FROM ".TB_PREF."supp_invoice_items, ".TB_PREF."supp_trans
415                 WHERE supp_trans_type = 20 AND stock_id = '$stock_id' AND po_detail_item_id = $po_item_id
416                 AND supp_trans_no = trans_no";
417         $result = db_query($sql, "Cannot retreive supplier transaction detail records");
418         return db_fetch($result);  
419 }
420
421 function void_supp_invoice($type, $type_no)
422 {
423         begin_transaction();
424
425         $trans = get_supp_trans($type_no, $type);
426
427         void_bank_trans($type, $type_no, true);
428
429         void_gl_trans($type, $type_no, true);
430
431         void_supp_allocations($type, $type_no);
432
433         void_supp_trans($type, $type_no);
434
435         $result = get_supp_invoice_items($type, $type_no);
436
437         // now remove this invoice/credit from any GRNs/POs that it's related to
438         if (db_num_rows($result) > 0)
439         {
440                 $date_ = Today();
441         while ($details_row = db_fetch($result))
442         {
443                 if ((int)$details_row["grn_item_id"] > 0) // it can be empty for GL items
444                 {
445                         // Changed 2008-10-17 by Joe Hunt to get the avg. material cost updated
446                                 $old = update_supp_received_items_for_invoice($details_row["grn_item_id"],
447                                         $details_row["po_detail_item_id"], -$details_row["quantity"], $details_row["FullUnitPrice"]); 
448                                 
449                                 //$diff = $details_row["FullUnitPrice"] - $old[2];
450                                 $old_date = sql2date($old[1]);
451                                 
452                                 $batch = get_grn_batch_from_item($details_row["grn_item_id"]);  
453                                 $grn = get_grn_batch($batch);
454                         if ($type == 21) // credit note 2009-06-14 Joe Hunt Must restore the po and grn
455                         {       // We must get the corresponding invoice item to check for price chg.
456                                         $match = get_matching_invoice_item($details_row["stock_id"], $details_row["po_detail_item_id"]);
457                                         if ($match !== false)
458                                                 $mat_cost = update_average_material_cost($grn["supplier_id"], $details_row["stock_id"],  
459                                                         $match["unit_price"], -$details_row["quantity"], sql2date($match['tran_date']), $match['tran_date'] !== $trans['tran_date']);
460                                         else            
461                                                 $mat_cost = update_average_material_cost($grn["supplier_id"], $details_row["stock_id"],  
462                                                         $details_row["FullUnitPrice"], -$details_row["quantity"], $old_date, $old[1] !== $trans['tran_date']);
463                                         $sql = "UPDATE ".TB_PREF."purch_order_details
464                                         SET quantity_ordered = quantity_ordered + ".-$details_row["quantity"].", ";
465                                 if ($match !== false)
466                                         $sql .= "act_price=".$match['unit_price'].", ";
467                                 $sql .= "quantity_received = quantity_received + ".-$details_row["quantity"]."
468                                     WHERE po_detail_item = ".$details_row["po_detail_item_id"];
469                                         db_query($sql, "a purchase order details record could not be updated. This receipt of goods has not been processed ");
470                                         $sql = "UPDATE ".TB_PREF."grn_items SET qty_recd=qty_recd+".-$details_row["quantity"]."
471                                                 WHERE id=".$details_row["grn_item_id"];
472                                         db_query($sql);
473                         }
474                         else
475                         {
476                                         $diff = get_diff_in_home_currency($grn["supplier_id"], $old_date, sql2date($trans['tran_date']), $old[2], 
477                                                 $details_row["FullUnitPrice"]);
478                                         // Only adjust the avg for the diff
479                                         $mat_cost = update_average_material_cost(null, $details_row["stock_id"],
480                                                 $diff, -$details_row["quantity"], $old_date, true);
481                                 }
482                                 $deliveries = get_deliveries_between($details_row["stock_id"], $old_date, $date_);
483                                 if ($deliveries[0] != 0) // have deliveries been done during the period?
484                                 {
485                                         update_stock_move_pid(13, $details_row["stock_id"], $old_date, $date_, 0, $mat_cost);
486                                 }
487                                 update_stock_move_pid(25, $details_row["stock_id"], $old_date, $old_date, $grn['supplier_id'], $mat_cost);
488                 }
489         }
490         }
491
492         if ($type == 21) // void the credits in stock moves
493                 void_stock_move(21, $type_no); 
494         void_supp_invoice_items($type, $type_no);
495         void_trans_tax_details($type, $type_no);
496
497         commit_transaction();
498 }
499
500 //----------------------------------------------------------------------------------------
501
502
503 ?>