Overhead and labour cost changed to define standard costs.
[fa-stable.git] / manufacturing / includes / db / work_order_costing_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
13 function add_wo_costing($workorder_id, $cost_type, $trans_type, $trans_no, $factor=1)
14 {
15         $sql = "INSERT INTO ".TB_PREF."wo_costing (workorder_id, cost_type, trans_type, trans_no, factor)
16                 VALUES (".db_escape($workorder_id).","
17                 .db_escape($cost_type).",".db_escape($trans_type).","
18                 .db_escape($trans_no).",".db_escape($factor).")";
19
20         db_query($sql, "could not add work order costing");
21
22 }
23
24 function get_wo_costing($workorder_id)
25 {
26         $sql="SELECT * FROM ".TB_PREF."wo_costing WHERE workorder_id=".db_escape($workorder_id);
27
28         return db_query($sql, "could not get work order costing");
29 }
30
31 function delete_wo_costing($trans_type, $trans_no)
32 {
33         $sql="DELETE FROM ".TB_PREF."wo_costing WHERE trans_type=".db_escape($trans_type)
34                 ." AND trans_no=".db_escape($trans_no);
35
36         db_query($sql, "could not delete work order costing");
37 }
38
39 /*
40         Updates average material cost for item.
41 */
42 function update_material_cost($stock_id, $qty, $unit_cost, $date)
43 {
44         global $Refs;
45
46         $product = get_item($stock_id);
47         $avg_cost =  $product['material_cost'];
48
49         $qoh = get_qoh_on_date($stock_id, null, $date);
50
51         // unit_cost*qty is already posted on inventory GL
52 /* 
53         if ($qoh > 0)
54         {
55                  if ($qty<0)
56                         $qty_done = max(-$qoh, $qty);
57                  else
58                         $qty_done = $qty;
59         } else {
60                 if ($qty>0)
61                         $qty_done = max($qoh+$qty, 0);
62                 else
63                         $qty_done = 0;
64         }
65         $qty_delayed = $qty-$qty_done;
66 */
67         if ($qoh < 0)
68         {
69                         $qty_delayed = min(-$qoh, $qty);
70         } else {
71                         $qty_delayed = 0;
72         }
73         $qty_new = $qty - $qty_delayed;
74
75         if ($qty_delayed > 0 && ($unit_cost != $avg_cost))
76         {
77                 // post $qty_delayed*($avg_cost-$unit_cost)
78                 $diff = $qty_delayed*($unit_cost-$avg_cost);
79                 $stock_gl_code = get_item($stock_id);
80
81                 $dec = user_price_dec();
82                 $old_cost = -round2($avg_cost, $dec);
83                 $new_cost = -round2($unit_cost, $dec);
84
85                 $cart = new items_cart(ST_COSTUPDATE);
86                 $cart->tran_date = $cart->doc_date = $cart->event_date = $date;
87
88                 if (!is_date_in_fiscalyear($cart->tran_date))
89                         $cart->tran_date = end_fiscalyear();
90
91                 $cart->reference = $Refs->get_next(ST_COSTUPDATE, null, $cart->tran_date, $date);
92
93                 $cart->memo_ = sprintf(_("COGS changed from %s to %s for %d %s of '%s'"), $avg_cost, $unit_cost, $qty_delayed, $item['units'], $stock_id);
94
95                 $cart->add_gl_item($item["cogs_account"], $item["dimension_id"], $item["dimension2_id"], -$diff);
96                 $cart->add_gl_item($item["inventory_account"], 0, 0, $diff);
97
98                 write_journal_entries($cart);
99         }
100
101         if ($qty > 0 && ($qoh != -$qty))
102                 $avg_cost = ($avg_cost*($qoh+$qty_delayed)+$unit_cost*$qty_new)/($qoh+$qty);
103
104         $sql = "UPDATE ".TB_PREF."stock_master SET material_cost=".db_escape($avg_cost)."
105                     WHERE stock_id=".db_escape($stock_id);
106         db_query($sql,"The cost details for the inventory item could not be updated");
107 }
108
109 function add_issue_cost($stock_id, $qty, $date_, $costs, $adj_only=false)
110 {
111         if ($qty != 0)
112                 $costs /= $qty;
113         $sql = "SELECT material_cost FROM ".TB_PREF."stock_master WHERE stock_id = "
114                 .db_escape($stock_id);
115         $result = db_query($sql);
116         $myrow = db_fetch($result);
117         $material_cost =  $myrow['material_cost'];
118         $qoh = get_qoh_on_date($stock_id);
119         if ($qoh < 0)
120                 $qoh = 0;
121         if ($adj_only)
122         {
123                 if ($qoh>0)
124                         $material_cost = ($qoh * $material_cost + $costs) / $qoh;
125                 else // Journal Entry if QOH is 0/negative
126                 {
127                         global $Refs;
128
129                         $id = get_next_trans_no(ST_JOURNAL);
130                         $ref = $Refs->get_next(ST_JOURNAL, null, $date_);
131                         add_journal(ST_JOURNAL, $id, $costs, $date_, get_company_currency(), $ref);
132                         
133                         $stock_gl_code = get_stock_gl_code($stock_id);
134                         $memo = "WO Issue settlement JV for zero/negative respository of ".$stock_id;
135                         //Reverse the inventory effect if $qoh <=0
136                         add_gl_trans_std_cost(ST_JOURNAL, $id, $date_, 
137                                 $stock_gl_code["inventory_account"],
138                                 $stock_gl_code['dimension_id'], $stock_gl_code['dimension2_id'], $memo, 
139                                 -$costs);
140                         //GL Posting to inventory adjustment account
141                         add_gl_trans_std_cost(ST_JOURNAL, $id, $date_, 
142                                 $stock_gl_code["assembly_account"],
143                                 $stock_gl_code['dimension_id'], $stock_gl_code['dimension2_id'], $memo,
144                                 $costs);
145                                 
146                         add_audit_trail(ST_JOURNAL, $id, $date_);
147                         add_comments(ST_JOURNAL, $id, $date_, $memo);
148                         $Refs->save(ST_JOURNAL, $id, $ref);     
149                         if ($qty != 0)
150                                 $material_cost = $costs / $qty; 
151                 }
152         }
153         else
154         {
155                 if ($qoh + $qty != 0)   
156                         $material_cost = ($qoh * $material_cost + $qty * $costs) /      ($qoh + $qty);
157         }       
158         $sql = "UPDATE ".TB_PREF."stock_master SET material_cost=material_cost+"
159                 .db_escape($material_cost)
160                 ." WHERE stock_id=".db_escape($stock_id);
161         db_query($sql,"The cost details for the inventory item could not be updated");
162 }
163
164 /*
165         Create journal entry for WO related costs.
166 */
167 function add_wo_costs_journal($wo_id, $amount, $cost_type, $cr_acc, $db_acc, $date, $dim1=0, $dim2=0, $memo = null, $ref= null)
168 {
169 //-------- this should be done by single call to write_journal_entries() using items_cart()
170 //
171         global $Refs, $wo_cost_types;
172
173         begin_transaction();
174
175     $journal_id = get_next_trans_no(ST_JOURNAL);
176
177     if (!$ref) $ref = $Refs->get_next(ST_JOURNAL, null, $date);
178
179         add_gl_trans_std_cost(ST_JOURNAL, $journal_id, $date, $cr_acc,
180                 0, 0, $wo_cost_types[$cost_type], -$amount);
181         $is_bank_to = is_bank_account($cr_acc);
182         if ($is_bank_to)
183         {
184                 add_bank_trans(ST_JOURNAL, $journal_id, $is_bank_to, "",
185                         $date, -$amount, PT_WORKORDER, $wo_id, get_company_currency(),
186                         "Cannot insert a destination bank transaction");
187         } 
188         add_journal(ST_JOURNAL, $journal_id, $amount, $date, get_company_currency(), $ref);
189
190         add_gl_trans_std_cost(ST_JOURNAL, $journal_id, $date, $db_acc,
191                 $dim1, $dim2,  $wo_cost_types[$cost_type], $amount);
192
193         $wip = get_company_pref('wip_act');
194
195         if ($db_acc != $wip)
196         {
197                 add_gl_trans_std_cost(ST_JOURNAL, $journal_id, $date, $db_acc,
198                         0, 0,  $wo_cost_types[$cost_type], -$amount);
199
200                 add_gl_trans_std_cost(ST_JOURNAL, $journal_id, $date, $wip,
201                         0, 0,  $wo_cost_types[$cost_type], $amount);
202         }
203
204         $Refs->save(ST_JOURNAL, $journal_id, $ref);
205
206         add_wo_costing($wo_id, $cost_type, ST_JOURNAL, $journal_id);
207
208         add_comments(ST_JOURNAL, $journal_id, $date, $memo);
209         add_audit_trail(ST_JOURNAL, $journal_id, $date);
210
211         commit_transaction();
212 }
213
214 /*
215         Process component usage: generate and post stock move, update average component cost.
216 */
217 function work_order_production_gl($woid, $stock_id, $quantity, $date_, $rcv_no)
218 {
219         $result = get_wo_requirements($woid);
220
221         // credit all the components
222         $total_cost = 0;
223         while ($bom_item = db_fetch($result))
224         {
225
226                 update_wo_requirement_issued($bom_item['id'], $bom_item["units_req"] * $quantity, $bom_item["ComponentCost"]);
227
228                 // insert a -ve stock move for each item
229                 add_stock_move(ST_MANURECEIVE, $bom_item["stock_id"], $rcv_no,
230                         $bom_item["loc_code"], $date_, "", -$bom_item["units_req"] * $quantity, $bom_item["ComponentCost"], 0);
231
232                 if (!is_service($bom_item["mb_flag"]))
233                         $ivaccount = $bom_item["inventory_account"];
234                 else
235                         $ivaccount = $bom_item["assembly_account"];
236
237                 $memo = $date_.": ".$bom_item["units_req"] ." * ".$bom_item["description"];
238
239                 $total_cost += add_gl_trans_std_cost(ST_MANURECEIVE, $rcv_no, $date_, $ivaccount, 0, 0,
240                         $memo, -$bom_item["ComponentCost"] * $bom_item["units_req"] * $quantity);
241         }
242
243     add_gl_trans_std_cost(ST_MANURECEIVE, $rcv_no, $date_, get_company_pref('wip_act'),
244         0, 0, $memo, -$total_cost);
245 }
246
247 function void_wo_costing($wo_id)
248 {
249         $res = get_wo_costing($wo_id);
250
251         while($row = db_fetch($res))
252         {
253                 // void any related gl trans
254                 void_gl_trans($row['trans_type'], $row['trans_no'], true);
255         }
256
257         $sql = "DELETE FROM ".TB_PREF."wo_costing WHERE workorder_id=".db_escape($wo_id);
258         db_query($sql, "could not delete work order costing");
259 }