Final rewriting of sales module
[fa-stable.git] / sales / customer_invoice.php
1 <?php
2 //---------------------------------------------------------------------------
3 //
4 //      Entry/Modify Sales Invoice against single delivery
5 //      Entry/Modify Batch Sales Invoice against batch of deliveries
6 //
7 $page_security = 2;
8 $path_to_root="..";
9 include_once($path_to_root . "/sales/includes/cart_class.inc");
10 include_once($path_to_root . "/includes/session.inc");
11 include_once($path_to_root . "/includes/data_checks.inc");
12 include_once($path_to_root . "/includes/manufacturing.inc");
13 include_once($path_to_root . "/sales/includes/sales_db.inc");
14 include_once($path_to_root . "/sales/includes/sales_ui.inc");
15 include_once($path_to_root . "/reporting/includes/reporting.inc");
16 include_once($path_to_root . "/taxes/tax_calc.inc");
17
18 $js = "";
19 if ($use_popup_windows) {
20         $js .= get_js_open_window(900, 500);
21 }
22 if ($use_date_picker) {
23         $js .= get_js_date_picker();
24 }
25
26 if (isset($_GET['ModifyInvoice'])) {
27         $_SESSION['page_title'] = sprintf(_("Modifying Sales Invoice # %d.") ,$_GET['ModifyInvoice']);
28         $help_page_title = _("Modifying Sales Invoice");
29 } elseif (isset($_GET['DeliveryNumber'])) {
30         $_SESSION['page_title'] = _("Issue an Invoice for Delivery Note");
31 } elseif (isset($_GET['BatchInvoice'])) {
32         $_SESSION['page_title'] = _("Issue Batch Invoice for Delivery Notes");
33 }
34
35 page($_SESSION['page_title'], false, false, "", $js);
36
37 //-----------------------------------------------------------------------------
38
39 if (isset($_GET['AddedID'])) {
40
41         $invoice_no = $_GET['AddedID'];
42         $trans_type = 10;
43         print_hidden_script(10);
44
45         display_notification(_("Selected deliveries has been processed"), true);
46
47         display_note(get_customer_trans_view_str($trans_type, $invoice_no, _("View This Invoice")), 0, 1);
48
49         display_note(print_document_link($invoice_no, _("Print This Invoice"), true, 10));
50
51         display_note(get_gl_view_str($trans_type, $invoice_no, _("View the GL Journal Entries for this Invoice")),1);
52
53         hyperlink_params("$path_to_root/sales/inquiry/sales_deliveries_view.php", _("Select Another Delivery For Invoicing"), "OutstandingOnly=1");
54
55         display_footer_exit();
56
57 } elseif (isset($_GET['UpdatedID']))  {
58
59         $invoice_no = $_GET['UpdatedID'];
60         print_hidden_script(10);
61
62         display_notification_centered(sprintf(_('Sales Invoice # %d has been updated.'),$invoice_no));
63
64         display_note(get_trans_view_str(10, $invoice_no, _("View This Invoice")));
65         echo '<br>';
66         display_note(print_document_link($invoice_no, _("Print This Invoice"), true, 10));
67
68         hyperlink_no_params($path_to_root . "/sales/inquiry/customer_inquiry.php", _("Select A Different Invoice to Modify"));
69
70         display_footer_exit();
71
72 } elseif (isset($_GET['RemoveDN'])) {
73
74         for($line_no = 0; $line_no < count($_SESSION['Items']->line_items); $line_no++) {
75                 $line = &$_SESSION['Items']->line_items[$line_no];
76                 if ($line->src_no == $_GET['RemoveDN']) {
77                         $line->quantity = $line->qty_done;
78                         $line->qty_dispatched=0;
79                 }
80         }
81         unset($line);
82 }
83
84 //-----------------------------------------------------------------------------
85
86 if ( (isset($_GET['DeliveryNumber']) && ($_GET['DeliveryNumber'] > 0) )
87         || isset($_GET['BatchInvoice'])) {
88
89         processing_start();
90
91         if (isset($_GET['BatchInvoice'])) {
92                 $src = $_SESSION['DeliveryBatch'];
93                 unset($_SESSION['DeliveryBatch']);
94         } else {
95                 $src = array($_GET['DeliveryNumber']);
96         }
97         /*read in all the selected deliveries into the Items cart  */
98         $dn = new Cart(13, $src, true);
99
100         if ($dn->count_items() == 0) {
101                 hyperlink_params($path_to_root . "/sales/inquiry/sales_deliveries_view.php",
102                         _("Select a different delivery to invoice"), "OutstandingOnly=1");
103                 die ("<br><b>" . _("There are no delivered items with a quantity left to invoice. There is nothing left to invoice.") . "</b>");
104         }
105
106         $dn->trans_type = 10;
107         $dn->src_docs = $dn->trans_no;
108         $dn->trans_no = 0;
109         $dn->reference = references::get_next(10);
110
111         $_SESSION['Items'] = $dn;
112         copy_from_cart();
113
114 } elseif (isset($_GET['ModifyInvoice']) && $_GET['ModifyInvoice'] > 0) {
115
116         processing_start();
117         $_SESSION['Items'] = new Cart(10, $_GET['ModifyInvoice']);
118
119         if ($_SESSION['Items']->count_items() == 0) {
120                 echo"<center><br><b>" . _("All quantities on this invoice has been credited. There is nothing to modify on this invoice") . "</b></center>";
121                 display_footer_exit();
122         }
123         copy_from_cart();
124 } elseif (!processing_active()) {
125         /* This page can only be called with a delivery for invoicing or invoice no for edit */
126         display_error(_("This page can only be opened after delivery selection. Please select delivery to invoicing first."));
127
128         hyperlink_no_params("$path_to_root/sales/inquiry/sales_deliveries_view.php", _("Select Delivery to Invoice"));
129
130         end_page();
131         exit;
132 } else {
133         foreach ($_SESSION['Items']->line_items as $line_no=>$itm) {
134                 if (isset($_POST['Line'.$line_no])) {
135                         $line_qty = $_POST['Line'.$line_no];
136                         if (is_numeric($line_qty) && $_POST['Line'.$line_no] <= ($itm->quantity - $itm->qty_done)) {
137                                 $_SESSION['Items']->line_items[$line_no]->qty_dispatched = $line_qty;
138                         }
139                 }
140
141                 if (isset($_POST['Line'.$line_no.'Desc'])) {
142                         $line_desc = $_POST['Line'.$line_no.'Desc'];
143                         if (strlen($line_desc) > 0) {
144                                 $_SESSION['Items']->line_items[$line_no]->item_description = $line_desc;
145                         }
146                 }
147         }
148 }
149 //-----------------------------------------------------------------------------
150
151 function copy_to_cart()
152 {
153         $cart = &$_SESSION['Items'];
154         $cart->ship_via = $_POST['ship_via'];
155         $cart->freight_cost = $_POST['ChargeFreightCost'];
156         $cart->document_date =  $_POST['InvoiceDate'];
157         $cart->due_date =  $_POST['due_date'];
158         $cart->Comments = $_POST['Comments'];
159 }
160 //-----------------------------------------------------------------------------
161
162 function copy_from_cart()
163 {
164         $cart = &$_SESSION['Items'];
165         $_POST['ship_via'] = $cart->ship_via;
166         $_POST['ChargeFreightCost'] = $cart->freight_cost;
167         $_POST['InvoiceDate']= $cart->document_date;
168         $_POST['due_date'] = $cart->due_date;
169         $_POST['Comments']= $cart->Comments;
170 }
171
172 //-----------------------------------------------------------------------------
173
174 function check_data()
175 {
176         if (!isset($_POST['InvoiceDate']) || !is_date($_POST['InvoiceDate'])) {
177                 display_error(_("The entered invoice date is invalid."));
178                 return false;
179         }
180
181         if (!is_date_in_fiscalyear($_POST['InvoiceDate'])) {
182                 display_error(_("The entered invoice date is not in fiscal year."));
183                 return false;
184         }
185
186         if (!isset($_POST['due_date']) || !is_date($_POST['due_date'])) {
187                 display_error(_("The entered invoice due date is invalid."));
188                 return false;
189         }
190
191         if ($_SESSION['Items']->trans_no == 0) {
192                 if (!references::is_valid($_POST['ref'])) {
193                         display_error(_("You must enter a reference."));
194                         return false;
195                 }
196
197                 if (!is_new_reference($_POST['ref'], 10)) {
198                         display_error(_("The entered reference is already in use."));
199                         return false;
200                 }
201         }
202
203         if ($_POST['ChargeFreightCost'] == "") {
204                 $_POST['ChargeFreightCost'] = 0;
205         }
206
207         if (!is_numeric($_POST['ChargeFreightCost']) || $_POST['ChargeFreightCost'] < 0) {
208                 display_error(_("The entered shipping value is not numeric."));
209                 return false;
210         }
211
212         if ($_SESSION['Items']->has_items_dispatch() == 0 && $_POST['ChargeFreightCost'] == 0) {
213                 display_error(_("There are no item quantities on this invoice."));
214                 return false;
215         }
216
217         return true;
218 }
219
220 //-----------------------------------------------------------------------------
221 if (isset($_POST['process_invoice']) && check_data()) {
222
223         $newinvoice=  $_SESSION['Items']->trans_no == 0;
224         copy_to_cart();
225         $invoice_no = $_SESSION['Items']->write();
226
227         processing_end();
228         if ($newinvoice) {
229                 meta_forward($_SERVER['PHP_SELF'], "AddedID=$invoice_no");
230         } else {
231                 meta_forward($_SERVER['PHP_SELF'], "UpdatedID=$invoice_no");
232         }
233 }
234
235 // find delivery spans for batch invoice display
236 $dspans = array();
237 $lastdn = ''; $spanlen=1;
238
239 for ($line_no = 0; $line_no < count($_SESSION['Items']->line_items); $line_no++) {
240         $line = $_SESSION['Items']->line_items[$line_no];
241         if ($line->quantity == $line->qty_done) {
242                 continue;
243         }
244         if ($line->src_no == $lastdn) {
245                 $spanlen++;
246         } else {
247                 if ($lastdn != '') {
248                         $dspans[] = $spanlen;
249                         $spanlen = 1;
250                 }
251         }
252         $lastdn = $line->src_no;
253 }
254 $dspans[] = $spanlen;
255
256 //-----------------------------------------------------------------------------
257
258 $is_batch_invoice = count($_SESSION['Items']->src_docs) > 1;
259 $is_edition = $_SESSION['Items']->trans_type == 10 && $_SESSION['Items']->trans_no != 0;
260 start_form(false, true);
261
262 start_table("$table_style2 width=80%", 5);
263
264 start_row();
265 label_cells(_("Customer"), $_SESSION['Items']->customer_name, "class='tableheader2'");
266 label_cells(_("Branch"), get_branch_name($_SESSION['Items']->Branch), "class='tableheader2'");
267 label_cells(_("Currency"), $_SESSION['Items']->customer_currency, "class='tableheader2'");
268 end_row();
269 start_row();
270
271 if ($_SESSION['Items']->trans_no == 0) {
272         ref_cells(_("Reference"), 'ref', $_SESSION['Items']->reference, "class='tableheader2'");
273 } else {
274         label_cells(_("Reference"), $_SESSION['Items']->reference, "class='tableheader2'");
275 }
276
277 label_cells(_("Delivery Notes:"),
278 get_customer_trans_view_str(systypes::cust_dispatch(), array_keys($_SESSION['Items']->src_docs)), "class='tableheader2'");
279
280 if (!isset($_POST['sales_type_id'])) {
281         $_POST['sales_type_id'] = $_SESSION['Items']->default_sales_type;
282 }
283
284 label_cell(_("Sales Type"), "class='tableheader2'");
285 sales_types_list_cells(null, 'sales_type_id', $_POST['sales_type_id']);
286
287 end_row();
288 start_row();
289
290 if (!isset($_POST['ship_via'])) {
291         $_POST['ship_via'] = $_SESSION['Items']->ship_via;
292 }
293 label_cell(_("Shipping Company"), "class='tableheader2'");
294 shippers_list_cells(null, 'ship_via', $_POST['ship_via']);
295
296 if (!isset($_POST['InvoiceDate']) || !is_date($_POST['InvoiceDate'])) {
297         $_POST['InvoiceDate'] = Today();
298         if (!is_date_in_fiscalyear($_POST['InvoiceDate'])) {
299                 $_POST['InvoiceDate'] = end_fiscalyear();
300         }
301 }
302
303 date_cells(_("Date"), 'InvoiceDate', $_POST['InvoiceDate'], 0, 0, 0, "class='tableheader'");
304
305 if (!isset($_POST['due_date']) || !is_date($_POST['due_date'])) {
306         $_POST['due_date'] = get_invoice_duedate($_SESSION['Items']->customer_id, $_POST['InvoiceDate']);
307 }
308
309 date_cells(_("Due Date"), 'due_date', $_POST['due_date'], 0, 0, 0, "class='tableheader'");
310
311 end_row();
312 end_table();
313
314 display_heading(_("Invoice Items"));
315
316 start_table("$table_style width=80%");
317 $th = array(_("Item Code"), _("Item Description"), _("Delivered"), _("Units"), _("Invoiced"),
318         _("This Invoice"), _("Price"), _("Tax Type"), _("Discount"), _("Total"));
319
320 if ($is_batch_invoice) {
321 $th[] = _("DN");
322 $th[] = "";
323 }
324
325 if ($is_edition) {
326 $th[4] = _("Credited");
327 }
328
329 table_header($th);
330 $k = 0;
331 $has_marked = false;
332 $show_qoh = true;
333
334 $dn_line_cnt = 0;
335
336 foreach ($_SESSION['Items']->line_items as $line=>$ln_itm) {
337         if ($ln_itm->quantity == $ln_itm->qty_done) {
338                 continue; // this line was fully invoiced
339         }
340         alt_table_row_color($k);
341         view_stock_status_cell($ln_itm->stock_id);
342
343         text_cells(null, 'Line'.$line.'Desc', $ln_itm->item_description, 30, 50);
344         qty_cell($ln_itm->quantity);
345         label_cell($ln_itm->units);
346         qty_cell($ln_itm->qty_done);
347
348         if ($is_batch_invoice) {
349                 // for batch invoices we can only remove whole deliveries
350                 echo '<td nowrap align=right>';
351                 hidden('Line' . $line, $ln_itm->qty_dispatched );
352                 echo number_format2($ln_itm->qty_dispatched, user_qty_dec()).'</td>';
353         } else {
354                 text_cells(null, 'Line'.$line, $ln_itm->qty_dispatched, 10, 10);
355         }
356         $display_discount_percent = number_format2($ln_itm->discount_percent*100, user_percent_dec()) . "%";
357
358         $line_total = ($ln_itm->qty_dispatched * $ln_itm->price * (1 - $ln_itm->discount_percent));
359
360         amount_cell($ln_itm->price);
361         label_cell($ln_itm->tax_type_name);
362         label_cell($display_discount_percent, "nowrap align=right");
363         amount_cell($line_total);
364
365         if ($is_batch_invoice) {
366                 if ($dn_line_cnt == 0) {
367                         $dn_line_cnt = $dspans[0];
368                         $dspans = array_slice($dspans, 1);
369                         label_cell($ln_itm->src_no, "rowspan=$dn_line_cnt class=oddrow");
370                         label_cell("<a href='" . $_SERVER['PHP_SELF'] . "?RemoveDN=".
371                                 $ln_itm->src_no."'>" . _("Remove") . "</a>", "rowspan=$dn_line_cnt class=oddrow");
372                 }
373                 $dn_line_cnt--;
374         }
375         end_row();
376 }
377
378 /*Don't re-calculate freight if some of the order has already been delivered -
379 depending on the business logic required this condition may not be required.
380 It seems unfair to charge the customer twice for freight if the order
381 was not fully delivered the first time ?? */
382
383 if (!isset($_POST['ChargeFreightCost']) || $_POST['ChargeFreightCost'] == "") {
384         if ($_SESSION['Items']->any_already_delivered() == 1) {
385                 $_POST['ChargeFreightCost'] = 0;
386         } else {
387                 $_POST['ChargeFreightCost'] = $_SESSION['Items']->freight_cost;
388         }
389
390         if (!is_numeric($_POST['ChargeFreightCost'])) {
391                 $_POST['ChargeFreightCost'] = 0;
392         }
393 }
394
395 start_row();
396
397 small_amount_cells(_("Shipping Cost"), 'ChargeFreightCost', null, "colspan=9 align=right");
398 if ($is_batch_invoice) {
399 label_cell('', 'colspan=2');
400 }
401
402 end_row();
403 $inv_items_total = $_SESSION['Items']->get_items_total_dispatch();
404
405 $display_sub_total = number_format2($inv_items_total + $_POST['ChargeFreightCost'],user_price_dec());
406
407 label_row(_("Sub-total"), $display_sub_total, "colspan=9 align=right","align=right", $is_batch_invoice ? 2 : 0);
408
409 $taxes = $_SESSION['Items']->get_taxes($_POST['ChargeFreightCost']);
410 $tax_total = display_edit_tax_items($taxes, 9, $_SESSION['Items']->tax_included, $is_batch_invoice ? 2:0);
411
412 $display_total = number_format2(($inv_items_total + $_POST['ChargeFreightCost'] + $tax_total), user_price_dec());
413
414 label_row(_("Invoice Total"), $display_total, "colspan=9 align=right","align=right", $is_batch_invoice ? 2 : 0);
415
416 end_table(1);
417
418 start_table($table_style2);
419
420 textarea_row(_("Memo"), 'Comments', null, 50, 4);
421
422 end_table(1);
423
424 submit_center_first('Update', _("Update"));
425 submit_center_last('process_invoice', _("Process Invoice"));
426
427 end_form();
428
429 end_page();
430
431 ?>