f610356b92d601836259a44ae47849131364cb0b
[fa-stable.git] / manufacturing / includes / db / work_order_costing_db.inc
1 <?php
2
3 /**********************************************************************
4     Copyright (C) FrontAccounting, LLC.
5         Released under the terms of the GNU General Public License, GPL, 
6         as published by the Free Software Foundation, either version 3 
7         of the License, or (at your option) any later version.
8     This program is distributed in the hope that it will be useful,
9     but WITHOUT ANY WARRANTY; without even the implied warranty of
10     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
11     See the License here <http://www.gnu.org/licenses/gpl-3.0.html>.
12 ***********************************************************************/
13
14 function add_wo_costing($workorder_id, $cost_type, $trans_type, $trans_no, $factor=1)
15 {
16         begin_transaction(__FUNCTION__, func_get_args());
17         $sql = "INSERT INTO ".TB_PREF."wo_costing (workorder_id, cost_type, trans_type, trans_no, factor)
18                 VALUES (".db_escape($workorder_id).","
19                 .db_escape($cost_type).",".db_escape($trans_type).","
20                 .db_escape($trans_no).",".db_escape($factor).")";
21
22         db_query($sql, "could not add work order costing");
23         commit_transaction();
24 }
25
26 function get_wo_costing($workorder_id)
27 {
28         $sql="SELECT * 
29                 FROM ".TB_PREF."wo_costing cost,
30                         ".TB_PREF."journal gl
31                 WHERE
32                         cost.trans_type=gl.type AND cost.trans_no=gl.trans_no
33                         AND workorder_id=".db_escape($workorder_id);
34
35         return db_query($sql, "could not get work order costing");
36 }
37
38 function delete_wo_costing($trans_type, $trans_no)
39 {
40         begin_transaction(__FUNCTION__, func_get_args());
41         $sql="DELETE FROM ".TB_PREF."wo_costing WHERE trans_type=".db_escape($trans_type)
42                 ." AND trans_no=".db_escape($trans_no);
43
44         db_query($sql, "could not delete work order costing");
45         commit_transaction();
46 }
47
48 /*
49         Updates average material cost for item.
50 */
51 function update_material_cost($stock_id, $qty, $unit_cost, $date)
52 {
53         global $Refs;
54
55         $item = get_item($stock_id);
56         $avg_cost =  $item['material_cost'];
57
58         $qoh = get_qoh_on_date($stock_id, null, $date);
59
60         // unit_cost*qty is already posted on inventory GL
61 /* 
62         if ($qoh > 0)
63         {
64                  if ($qty<0)
65                         $qty_done = max(-$qoh, $qty);
66                  else
67                         $qty_done = $qty;
68         } else {
69                 if ($qty>0)
70                         $qty_done = max($qoh+$qty, 0);
71                 else
72                         $qty_done = 0;
73         }
74         $qty_delayed = $qty-$qty_done;
75 */
76         if ($qoh < 0)
77         {
78                         $qty_delayed = min(-$qoh, $qty);
79         } else {
80                         $qty_delayed = 0;
81         }
82         $qty_new = $qty - $qty_delayed;
83
84         if ($qty_delayed > 0 && ($unit_cost != $avg_cost))
85         {
86                 // post $qty_delayed*($avg_cost-$unit_cost)
87                 $diff = $qty_delayed*($unit_cost-$avg_cost);
88                 $stock_gl_code = get_item($stock_id);
89
90                 $dec = user_price_dec();
91                 $old_cost = -round2($avg_cost, $dec);
92                 $new_cost = -round2($unit_cost, $dec);
93
94                 $cart = new items_cart(ST_COSTUPDATE);
95                 $cart->tran_date = $cart->doc_date = $cart->event_date = $date;
96
97                 if (!is_date_in_fiscalyear($cart->tran_date))
98                         $cart->tran_date = end_fiscalyear();
99
100                 $cart->reference = $Refs->get_next(ST_COSTUPDATE, null, $cart->tran_date);
101
102                 $cart->memo_ = sprintf(_("COGS changed from %s to %s for %d %s of '%s'"), $avg_cost, $unit_cost, $qty_delayed, $item['units'], $stock_id);
103
104                 $cart->add_gl_item($item["cogs_account"], $item["dimension_id"], $item["dimension2_id"], -$diff);
105                 $cart->add_gl_item($item["inventory_account"], 0, 0, $diff);
106
107                 write_journal_entries($cart);
108         }
109
110         if ($qty > 0 && ($qoh != -$qty))
111             $avg_cost = ($avg_cost*($qoh+$qty_delayed)+$unit_cost*$qty_new)/($qoh+$qty);
112
113         $sql = "UPDATE ".TB_PREF."stock_master SET material_cost=".db_escape($avg_cost)."
114                     WHERE stock_id=".db_escape($stock_id);
115         db_query($sql,"The cost details for the inventory item could not be updated");
116 }
117
118 /*
119         Create journal entry for WO related costs.
120 */
121 function add_wo_costs_journal($wo_id, $amount, $cost_type, $cr_acc, $date, $dim1=0, $dim2=0, $memo = null, $ref= null)
122 {
123 //-------- this should be done by single call to write_journal_entries() using items_cart()
124 //
125         global $Refs, $wo_cost_types;
126
127         begin_transaction(__FUNCTION__, func_get_args());
128
129     $journal_id = get_next_trans_no(ST_JOURNAL);
130
131         $wo = get_work_order($wo_id);
132
133     if (!$ref) $ref = $Refs->get_next(ST_JOURNAL, null, $date);
134
135         add_gl_trans_std_cost(ST_JOURNAL, $journal_id, $date, $cr_acc,
136                 0, 0, $wo_cost_types[$cost_type], -$amount);
137
138         $is_bank_to = is_bank_account($cr_acc);
139         if ($is_bank_to)
140         {
141                 add_bank_trans(ST_JOURNAL, $journal_id, $is_bank_to, "",
142                         $date, -$amount, PT_WORKORDER, $wo_id, get_company_currency());
143         }
144         add_journal(ST_JOURNAL, $journal_id, $amount, $date, get_company_currency(), $ref, '', 1, $date, $date);
145
146         add_gl_trans_std_cost(ST_JOURNAL, $journal_id, $date, $wo['wip_account'],
147                 $dim1, $dim2,  $wo_cost_types[$cost_type], $amount);
148
149         $wo = get_work_order($wo_id);
150         $wip = $wo['wip_account'];
151
152         $Refs->save(ST_JOURNAL, $journal_id, $ref);
153
154         add_wo_costing($wo_id, $cost_type, ST_JOURNAL, $journal_id);
155
156         add_comments(ST_JOURNAL, $journal_id, $date, $memo);
157         add_audit_trail(ST_JOURNAL, $journal_id, $date);
158
159         commit_transaction();
160 }
161
162 /*
163         Process component usage: generate and post stock move, update average component cost.
164 */
165 function work_order_production_gl($woid, $stock_id, $quantity, $date_, $rcv_no)
166 {
167         global $SysPrefs, $path_to_root;
168
169         $memo = "";
170         if ($SysPrefs->loc_notification() == 1)
171         {
172                 include_once($path_to_root . "/sales/includes/cart_class.inc");
173                 include_once($path_to_root . "/inventory/includes/inventory_db.inc");
174                 $st_ids = array();
175                 $st_names = array();
176                 $st_num = array();
177                 $st_reorder = array();
178         }
179         
180         $result = get_wo_requirements($woid);
181
182         // credit all the components
183         $total_cost = 0;
184         while ($bom_item = db_fetch($result))
185         {
186                 if ($SysPrefs->loc_notification() == 1 && is_inventory_item($bom_item['stock_id']))
187                 {
188                         $line = new line_details($bom_item['stock_id'], $bom_item["units_req"] * $quantity, 0, 0, 0, 0, $bom_item['description']);
189                         $loc = calculate_reorder_level($bom_item["loc_code"], $line, $st_ids, $st_names, $st_num, $st_reorder); 
190                 }
191                 update_wo_requirement_issued($bom_item['id'], $bom_item["units_req"] * $quantity, $bom_item["ComponentCost"]);
192
193                 // insert a -ve stock move for each item
194                 add_stock_move(ST_MANURECEIVE, $bom_item["stock_id"], $rcv_no,
195                         $bom_item["loc_code"], $date_, "", -$bom_item["units_req"] * $quantity, $bom_item["ComponentCost"], 0);
196
197                 if (!is_service($bom_item["mb_flag"]))
198                         $ivaccount = $bom_item["inventory_account"];
199                 else
200                         $ivaccount = $bom_item["cogs_account"];
201
202                 $memo = $date_.": ".$bom_item["units_req"] ." * ".$bom_item["description"];
203
204                 $total_cost += add_gl_trans_std_cost(ST_MANURECEIVE, $rcv_no, $date_, $ivaccount, 0, 0,
205                         $memo, -$bom_item["ComponentCost"] * $bom_item["units_req"] * $quantity);
206         }
207
208         $wo = get_work_order($woid);
209     add_gl_trans_std_cost(ST_MANURECEIVE, $rcv_no, $date_, $wo['wip_account'],
210         0, 0, $memo, -$total_cost);
211         if ($SysPrefs->loc_notification() == 1 && count($st_ids) > 0)
212                 send_reorder_email($loc, $st_ids, $st_names, $st_num, $st_reorder);
213 }
214
215 function check_wo_costing($type, $trans_no)
216 {
217         $sql = "SELECT workorder_id FROM ".TB_PREF."wo_costing WHERE trans_type= ".db_escape($type)." AND trans_no=".db_escape($trans_no);
218         $costs = db_query($sql, 'cannot check WO costing');
219
220         if (!db_num_rows($costs))
221                 return 0;
222
223         $wo = db_fetch($costs);
224         return $wo['workorder_id'];
225 }
226
227 function void_wo_costing($wo_id)
228 {
229         global $Refs;
230
231         begin_transaction(__FUNCTION__, func_get_args());
232         $res = get_wo_costing($wo_id);
233
234         while($row = db_fetch($res))
235         {
236                 // void any related gl trans
237                 $date = Today();
238                 $type = $row['trans_type'];
239                 $trans_no = $row['trans_no'];
240                 $memo = sprintf(_("Voided WO #%s"), $wo_id);
241                 void_gl_trans($type, $trans_no, true);
242                 add_audit_trail($type, $trans_no, $date, $memo);
243                 add_voided_entry($type, $trans_no, $date, $memo);
244                 $Refs->restore_last($type, $trans_no);
245         }
246
247         $sql = "DELETE FROM ".TB_PREF."wo_costing WHERE workorder_id=".db_escape($wo_id);
248         db_query($sql, "could not delete work order costing");
249         commit_transaction();
250 }