3 require_once 'Pman/Roo.php';
5 class Pman_Xtuple_Import_Magento extends Pman_Roo
10 if (HTML_FlexyFramework::get()->cli) {
11 $this->authUser = DB_DataObject::Factory('Person');
12 $this->authUser->get('email','brian@bloomandgrowdirect.com');
15 return parent::getAuth();
22 ini_set('memory_limit', '1024M');
23 $this->sessionState(0); // turn off session..
24 //$this->transObj = DB_DataObject::Factory('coitem');
26 // $this->transObj->query('BEGIN');
28 PEAR::setErrorHandling(PEAR_ERROR_CALLBACK, array($this, 'onPearError'));
30 $this->timer = microtime(true);
32 require_once 'File/Convert.php';
34 $this->debugLog('reading /tmp/magento.csv');
35 $fc = new File_Convert('/tmp/magento.csv', 'text/csv');
36 $csv = $fc->convert('text/csv');
37 $this->debugLog('running import CSV');
38 $this->importCsv($csv);
43 function debugLog($str)
45 if (!HTML_FlexyFramework::get()->cli) {
48 static $start = false;
51 $start = microtime(true);
53 $timed = microtime(true) - $start;
56 echo number_format($timed/60 ,2, '.',','). 'm MEM:' . number_format(memory_get_usage() /1000000,0, '.',','). 'M : '. $str . "\n";
63 ini_set('memory_limit', '512M');
65 $this->timer = microtime(true);
67 $this->sessionState(0); // turn off session..
68 //$this->transObj = DB_DataObject::Factory('coitem');
70 // $this->transObj->query('BEGIN');
72 PEAR::setErrorHandling(PEAR_ERROR_CALLBACK, array($this, 'onPearError'));
74 $img = DB_DataObject::Factory('images');
77 'ontable' => 'ipshead'
79 $img->onUpload(false);
81 require_once 'File/Convert.php';
82 $fc = new File_Convert($img->getStoreName(), $img->mimetype );
83 $csv = $fc->convert('text/csv');
84 $this->importCsv($csv);
87 function importCsv($content)
89 ini_set("auto_detect_line_endings", true);
91 $this->debugLog('reading file');
93 $fh = fopen($content, 'r');
96 $this->jerr("invalid content file");
100 'ORDER ID', 'CREATION DATE', 'BILLING NAME',
101 'CUSTOMER EMAIL', 'SHIPPING NAME', 'SHIPPING ADDRESS',
102 'SHIPPING CITY', 'SHIPPING STATE', 'SHIPPING POSTCODE',
103 'SHIPPING COUNTRY', 'SHIPPING TELEPHONE', 'SHIPPING METHOD',
104 'DELIVERY NOTE', 'SIGNATURE REQUIRED?', 'TOTAL WEIGHT',
105 'CUSTOMER BALANCE', 'SHIPPING', 'GRAND TOTAL', 'GIFT CARD',
106 'TAX AMOUNT', 'SKU', 'PRODUCT NAME',
107 'QTY', 'UNIT PRICE', 'SUBTOTAL',
108 'DISCOUNT', 'DISCOUNTED TOTAL', 'PAYMENT METHOD',
109 'GIFT WRAP ID', 'COUPON CODE', 'CUSTOMER GROUP'
118 $c = DB_DataObject::factory('custinfo');
120 cust_number = 'MGCREDITCARD'
122 cust_number= 'MGPAYPAL'
124 cust_number = 'MGDEPOSIT'
127 $customers = $c->fetchAll('cust_id', 'cust_number');
129 if(count(array_values($customers)) != 3){
130 $this->jerr("error occur on getting MGCREDITCARD, MGPAYPAL and MGDEPOSIT customers". print_R($customers,true));
133 foreach($customers as $id=>$n) {
137 $this->errmsg = array();
139 $no_lines_in_file= count(file($content)) ;
141 $no_split = $no_lines_in_file > 500 ? false : true; // small file just group by customers.. big file group by the first two char of the order number!
143 if (!HTML_FlexyFramework::get()->cli && !$no_split && $no_lines_in_file > 2500) {
144 $this->jerr("file is too large to upload - please send to admin to import ($no_lines_in_file)");
151 while(false !== ($n = fgetcsv($fh,10000, ',', '"'))) {
153 if (!strlen(implode('', $n))) {
159 $cols[] = strtoupper(trim($k));
165 foreach($reqs as $req) {
166 if (!in_array($req,$cols)) {
174 foreach ($cols as $k => $c){
176 if (empty($c) || !strlen(trim($c))) {
180 if($c == 'ORDER ID'){
185 $r[$c] = substr($n[$k], -4) * 10000 + $row;
189 if($c == 'CREATION DATE'){
190 $r[$c] = date('Y-m-d',strtotime(str_replace('/', '-', $n[$k])));
193 if (!isset($n[$k])) {
194 $this->jerr("Error on line $lnum - missing data in column $c");
200 $od = $r['CREATION DATE'];
202 if ($od != $r['CREATION DATE']) {
203 $this->jerr("the file contains orders for multiple days first was $od, found {$r['CREATION DATE']}
204 - either download again, or remove the extra lines");
207 if (empty($r['QTY']) || (floor($r['QTY']) != $r['QTY'])) {
208 $this->jerr("Error on line $lnum - {$r['QTY']} is not a valid quantity");
213 $index = $no_split ? 1 : floor($r['ORDER ID'] / 1000000);
215 if($r['PAYMENT METHOD'] == 'creditcard' || $r['PAYMENT METHOD'] == 'free' || $r['PAYMENT METHOD'] == 'secureframe'){
216 $rows['MGCREDITCARD'][$index][] = $r;
221 if($r['PAYMENT METHOD'] == 'paypal_express'){
222 $rows['MGPAYPAL'][$index][] = $r;
225 if($r['PAYMENT METHOD'] == 'paypal_standard'){
226 $rows['MGPAYPAL'][$index][] = $r;
229 if($r['PAYMENT METHOD'] == 'directdeposit_au'){
230 $rows['MGDEPOSIT'][$index][] = $r;
233 $this->jerr("invalid payment type: {$r['PAYMENT METHOD']} on line $lnum");
240 $this->jerr("could not find a row with " . implode(' / ', $req));
246 foreach($customers as $cid => $cname) {
249 if (empty($rows[$cname])) {
253 foreach ($rows[$cname] as $k => $v){
254 $this->transObj = DB_DataObject::Factory('cohead');
255 $this->transObj->query('BEGIN');
256 $this->transObj->lockTables();
257 list( $ltotal, $lbilled) = $this->createOrder($cid, $cname, $v, ($no_split) ? '' : ($k));
259 if (!empty($this->errmsg)) {
260 $this->jerr(implode("\n", $this->errmsg));
263 $this->transObj->query('COMMIT');
264 $this->transObj = false;
266 $GLOBALS['_DB_DATAOBJECT']['RESULTS'] = array();
267 $GLOBALS['_DB_DATAOBJECT']['RESULTFIELDS'] = array();
276 $msg = empty($this->ok_message) ? 'data imported successfully' : $this->ok_message;
278 $this->addEvent('MAGENTO', false, $msg);
280 $this->jok($msg . ' TOTAL: '. number_format($total,2) . ' BILLED: ' .number_format($billed, 2) . ' Time:' . (microtime(true) - $this->timer) . ' seconds' );
284 function createOrder($cust_id, $cust_name, $rows, $suffix = '')
287 $this->debugLog("creating order $cust_name - $suffix");
288 $customer = DB_DataObject::Factory('custinfo');
289 $customer->get($cust_id);
290 $location_id = DB_DataObject::Factory('location')->defaultByItemsite()->itemsite_location_id;
293 $this->warning[] = "No products sold for " . $cust_name;
297 $order_date = $rows[0]['CREATION DATE'];
300 'MGCREDITCARD' => 'MGC',
302 'MGDEPOSIT' => 'MGD',
307 static $oldvoid = array();
308 $nox = $prefix[$cust_name] . str_replace('-', '', $order_date);
309 if (!in_array($nox, $oldvoid)) {
310 $this->debugLog('void the non-x sales order');
312 $co = DB_DataObject::factory('cohead');
313 if($co->get('cohead_number',$nox)){
314 $this->voidInvAndShipments($co);
320 $suffix = 'x'.$suffix;
323 $cohead_number = $prefix[$cust_name] . str_replace('-', '', $order_date) . $suffix;
325 $co = DB_DataObject::factory('cohead');
327 if($co->get('cohead_number',$cohead_number)){
329 $this->voidInvAndShipments($co);
333 $this->debugLog('creating new head');
334 // create cntct if not exists
336 'cntct_first_name' => $customer->cust_name,
337 'cust_id' => $cust_id,
339 'cntct_name' => $customer->cust_name
341 // simple address for customer..
344 'addr_line1' => 'Address for ' . $customer->cust_name . ' account'
346 $cn = $this->checkcntctAndaddr($cntct, $addr);
349 $this->jerr('error occur on getting the contact of ' . $customer->cust_name);
351 $sr = $this->authUser->salesrep();
353 $co = DB_DataObject::factory('cohead');
356 'cohead_cust_id' => $cust_id,
357 'cohead_number' => $cohead_number,
358 'cohead_cust_id_cust_name' => $cust_name,
359 'cohead_orderdate' => $order_date,
360 'cohead_targetdate' => $order_date,
361 'cohead_location_src' => $location_id,
362 'cohead_terms_id' => $customer->cust_terms_id,
363 'cohead_salesrep_id' => empty($sr->salesrep_id) ? $customer->cust_salesrep_id : $sr->salesrep_id,
364 'cohead_billto_cntct_id' => $cn->pid(),
365 'cohead_shipto_cntct_id' => $cn->pid(),
366 'cohead_curr_id' => $customer->cust_curr_id,
367 'cohead_taxzone_id' => $customer->cust_taxzone_id
371 foreach($co->defaults() as $k=>$v) {
372 if (!isset($co->$k)) {
379 $co->updateAddress();
385 $this->debugLog('adding lines');
389 $billitems = array();
390 $invoice_total = 0.0;
391 $toShipment = array();
394 $this->debugLog("number of items " . count($rows));
396 foreach ($rows as $r){
398 // do we have the itme??
399 $i = DB_DataObject::factory('item')->lookupSKU($r['SKU']);
401 $this->errmsg[] = $r['SKU'] . ' not found';
405 // delete the old line numbers.. AGHH !!! = this has been destroying data!!! (it did not have coitem_cohead_id before..)
406 $coitem = DB_DataObject::factory('coitem');
407 $coitem->coitem_cohead_id = $co->pid();
409 if($coitem->get('coitem_linenumber', $r['ORDER ID'])){ // coitem not exist
413 $coitem = DB_DataObject::factory('coitem');
416 $itemsite = $i->itemsite();
418 $this->jerr("product {$i->item_number} has not been created correctly - missing itemsite");
421 $coitem_custprice = round(($r['UNIT PRICE']) * (10 / 11) ,3); // take out the tax
423 // grand total = unit price + discount + shipping -customer balance - gift card
425 $discount_rate = round((abs(
427 ($r['GRAND TOTAL'] - $r['SHIPPING'] - $r['DISCOUNT'] +
428 $r['CUSTOMER BALANCE'] + $r['GIFT CARD'])
429 ), 3); // discount rate
431 $coitem_price = round(($coitem_custprice * (1 - $discount_rate)), 3);
434 $coitem->setFrom(array(
435 'coitem_cohead_id' => $co->pid(),
436 'coitem_linenumber' => $r['ORDER ID'],
437 'coitem_itemsite_id' => $itemsite->pid(),
438 'coitem_qtyord' => $r['QTY'],
439 'coitem_price' => $coitem_price,
440 'coitem_custprice' => $coitem_custprice,
441 'coitem_qtyshipped' => 0,
442 'coitem_location_src' => $co->cohead_location_src,
443 'coitem_shipto_id' => $co->cohead_shipto_id, // $shipto,
444 'coitem_scheddate' => $r['CREATION DATE']
446 foreach($coitem->defaults() as $k=>$v) {
447 if (empty($coitem->$k)) {
454 if(!$coitem->coitem_id){
455 $this->errmsg[] = $r['SKU'] . ' error occur on insert';
459 $toShipment[$coitem->pid()] = $coitem;
461 if(substr($r['ORDER ID'],-4,4) == 1){
463 $total += ($r['GRAND TOTAL'] + $r['CUSTOMER BALANCE'] + $r['GIFT CARD']) ; // with tax
465 // if ( $r['PAYMENT METHOD'] == 'free' ) {
466 // $r['SHIPPING'] = 0.0;
469 $delivery += round(($r['SHIPPING'] * (10 / 11)), 3); // without tax
471 $gift += round((($r['CUSTOMER BALANCE'] + $r['GIFT CARD']) * (10 / 11)), 3); // without tax
475 'cobill_coitem_id' => $coitem->pid(),
476 'cobill_qty' => $coitem->coitem_qtyord
479 $billitems[] = (object)$x;
481 $invoice_total += round($coitem->coitem_qtyord * $coitem->coitem_price, 3);
487 if (!empty($this->errmsg)) {
491 // last row - create shipment...
493 if(count($toShipment)){
495 'curr' => $co->cohead_curr_id
497 $this->orderShipments($toShipment,$d);
501 $this->debugLog('sorting out discounts');
503 $coitem = DB_DataObject::factory('coitem');
504 $coitem->get('coitem_cohead_id', $co->pid());
505 if($coitem->find(true)){
506 $coitem->updatePretaxDiscount($this);
510 $this->debugLog('sorting out delivery');
512 $i = dB_DataObject::Factory('item');
513 if (!$i->get('item_number', 'Z-DELIVERY CHARGE')) {
514 $this->jerr("delivery charge not found");
516 $itemsite = $i->itemsite();
518 // add z-Delivery Charge coz it's taxable
519 $coitem = DB_DataObject::factory('coitem');
520 $coitem->coitem_cohead_id = $co->pid();
521 $coitem->coitem_itemsite_id = $itemsite->pid();
522 $coitem->coitem_linenumber = 9999998;
523 if (!$coitem->find(true)) {
525 $coitem->setFrom(array(
526 'coitem_cohead_id' => $co->pid(),
527 'coitem_linenumber' => 9999998, // need a line number..
528 'coitem_itemsite_id' => $coitem->coitem_itemsite_id,
529 'coitem_qtyord' => 1,
530 'coitem_price' => round($delivery,3),
531 'coitem_custprice' => round($delivery,3),
532 'coitem_qtyshipped' => 0,
533 'coitem_location_src' => $co->cohead_location_src,
534 'coitem_shipto_id' => $co->cohead_shipto_id, // $shipto,
535 'coitem_scheddate' => $order_date
538 foreach($coitem->defaults() as $k=>$v) {
539 if (empty($coitem->$k)) {
545 $co_old = clone($coitem);
546 $coitem->setFrom(array(
547 'coitem_price' => round($delivery,3),
548 'coitem_custprice' => round($delivery,3),
549 'coitem_location_src' => $co->cohead_location_src,
550 'coitem_shipto_id' => $co->cohead_shipto_id, // $shipto,
551 'coitem_scheddate' => $order_date
553 $coitem->update($co_old);
557 // add freigth total to invoice..
558 $invoice_total += round($delivery,3) ;
561 'cobill_coitem_id' => $coitem->pid(),
562 'cobill_qty' => $coitem->coitem_qtyord
565 $billitems[] = (object)$x;
571 $i = DB_DataObject::Factory('item');
572 if (!$i->get('item_number', 'Z-LIST-DISCOUNT')) {
573 $this->jerr("Z-LIST-DISCOUNT not found");
575 $itemsite = $i->itemsite();
577 $coitem = DB_DataObject::factory('coitem');
578 $coitem->coitem_cohead_id = $co->pid();
579 $coitem->coitem_itemsite_id = $itemsite->pid();
580 if($coitem->find(true)){
582 'cobill_coitem_id' => $coitem->pid(),
583 'cobill_qty' => $coitem->coitem_qtyord
586 $billitems[] = (object)$x;
587 $invoice_total = $invoice_total + round(($coitem->coitem_qtyord * $coitem->coitem_price), 3);
589 $co_old = clone ($co);
591 'cohead_misc' => $coitem->coitem_qtyord * $coitem->coitem_price * -1,
592 'cohead_misc_descript' => 'DISCOUNT'
594 $co->update($co_old);
598 $cobapply_list = array();
600 if($gift > 0){ // check credit memo
602 $cmhead = DB_DataObject::factory('cmhead');
603 $cmhead->setFrom(array(
604 'cmhead_number' => $cmhead->nextNumber(),
605 'cmhead_cust_id' => $co->cohead_cust_id,
606 'cmhead_billto_cntct_id' => $co->cohead_billto_cntct_id,
607 'cmhead_taxzone_id' => $co->cohead_taxzone_id,
608 'cmhead_curr_id' => $co->cohead_curr_id,
609 'cmhead_salesrep_id' => $co->cohead_salesrep_id,
610 'cmhead_docdate' => $co->cohead_targetdate,
611 'cmhead_location_id' => $co->cohead_location_src
614 foreach($cmhead->defaults() as $k=>$v) {
615 if (empty($cmhead->$k)) {
623 $this->jerr('error occur on insert a credit memo');
626 $i = dB_DataObject::Factory('item');
627 if (!$i->get('item_number', 'Z-DISCOUNT-VOUCHER')) {
628 $this->jerr("Z-DISCOUNT-VOUCHER not found");
630 $itemsite = $i->itemsite();
632 $cmitem = DB_DataObject::factory('cmitem');
633 $cmitem->setFrom(array(
634 'cmitem_cmhead_id' => $cmhead->pid(),
635 'cmitem_linenumber' => 999997,
636 'cmitem_itemsite_id' => $itemsite->pid(),
637 'cmitem_qtycredit' => 1,
638 'cmitem_qtyreturned' => 1,
639 'cmitem_unitprice' => $gift,
640 'cmitem_taxtype_id' => $cmitem->sqlValue("gettaxtypeid('Taxable'::text)"),
641 'cmitem_comments' => $i->item_descrip1,
644 foreach($cmitem->defaults() as $k=>$v) {
645 if (empty($cmitem->$k)) {
652 $this->jerr('error occur on insert Z-DISCOUNT-VOUCHER to credit memo');
655 $cmhead->post($this);
657 $aropen = DB_DataObject::factory('aropen');
658 $aropen->aropen_cust_id = $customer->pid();
659 $aropen->aropen_doctype = 'C';
660 $aropen->aropen_docnumber = $cmhead->cmhead_number;
661 $cobapply_list = $aropen->fetchAll('aropen_id');
662 if(count($cobapply_list) != 1){
663 $this->jerr('error occur on getting cmhead aropen');
668 $this->debugLog('creating bill');
670 // create the invoice for each magento order
671 $cobmisc = DB_DataObject::factory('cobmisc');
673 $cobmisc->setFrom(array(
674 'cobmisc_cohead_id' => $co->pid(),
675 'cobmisc_invcdate' => $order_date,
676 'cobmisc_shipdate' => $order_date
679 $cobmisc->cobmisc_taxtype_id = $cobmisc->sqlValue("gettaxtypeid('Taxable'::text)");
682 $t = $cobmisc->factory('cobmisc');
683 $t->query("SELECT createBillingHeader({$co->pid()}) AS cobmisc_id");
686 $cobmisc->cobmisc_id = $t->cobmisc_id;
687 $cobmisc->updateItems($billitems);
689 // apply the credit memo..
690 if (!empty($cobapply_list)) {
691 $cobmisc->updateCobApply($this,$cobapply_list);
696 $t = $cobmisc->factory('cobmisc');
697 $t->get($cobmisc->cobmisc_id);
700 // fetch again so it should have invoice..
701 $t = $cobmisc->factory('cobmisc');
702 $t->get($cobmisc->cobmisc_id);
704 $invoice = $t->invchead();
706 $aropen = $invoice->aropen();
708 return array( $total, $aropen->aropen_amount);
712 function checkcntctAndaddr($cntct,$addrs)
714 $cn = DB_DataObject::factory('cntct');
716 if(!$cn->get('cntct_first_name', $cntct['cntct_first_name'])){ // contact not exists
717 $cn->setFrom($cntct);
718 $cn->cntct_crmacct_id = $cn->sqlValue("(SELECT crmacct_id FROM crmacct WHERE crmacct_cust_id = {$cntct['cust_id']})");
723 if(!$cn->cntct_addr_id){ // addr not exists
724 $addr = DB_DataObject::Factory('addr');
725 $addr->setFrom($addrs);
730 $cn->cntct_addr_id = $addr->pid();
738 function checkshiptoinfo($customer, $cntct)
740 $sh = DB_DataObject::Factory('shiptoinfo');
741 $sh->shipto_cntct_id = $cntct->pid();
742 $sh->shipto_addr_id = $cntct->cntct_addr_id;
743 if ($sh->find(true)) {
748 'shipto_cust_id' => $cust_id,
749 'shipto_name' => $cntct->cntct_name,
750 'shipto_salesrep_id'=> $customer->cust_salesrep_id,
751 'shipto_active' => true,
752 'shipto_adefault' => false,
753 'shipto_shipchrg_id' => $customer->cust_shipchrg_id,
754 'shipto_taxzone_id' => $customer->cust_taxzone_id,
755 'shipto_shipform_id' => $customer->cust_shipform_id,
756 'shipto_commission' => 0,
757 'shipto_shipzone_id' => $customer->cust_taxzone_id
767 function orderShipments($toShipments,$d)
769 $this->debugLog('creating shipments');
771 $ids = array_keys($toShipments);
772 $c = array_pop($toShipments);
774 $cc = DB_DataObject::factory('coitem');
775 $cc->coitem_cohead_id = $c->coitem_cohead_id;
779 $shipitems = array();
780 while ($cc->fetch()){ // this query will take about 6 secs...
782 $itemsite = $cc->itemsite();
783 // skip non-stock items..
784 if (!$itemsite->itemsite_stocked) {
788 $x['shipitem_orderitem_id'] = $cc->pid();
789 $x['shipitem_qty'] = 0;
790 if(in_array($cc->pid(), $ids)){
791 $x['shipitem_qty'] = $cc->coitem_qtyord;
793 $shipitems[] = (object)$x;
796 $sh = DB_DataObject::factory('shiphead');
801 'shiphead_shipdate' => $c->coitem_scheddate,
802 'shiphead_order_id' => $c->coitem_cohead_id,
803 'shiphead_sfstatus' => 'N',
804 'shiphead_location_id' => $c->coitem_location_src,
805 'shiphead_shipto_id' => $c->coitem_shipto_id,
806 //'shiphead_number' => $cohead->cohead_number . substr($c->coitem_linenumber, 0, 4),
807 'shiphead_order_type' => 'SO',
808 'shiphead_freight' => 0, //$d['freight'],
809 'shiphead_freight_curr_id' => $d['curr'],
810 'shiphead_notes' => '' // $r['SHIP NOTE']
812 $sh->beforeInsert(array('shiphead_number' => 'Automatic'),$this);
813 //$this->jerr("about to create shipment: " . $sh->shiphead_number);
817 $sh->updateItems($shipitems, $this);
822 function voidInvAndShipments($co)
824 $this->debugLog('deelting old invoices / shipments');
825 $invoices = $co->cobmiscs();
827 foreach($invoices as $i) {
828 $i->void($this, true);
832 $invoices = $co->cobmiscs();
833 foreach($invoices as $i) {
834 $i->void($this,true);
836 // delete the shipments...
837 $shipheads = $co->shipheads();
839 foreach($shipheads as $s) {
841 if (!empty($s->shiphead_shipped) && !empty($s->shiphead_shipdate)) {
842 $res = $s->unconfirm($this,true);
845 $this->jerr("ship unconfirm failed:" . $res);
849 if (!empty($s->shiphead_shipdate)) { // 395.43042492867 secs...
850 $res = $s->void($this,true); // void shipitem will take 0.1 sec for each... a little bit long time...
853 $this->jerr("ship voiding failed" . $res) ;
865 // Gift Wrapping not found
866 // Gift Wrapping not found
868 // Gift Wrapping not found