170
169
# from the Magento's corresponding orders
172
171
# Get all need_to_update orders in OERP
173
orders_to_update = so_obj.search(cr,uid,[('need_to_update', '=', True), ('shop_id', '=', shop.id)])
174
for order in so_obj.browse(cr, uid, orders_to_update):
175
mag_status = ORDER_STATUS_MAPPING.get(order.state, False)
176
# For each one, check if the status has change in Magento
177
# We dont use oeid_to_extid function cause it only handle int id
178
# Magento can have something like '100000077-2'
179
model_data_ids = self.pool.get('ir.model.data').search(cr, uid, [('model', '=', so_obj._name), ('res_id', '=', order.id), ('external_referential_id', '=', shop.referential_id.id)])
181
prefixed_id = self.pool.get('ir.model.data').read(cr, uid, model_data_ids[0], ['name'])['name']
182
ext_id = so_obj.id_from_prefixed_id(prefixed_id)
185
data_record=conn.call('sales_order.info', [ext_id])
187
if data_record['status'] == 'canceled':
188
wf_service = netsvc.LocalService("workflow")
189
wf_service.trg_validate(uid, 'sale.order', order.id, 'cancel', cr)
191
self.log(cr, uid, order.id, "order %s canceled when updated from external system" % (order.id,))
192
logger.notifyChannel('ext synchro', netsvc.LOG_INFO, "order %s canceled when updated from external system" % (order.id,))
193
# If the order isn't canceled and was blocked,
194
# so we follow the standard flow according to ext_payment_method:
196
paid = so_obj.create_payments(cr, uid, order.id, data_record, context)
197
so_obj.oe_status(cr, uid, order.id, paid, context)
200
self.log(cr, uid, order.id, "order %s paid when updated from external system" % (order.id,))
201
logger.notifyChannel('ext synchro', netsvc.LOG_INFO, "order %s paid when updated from external system" % (order.id,))
202
# Untick the need_to_update if updated (if so was canceled in magento
203
# or if it has been paid through magento)
205
so_obj.write(cr, uid, order.id, {'need_to_update': False})
172
orders_to_update = so_obj.search(
174
[('need_to_update', '=', True),
175
('shop_id', '=', shop.id)],
177
so_obj.check_need_to_update(
178
cr, uid, orders_to_update, conn, context=context)
209
def _create_magento_invoice(self, cr, uid, order, conn, ext_id, context=None):
210
""" Creation of an invoice on Magento."""
211
cr.execute("select account_invoice.id "
212
"from account_invoice "
213
"inner join sale_order_invoice_rel "
214
"on invoice_id = account_invoice.id "
215
"where order_id = %s" % order.id)
216
resultset = cr.fetchone()
218
if resultset and len(resultset) == 1:
219
invoice = self.pool.get("account.invoice").browse(
220
cr, uid, resultset[0], context=context)
221
if (invoice.amount_total == order.amount_total and
222
not invoice.magento_ref):
224
magento_invoice_ref = conn.call(
225
'sales_order_invoice.create',
226
[order.magento_incrementid,
228
_("Invoice Created"),
230
order.shop_id.allow_magento_notification])
231
self.pool.get("account.invoice").write(
234
{'magento_ref': magento_invoice_ref,
235
'origin': magento_invoice_ref})
236
self.log(cr, uid, order.id,
237
"created Magento invoice for order %s" %
241
self.log(cr, uid, order.id,
242
"failed to create Magento invoice for order %s" %
244
# TODO make sure that's because Magento invoice already
245
# exists and then re-attach it!
248
181
def update_shop_orders(self, cr, uid, order, ext_id, context=None):
249
182
if context is None: context = {}
252
185
if order.shop_id.allow_magento_order_status_push:
186
sale_obj = self.pool.get('sale.order')
254
188
conn = context.get('conn_obj', False)
255
189
status = ORDER_STATUS_MAPPING.get(order.state, False)
340
281
if 'address_type' in data_record['shipping_address']:
341
282
data_record['shipping_address'].update(self.get_mage_customer_address_id(data_record['shipping_address']))
342
283
shipping_default = {}
344
res['partner_id'] = self.pool.get('res.partner').extid_to_oeid(cr, uid, data_record['customer_id'], external_referential_id)
288
# always update the customer when importing an order
289
partner_obj.get_external_data(
291
context.get('conn_obj'),
292
external_referential_id,
294
context={'id': data_record['customer_id']})
295
res['partner_id'] = partner_obj.extid_to_oeid(cr, uid, data_record['customer_id'], external_referential_id)
345
297
if res.get('partner_id', False):
346
298
shipping_default = {'partner_id': res.get('partner_id', False)}
347
299
billing_default = shipping_default.copy()
348
300
billing_default.update({'email' : data_record.get('customer_email', False)})
350
inv_res = partner_address_obj.ext_import(cr, uid, [data_record['billing_address']], external_referential_id, billing_default, context)
351
if 'address_type' in data_record['shipping_address']:
352
ship_res = partner_address_obj.ext_import(cr, uid, [data_record['shipping_address']], external_referential_id, shipping_default, context)
354
ship_res = partner_address_obj.ext_import(cr, uid, [data_record['billing_address']], external_referential_id, shipping_default, context)
356
res['partner_order_id'] = len(inv_res['create_ids']) > 0 and inv_res['create_ids'][0] or inv_res['write_ids'][0]
302
inv_res = partner_address_obj.ext_import(
303
cr, uid, [data_record['billing_address']], external_referential_id, billing_default, context)
304
res['partner_order_id'] = inv_res.get('create_ids') and inv_res['create_ids'][0] or inv_res['write_ids'][0]
357
305
res['partner_invoice_id'] = res['partner_order_id']
358
res['partner_shipping_id'] = (len(ship_res['create_ids']) > 0 and ship_res['create_ids'][0]) or (len(ship_res['write_ids']) > 0 and ship_res['write_ids'][0]) or res['partner_order_id'] #shipping might be the same as invoice address
307
if data_record['shipping_address']:
308
ship_res = partner_address_obj.ext_import(
309
cr, uid, [data_record['shipping_address']], external_referential_id, shipping_default, context)
310
res['partner_shipping_id'] = (ship_res.get('create_ids') and ship_res['create_ids'][0]) or \
311
(ship_res.get('write_ids') and ship_res['write_ids'][0]) \
314
# when there is no shipping address, use the order's one
315
if not res.get('partner_shipping_id'):
316
res['partner_shipping_id'] = res['partner_order_id']
360
318
result = partner_address_obj.read(cr, uid, res['partner_order_id'], ['partner_id'])
361
319
if result and result['partner_id']:
362
320
partner_id = result['partner_id'][0]
410
368
partner_obj.write(cr, uid, [partner_id], partner_vals)
413
def get_order_lines(self, cr, uid, res, external_referential_id, data_record, key_field, mapping_lines, defaults, context=None):
414
if context is None: context = {}
415
mapping_id = self.pool.get('external.mapping').search(cr,uid,[('model','=','sale.order.line'),('referential_id','=',external_referential_id)])
417
mapping_line_ids = self.pool.get('external.mapping.line').search(cr,uid,[('mapping_id','=',mapping_id[0]),('type','in',['in_out','in'])])
418
mapping_lines = self.pool.get('external.mapping.line').read(cr,uid,mapping_line_ids,['external_field','external_type','in_function'])
421
is_tax_included = defaults.get('price_type', False) == 'tax_included'
422
for line_data in data_record.get('items', []):
423
# Setting the UoM in sale order line as defined in product definition in openerp
424
product_id = self.pool.get('product.product').extid_to_oeid(cr, uid, line_data['product_id'], external_referential_id)
425
product = self.pool.get('product.product').browse(cr, uid, product_id)
426
defaults_line = {'product_uom': product.uom_id.id}
427
#simple VAT tax on order line (else override method):
428
line_tax_vat = float(line_data.get('tax_percent', False) or 0) / 100.0
430
line_tax_ids = self.pool.get('account.tax').search(cr, uid, ['|', ('type_tax_use', '=', 'all'), ('type_tax_use', '=', 'sale'), ('price_include', '=', is_tax_included), ('amount', '>=', line_tax_vat - 0.001), ('amount', '<=', line_tax_vat + 0.001)])
431
if line_tax_ids and len(line_tax_ids) > 0:
432
defaults_line['tax_id'] = [(6, 0, [line_tax_ids[0]])]
433
context.update({'partner_id': res['partner_id'], 'pricelist_id': res['pricelist_id']})
434
if defaults.get('price_type', False) == 'tax_included':
435
context.update({'price_is_tax_included': True})
436
line_val = self.oevals_from_extdata(cr, uid, external_referential_id, line_data, 'item_id', mapping_lines, defaults_line, context)
437
if line_val['product_id']:
438
line_val['type'] = self.pool.get('product.product').read(cr, uid, line_val['product_id'], ['procure_method'], context)['procure_method']
439
if not line_val.has_key('_CANCEL_IMPORT'):
440
lines_vals.append((0, 0, line_val))
441
res['order_line'] = lines_vals
445
372
def add_order_extra_line(self, cr, uid, res, data_record, ext_field, product_ref, defaults, context=None):
446
373
""" Add or substract amount on order as a separate line item with single quantity for each type of amounts like :
465
392
model, product_id = model_data_obj.get_object_reference(cr, uid, *product_ref)
466
393
product = self.pool.get('product.product').browse(cr, uid, product_id, context)
467
is_tax_included = defaults.get('price_type', False) == 'tax_included'
394
is_tax_included = context.get('price_is_tax_included', False)
468
395
amount = float(data_record[ext_field]) * sign
471
if data_record[ext_tax_field] and float(data_record[ext_tax_field]) != 0:
472
tax_vat = abs(float(data_record[ext_tax_field]) / amount)
473
tax_ids = self.pool.get('account.tax').search(cr, uid, [('price_include', '=', is_tax_included), ('type_tax_use', '=', 'sale'), ('amount', '>=', tax_vat - 0.001), ('amount', '<=', tax_vat + 0.001)])
474
if tax_ids and len(tax_ids) > 0:
475
tax_id = [(6, 0, [tax_ids[0]])]
477
#try to find a tax with less precision
478
tax_ids = self.pool.get('account.tax').search(cr, uid, [('price_include', '=', is_tax_included), ('type_tax_use', '=', 'sale'), ('amount', '>=', tax_vat - 0.01), ('amount', '<=', tax_vat + 0.01)])
479
if tax_ids and len(tax_ids) > 0:
480
tax_id = [(6, 0, [tax_ids[0]])]
482
396
name = product.name
483
397
if ext_code_field and data_record.get(ext_code_field, False):
484
398
name = "%s [%s]" % (name, data_record[ext_code_field])
489
403
price_unit = float(amount)
491
res['order_line'].append((0, 0, {
492
'product_id': product.id,
494
'product_uom': product.uom_id.id,
495
'product_uom_qty': 1,
496
'price_unit': price_unit,
406
'product_id': product.id,
408
'product_uom': product.uom_id.id,
409
'product_uom_qty': 1,
410
'price_unit': price_unit,
413
if not res.get('order_line'):
414
res['order_line'] = []
416
if context.get('use_external_tax'):
417
# get the tax computed by the external system
418
tax_vat = abs(float(data_record[ext_tax_field]) / amount)
419
line_tax_id = self.pool.get('account.tax').get_tax_from_rate(cr, uid, tax_vat, context.get('is_tax_included'), context=context)
420
extra_line['tax_id'] = [(6, 0, line_tax_id)]
422
# compute the taxes, apply fiscal positions, default values and so on
423
extra_line = self.pool.get('sale.order.line').play_sale_order_line_onchange(cr, uid, extra_line, res, res['order_line'], defaults, context=context)
424
res['order_line'].append((0, 0, extra_line))
501
def add_order_shipping(self, cr, uid, res, external_referential_id, data_record, key_field, mapping_lines, defaults, context=None):
428
def add_order_shipping(self, cr, uid, res, external_referential_id, data_record, defaults, context=None):
502
429
if context is None: context = {}
503
430
if data_record.get('shipping_amount', False) and float(data_record.get('shipping_amount', False)) > 0:
504
431
ctx = context.copy()
542
469
product_ref = ('magentoerpconnect', 'product_product_cash_on_delivery')
543
470
res = self.add_order_extra_line(cr, uid, res, data_record, 'cod_fee', product_ref, defaults, ctx)
473
def convert_extdata_into_oedata(self, cr, uid, external_data, external_referential_id, parent_data=None, defaults=None, context=None):
474
res = super(sale_order, self).convert_extdata_into_oedata(cr, uid, external_data, external_referential_id, parent_data=parent_data, defaults=defaults, context=context)
476
external_data = external_data[0]
477
res = self.add_order_shipping(cr, uid, res, external_referential_id, external_data, defaults, context)
478
res = self.add_gift_certificates(cr, uid, res, external_referential_id, external_data, defaults, context)
479
res = self.add_discount(cr, uid, res, external_referential_id, external_data, defaults, context)
480
res = self.add_cash_on_delivery(cr, uid, res, external_referential_id, external_data, defaults, context)
546
483
def _merge_sub_items(self, cr, uid, product_type, top_item, child_items, context=None):
593
530
data_record['items'] = all_items
594
531
return data_record
597
def get_all_order_lines(self, cr, uid, res, external_referential_id, data_record, key_field, mapping_lines, defaults, context=None):
598
res = self.get_order_lines(cr, uid, res, external_referential_id, data_record, key_field, mapping_lines, defaults, context)
599
res = self.add_order_shipping(cr, uid, res, external_referential_id, data_record, key_field, mapping_lines, defaults, context)
600
res = self.add_gift_certificates(cr, uid, res, external_referential_id, data_record, key_field, mapping_lines, defaults, context)
601
res = self.add_discount(cr, uid, res, external_referential_id, data_record, key_field, mapping_lines, defaults, context)
602
res = self.add_cash_on_delivery(cr, uid, res, external_referential_id, data_record, key_field, mapping_lines, defaults, context)
605
def oevals_from_extdata(self, cr, uid, external_referential_id, data_record, key_field, mapping_lines, defaults, context=None):
533
def oevals_from_extdata(self, cr, uid, external_referential_id, data_record, key_field, mapping_lines, parent_data=None, previous_lines=None, defaults=None, context=None):
606
534
if context is None: context = {}
607
535
if data_record.get('items', False):
608
536
data_record = self.data_record_filter(cr, uid, data_record, context=context)
537
#TODO refactor this code regarding the new feature of sub-mapping in base_external_referential
610
538
if not context.get('one_by_one', False):
611
539
if data_record.get('billing_address', False):
612
540
defaults = self.get_order_addresses(cr, uid, defaults, external_referential_id, data_record, key_field, mapping_lines, defaults, context)
614
res = super(magerp_osv.magerp_osv, self).oevals_from_extdata(cr, uid, external_referential_id, data_record, key_field, mapping_lines, defaults, context)
541
res = super(magerp_osv.magerp_osv, self).oevals_from_extdata(cr, uid, external_referential_id, data_record, key_field, mapping_lines, parent_data, previous_lines, defaults, context)
616
if not context.get('one_by_one', False):
617
if data_record.get('items', False):
619
res = self.get_all_order_lines(cr, uid, res, external_referential_id, data_record, key_field, mapping_lines, defaults, context)
621
#TODO fix me error should be raise correctly in the reporting system
622
res = self.get_all_order_lines(cr, uid, res, external_referential_id, data_record, key_field, mapping_lines, defaults, context)
624
# res = self.get_all_order_lines(cr, uid, res, external_referential_id, data_record, key_field, mapping_lines, defaults, context)
625
#except Exception, e:
626
# print "order has errors with items lines, data are: ", data_record
628
#TODO flag that the order has an error, especially.
543
#Move me in a mapping
544
if not context.get('one_by_one', False):
630
545
if data_record.get('status_history', False) and len(data_record['status_history']) > 0:
631
546
res['date_order'] = data_record['status_history'][len(data_record['status_history'])-1]['created_at']
549
def _parse_external_payment(self, cr, uid, order_data, context=None):
551
Parse the external order data and return if the sale order
552
has been paid and the amount to pay or to be paid
554
:param dict order_data: payment information of the magento sale
556
:return: tuple where :
557
- first item indicates if the payment has been done (True or False)
558
- second item represents the amount paid or to be paid
560
paid = amount = False
561
payment_info = order_data.get('payment')
564
if payment_info.get('amount_paid', False):
565
amount = payment_info.get('amount_paid', False)
567
elif payment_info.get('amount_ordered', False):
568
amount = payment_info.get('amount_ordered', False)
634
571
def create_payments(self, cr, uid, order_id, data_record, context=None):
635
if context is None: context = {}
636
if context.get('external_referential_type', False) and 'Magento' in context['external_referential_type']:
638
if data_record.get('payment', False):
639
payment = data_record['payment']
641
if payment.get('amount_paid', False):
642
amount = payment.get('amount_paid', False)
644
elif payment.get('amount_ordered', False):
645
amount = payment.get('amount_ordered', False)
647
order = self.pool.get('sale.order').browse(cr, uid, order_id, context)
648
self.generate_payment_with_pay_code(cr, uid, payment['method'], order.partner_id.id, float(amount), "mag_" + payment['payment_id'], "mag_" + data_record['increment_id'], order.date_order, paid, context)
575
if 'Magento' in context.get('external_referential_type', ''):
576
payment_info = data_record.get('payment')
577
paid, amount = self._parse_external_payment(
578
cr, uid, data_record, context=context)
580
order = self.pool.get('sale.order').browse(
581
cr, uid, order_id, context)
582
self.generate_payment_with_pay_code(
584
payment_info['method'],
587
"mag_" + payment_info['payment_id'],
588
"mag_" + data_record['increment_id'],
650
paid = super(sale_order, self).create_payments(cr, uid, order_id, data_record, context=context)
593
paid = super(sale_order, self).create_payments(
594
cr, uid, order_id, data_record, context=context)
653
597
def _chain_cancel_orders(self, cr, uid, external_id, external_referential_id, defaults=None, context=None):
772
716
result['unchanged_ids'] = unchanged_ids
775
# UPDATE ORDER STATUS FROM MAGENTO TO OPENERP IS UNSTABLE, AND NOT VERY USEFULL. MAYBE IT WILL BE REFACTORED
777
#def oe_update(self,cr, uid, existing_rec_id, vals, data, external_referential_id, defaults, context):
778
#order_line_ids = self.pool.get('sale.order.line').search(cr,uid,[('order_id','=', existing_rec_id)])
779
#self.pool.get('sale.order.line').unlink(cr, uid, order_line_ids)
780
#TODO update order status eventually (that would be easier if they were linked by some foreign key...)
781
#self.oe_status(cr, uid, data, existing_rec_id, context)
782
#return super(magerp_osv.magerp_osv, self).oe_update(cr, uid, existing_rec_id, vals, data, external_referential_id, defaults, context)
784
#def oe_status(self, cr, uid, data, order_id, context):
785
#wf_service = netsvc.LocalService("workflow")
786
#if data.get('status_history', False) and len(data['status_history']) > 0 and data['status_history'][0]['status'] == 'canceled':
787
# wf_service.trg_validate(uid, 'sale.order', order_id, 'cancel', cr)
789
# super(magerp_osv.magerp_osv, self).oe_status(cr, uid, order_id, context)
719
def _check_need_to_update_single(self, cr, uid, order, conn, context=None):
721
For one order, check on Magento if it has been paid since last
722
check. If so, it will launch the defined flow based on the
723
payment type (validate order, invoice, ...)
725
:param browse_record order: browseable sale.order
726
:param Connection conn: connection with Magento
729
model_data_obj = self.pool.get('ir.model.data')
730
# check if the status has changed in Magento
731
# We don't use oeid_to_extid function cause it only handles integer ids
732
# Magento can have something like '100000077-2'
733
model_data_ids = model_data_obj.search(
735
[('model', '=', self._name),
736
('res_id', '=', order.id),
737
('external_referential_id', '=', order.shop_id.referential_id.id)],
741
prefixed_id = model_data_obj.read(
742
cr, uid, model_data_ids[0], ['name'], context=context)['name']
743
ext_id = self.id_from_prefixed_id(prefixed_id)
747
data_record = conn.call('sales_order.info', [ext_id])
749
if data_record['status'] == 'canceled':
750
wf_service = netsvc.LocalService("workflow")
751
wf_service.trg_validate(uid, 'sale.order', order.id, 'cancel', cr)
753
self.log(cr, uid, order.id, "order %s canceled when updated from external system" % (order.id,))
754
# If the order isn't canceled and was waiting for a payment,
755
# so we follow the standard flow according to ext_payment_method:
757
paid, __ = self._parse_external_payment(
758
cr, uid, data_record, context=context)
759
self.oe_status(cr, uid, order.id, paid, context)
760
# create_payments has to be done after oe_status
761
# because oe_status creates the invoice
762
# and create_payment could reconcile the payment
765
updated = self.create_payments(
766
cr, uid, order.id, data_record, context)
770
"order %s paid when updated from external system" %
772
# Untick the need_to_update if updated (if so was canceled in magento
773
# or if it has been paid through magento)
775
self.write(cr, uid, order.id, {'need_to_update': False})
779
def check_need_to_update(self, cr, uid, ids, conn, context=None):
781
For each order, check on Magento if it has been paid since last
782
check. If so, it will launch the defined flow based on the
783
payment type (validate order, invoice, ...)
785
:param Connection conn: connection with Magento
788
for order in self.browse(cr, uid, ids, context=context):
789
self._check_need_to_update_single(
790
cr, uid, order, conn, context=context)
793
def _create_external_invoice(self, cr, uid, order, conn, ext_id,
795
""" Creation of an invoice on Magento."""
796
magento_invoice_ref = conn.call(
797
'sales_order_invoice.create',
798
[order.magento_incrementid,
800
_("Invoice Created"),
802
order.shop_id.allow_magento_notification])
803
return magento_invoice_ref
805
# TODO Move in base_sale_multichannels?
806
def export_invoice(self, cr, uid, order, conn, ext_id, context=None):
807
""" Export an invoice on external referential """
808
cr.execute("select account_invoice.id "
809
"from account_invoice "
810
"inner join sale_order_invoice_rel "
811
"on invoice_id = account_invoice.id "
812
"where order_id = %s" % order.id)
813
resultset = cr.fetchone()
815
if resultset and len(resultset) == 1:
816
invoice = self.pool.get("account.invoice").browse(
817
cr, uid, resultset[0], context=context)
818
if (invoice.amount_total == order.amount_total and
819
not invoice.magento_ref):
821
self._create_external_invoice(
822
cr, uid, order, conn, ext_id, context=context)
825
self.log(cr, uid, order.id,
826
"failed to create Magento invoice for order %s" %
828
# TODO make sure that's because Magento invoice already
829
# exists and then re-attach it!