528
519
def _merge_with_default_values(self, cr, uid, external_session, ressource, vals, sub_mapping_list, defaults=None, context=None):
529
520
if not context: context ={}
530
pay_type_obj = self.pool.get('base.sale.payment.type')
531
payment_method = vals.get('ext_payment_method', False)
532
payment_settings = pay_type_obj.find_by_payment_code(
533
cr, uid, payment_method, context=context)
535
vals['order_policy'] = payment_settings.order_policy
536
vals['picking_policy'] = payment_settings.picking_policy
537
vals['invoice_quantity'] = payment_settings.invoice_quantity
521
if vals.get('payment_method_id'):
522
payment_method = self.pool.get('payment.method').browse(cr, uid, vals['payment_method_id'], context=context)
523
workflow_process = payment_method.workflow_process_id
525
vals['order_policy'] = workflow_process.order_policy
526
vals['picking_policy'] = workflow_process.picking_policy
527
vals['invoice_quantity'] = workflow_process.invoice_quantity
538
528
# update vals with order onchange in order to compute taxes
539
529
vals = self.play_sale_order_onchange(cr, uid, vals, defaults=defaults, context=context)
540
530
return super(sale_order, self)._merge_with_default_values(cr, uid, external_session, ressource, vals, sub_mapping_list, defaults=defaults, context=context)
542
def create_payments(self, cr, uid, order_id, data_record, context):
543
"""not implemented in this abstract module"""
544
# if not context.get('external_referential_type'):
545
# raise osv.except_osv(
546
# _('Error'), _('Missing external referential type '
547
# ' when creating payment'))
550
def _parse_external_payment(self, cr, uid, data, context=None):
552
Not implemented in this abstract module
554
Parse the external order data and return if the sale order
555
has been paid and the amount to pay or to be paid
557
:param dict data: payment information of the magento sale
559
:return: tuple where :
560
- first item indicates if the payment has been done (True or False)
561
- second item represents the amount paid or to be paid
565
def oe_status_and_paid(self, cr, uid, order_id, data, external_referential_id, defaults, context):
566
is_paid, amount = self._parse_external_payment(
567
cr, uid, data, context=context)
568
# create_payments has to be called after oe_status
569
# because oe_status may create an invoice
570
self.oe_status(cr, uid, order_id, is_paid, context)
571
self.create_payments(cr, uid, order_id, data, context)
574
def oe_create(self, cr, uid, vals, external_referential_id, defaults, context):
532
def oe_create(self, cr, uid, external_session, vals, resource, defaults, context):
575
533
#depending of the external system the contact address can be optionnal
576
vals = self._convert_special_fields(cr, uid, vals, external_referential_id, context=context)
534
vals = self._convert_special_fields(cr, uid, vals, external_session.referential_id.id, context=context)
577
535
if not vals.get('partner_order_id'):
578
536
vals['partner_order_id'] = vals['partner_invoice_id']
579
order_id = super(sale_order, self).oe_create(cr, uid, vals, external_referential_id, defaults, context)
580
self.oe_status_and_paid(cr, uid, order_id, vals, external_referential_id, defaults, context)
537
order_id = super(sale_order, self).oe_create(cr, uid, external_session, vals, resource, defaults, context)
538
self.paid_and_update(cr, uid, external_session, order_id, resource, context=context)
583
def generate_payment_from_order(self, cr, uid, ids, payment_ref, entry_name=None, paid=True, date=None, context=None):
584
if type(ids) in [int, long]:
587
for order in self.browse(cr, uid, ids, context=context):
588
id = self.generate_payment_with_pay_code(cr, uid,
589
order.ext_payment_method,
591
order.ext_total_amount or order.amount_total,
593
entry_name or order.name,
594
date or order.date_order,
597
id and res.append(id)
600
def generate_payment_with_pay_code(self, cr, uid, payment_code, partner_id,
601
amount, payment_ref, entry_name,
602
date, paid, context):
603
pay_type_obj = self.pool.get('base.sale.payment.type')
604
payment_settings = pay_type_obj.find_by_payment_code(
605
cr, uid, payment_code, context=context)
606
if payment_settings and \
607
payment_settings.journal_id and \
608
(payment_settings.check_if_paid and
609
paid or not payment_settings.check_if_paid):
610
return self.generate_payment_with_journal(
611
cr, uid, payment_settings.journal_id.id,
612
partner_id, amount, payment_ref,
613
entry_name, date, payment_settings.validate_payment,
541
def paid_and_update(self, cr, uid, external_session, order_id, resource, context=None):
542
wf_service = netsvc.LocalService("workflow")
543
paid = self.create_external_payment(cr, uid, external_session, order_id, resource, context)
544
order = self.browse(cr, uid, order_id, context=context)
545
validate_order = order.workflow_process_id.validate_order
546
if validate_order == 'always' or validate_order == 'if_paid' and paid:
548
wf_service.trg_validate(uid, 'sale.order', order.id, 'order_confirm', cr)
549
except Exception as e:
550
raise 'error', e#What we should do?? creating the order but not validating it???
551
#Maybe setting a special flag can be a good solution? with a retry method?
554
elif validate_order == 'if_paid':
555
days_before_order_cancel = order.workflow_process_id.days_before_order_cancel or 30
556
order_date = datetime.strptime(order.date_order, DEFAULT_SERVER_DATE_FORMAT)
557
order_cancel_date = order_date + relativedelta(days=days_before_order_cancel)
558
if order.state == 'draft' and order_cancel_date < datetime.now():
559
wf_service.trg_validate(uid, 'sale.order', order.id, 'cancel', cr)
560
self.write(cr, uid, order.id, {'need_to_update': False})
561
self.log(cr, uid, order.id, ("order %s canceled in OpenERP because older than % days"
562
"and still not confirmed") % (order.id, days_before_order_cancel))
563
#TODO eventually call a trigger to cancel the order in the external system too
564
external_session.logger.notifyChannel('ext synchro', netsvc.LOG_INFO,
565
("order %s canceled in OpenERP because older than % days and "
566
"still not confirmed") %(order.id, days_before_order_cancel))
568
self.write(cr, uid, order_id, {'need_to_update': True}, context=context)
617
def generate_payment_with_journal(self, cr, uid, journal_id, partner_id,
618
amount, payment_ref, entry_name,
619
date, should_validate, context):
621
Generate a voucher for the payment
623
It will try to match with the invoice of the order by
624
matching the payment ref and the invoice origin.
626
The invoice does not necessarily exists at this point, so if yes,
627
it will be matched in the voucher, otherwise, the voucher won't
628
have any invoice lines and the payment lines will be reconciled
629
later with "auto-reconcile" if the option is used.
632
voucher_obj = self.pool.get('account.voucher')
633
voucher_line_obj = self.pool.get('account.voucher.line')
634
move_line_obj = self.pool.get('account.move.line')
636
journal = self.pool.get('account.journal').browse(
637
cr, uid, journal_id, context=context)
639
voucher_vals = {'reference': entry_name,
640
'journal_id': journal.id,
643
'partner_id': partner_id,
644
'account_id': journal.default_credit_account_id.id,
645
'currency_id': journal.company_id.currency_id.id,
646
'company_id': journal.company_id.id,
648
voucher_id = voucher_obj.create(cr, uid, voucher_vals, context=context)
650
# call on change to search the invoice lines
651
onchange_voucher = voucher_obj.onchange_partner_id(
653
partner_id=partner_id,
654
journal_id=journal.id,
656
currency_id=journal.company_id.currency_id.id,
659
context=context)['value']
661
# keep in the voucher only the move line of the
662
# invoice (eventually) created for this order
664
if onchange_voucher.get('line_cr_ids'):
665
voucher_lines = onchange_voucher['line_cr_ids']
666
line_ids = [line['move_line_id'] for line in voucher_lines]
667
matching_ids = [line.id for line
668
in move_line_obj.browse(
669
cr, uid, line_ids, context=context)
670
if line.ref == entry_name]
671
matching_lines = [line for line
673
if line['move_line_id'] in matching_ids]
675
matching_line = matching_lines[0]
676
matching_line.update({
678
'voucher_id': voucher_id,
682
voucher_line_obj.create(cr, uid, matching_line, context=context)
685
wf_service = netsvc.LocalService("workflow")
686
wf_service.trg_validate(
687
uid, 'account.voucher', voucher_id, 'proforma_voucher', cr)
690
def oe_status(self, cr, uid, ids, paid=True, context=None):
691
if type(ids) in [int, long]:
694
wf_service = netsvc.LocalService("workflow")
695
logger = netsvc.Logger()
696
for order in self.browse(cr, uid, ids, context):
697
payment_settings = order.base_payment_type_id
700
if payment_settings.payment_term_id:
701
self.write(cr, uid, order.id, {'payment_term': payment_settings.payment_term_id.id})
703
if payment_settings.check_if_paid and not paid:
704
if order.state == 'draft' and datetime.strptime(order.date_order, DEFAULT_SERVER_DATE_FORMAT) < datetime.now() - relativedelta(days=payment_settings.days_before_order_cancel or 30):
705
wf_service.trg_validate(uid, 'sale.order', order.id, 'cancel', cr)
706
self.write(cr, uid, order.id, {'need_to_update': False})
707
self.log(cr, uid, order.id, "order %s canceled in OpenERP because older than % days and still not confirmed" % (order.id, payment_settings.days_before_order_cancel or 30))
708
#TODO eventually call a trigger to cancel the order in the external system too
709
logger.notifyChannel('ext synchro', netsvc.LOG_INFO, "order %s canceled in OpenERP because older than % days and still not confirmed" % (order.id, payment_settings.days_before_order_cancel or 30))
711
self.write(cr, uid, order.id, {'need_to_update': True})
713
if payment_settings.validate_order:
715
wf_service.trg_validate(uid, 'sale.order', order.id, 'order_confirm', cr)
716
self.write(cr, uid, order.id, {'need_to_update': False})
718
self.log(cr, uid, order.id, "ERROR could not valid order")
721
if payment_settings.validate_picking:
722
self.pool.get('stock.picking').validate_picking_from_order(cr, uid, order.id)
724
cr.execute('select * from ir_module_module where name=%s and state=%s', ('mrp','installed'))
725
if payment_settings.validate_manufactoring_order and cr.fetchone(): #if mrp module is installed
726
self.pool.get('stock.picking').validate_manufactoring_order(cr, uid, order.name, context)
728
if order.order_policy == 'prepaid':
729
if payment_settings.validate_invoice:
730
for invoice in order.invoice_ids:
731
wf_service.trg_validate(uid, 'account.invoice', invoice.id, 'invoice_open', cr)
732
if payment_settings.is_auto_reconcile:
733
invoice.auto_reconcile(context=context)
735
elif order.order_policy == 'manual':
736
if payment_settings.create_invoice:
737
wf_service.trg_validate(uid, 'sale.order', order.id, 'manual_invoice', cr)
738
invoice_id = self.browse(cr, uid, order.id).invoice_ids[0].id
739
if payment_settings.validate_invoice:
740
wf_service.trg_validate(uid, 'account.invoice', invoice_id, 'invoice_open', cr)
741
if payment_settings.is_auto_reconcile:
742
self.pool.get('account.invoice').auto_reconcile(cr, uid, [invoice_id], context=context)
744
# IF postpaid DO NOTHING
746
elif order.order_policy == 'picking':
747
if payment_settings.create_invoice:
749
invoice_id = self.pool.get('stock.picking').action_invoice_create(cr, uid, [picking.id for picking in order.picking_ids])
751
self.log(cr, uid, order.id, "Cannot create invoice from picking for order %s" %(order.name,))
752
if payment_settings.validate_invoice:
753
wf_service.trg_validate(uid, 'account.invoice', invoice_id, 'invoice_open', cr)
754
if payment_settings.is_auto_reconcile:
755
self.pool.get('account.invoice').auto_reconcile(cr, uid, [invoice_id], context=context)
571
def create_external_payment(self, cr, uid, external_session, order_id, resource, context):
573
Fonction that will create a payment from the external resource
575
vals = self._get_payment_information(cr, uid, external_session, order_id, resource, context=context)
577
if not vals.get('journal_id'):
578
external_session.logger.warning(_("Not journal found for payment method %s. Can not create payment")%vals['payment_method'])
581
self.pay_sale_order(cr, uid, order_id, vals['journal_id'], vals['amount'], vals['date'], context=context)
582
return vals.get('paid')
584
def _get_payment_information(self, cr, uid, external_session, order_id, resource, context=None):
586
Function that will return the information in order to create the payment
589
sale = self.browse(cr, uid, order_id, context=context)
590
vals['payment_method'] = sale.payment_method_id.name
591
vals['journal_id'] = sale.payment_method_id.journal_id and sale.payment_method_id.journal_id.id
592
vals['date'] = sale.date_order
759
595
def _prepare_invoice(self, cr, uid, order, lines, context=None):
760
596
"""Prepare the dict of values to create the new invoice for a
963
766
sale_order_line()
965
class base_sale_payment_type(osv.osv):
966
_name = "base.sale.payment.type"
967
_description = "Base Sale Payment Type"
970
'name': fields.char('Payment Codes', help="List of Payment Codes separated by ;", size=256, required=True),
971
'journal_id': fields.many2one('account.journal','Payment Journal', help='When a Payment Journal is defined on a Payment Type, a Customer Payment (Voucher) will be automatically created once the payment is done on the external system.'),
972
'picking_policy': fields.selection([('direct', 'Partial Delivery'), ('one', 'Complete Delivery')], 'Packing Policy'),
973
'order_policy': fields.selection([
974
('prepaid', 'Payment Before Delivery'),
975
('manual', 'Shipping & Manual Invoice'),
976
('postpaid', 'Invoice on Order After Delivery'),
977
('picking', 'Invoice from the Packing'),
978
], 'Shipping Policy'),
979
'invoice_quantity': fields.selection([('order', 'Ordered Quantities'), ('procurement', 'Shipped Quantities')], 'Invoice on'),
980
'is_auto_reconcile': fields.boolean('Auto-reconcile', help="If checked, will try to reconcile the Customer Payment (voucher) and the open invoice by matching the origin."),
981
'validate_order': fields.boolean('Validate Order'),
982
'validate_payment': fields.boolean('Validate Payment in Journal', help='If checked, the Customer Payment (voucher) generated in the Payment Journal will be validated and reconciled if the invoice already exists.'),
983
'create_invoice': fields.boolean('Create Invoice'),
984
'validate_invoice': fields.boolean('Validate Invoice'),
985
'validate_picking': fields.boolean('Validate Picking'),
986
'validate_manufactoring_order': fields.boolean('Validate Manufactoring Order'),
987
'check_if_paid': fields.boolean('Check if Paid'),
988
'days_before_order_cancel': fields.integer('Days Delay before Cancel', help='number of days before an unpaid order will be cancelled at next status update from Magento'),
989
'invoice_date_is_order_date' : fields.boolean('Force Invoice Date', help="If it's check the invoice date will be the same as the order date"),
990
'payment_term_id': fields.many2one('account.payment.term', 'Payment Term'),
994
'picking_policy': lambda *a: 'direct',
995
'order_policy': lambda *a: 'manual',
996
'invoice_quantity': lambda *a: 'order',
997
'is_auto_reconcile': lambda *a: False,
998
'validate_payment': lambda *a: False,
999
'validate_invoice': lambda *a: False,
1000
'days_before_order_cancel': lambda *a: 30,
1003
def find_by_payment_code(self, cr, uid, payment_code, context=None):
1004
payment_setting_ids = self.search(
1005
cr, uid, [['name', 'like', payment_code]], context=context)
1006
payment_setting = False
1007
payment_settings = self.browse(
1008
cr, uid, payment_setting_ids, context=context)
1009
for pay_type in payment_settings:
1010
# payment codes are in this form "bankpayment;checkmo"
1011
payment_codes = [x.strip() for x in pay_type.name.split(';')]
1012
if payment_code in payment_codes:
1013
payment_setting = pay_type
1015
return payment_setting
1017
base_sale_payment_type()
1019
class account_invoice(osv.osv):
1020
_inherit = "account.invoice"
1022
def auto_reconcile_single(self, cr, uid, invoice_id, context=None):
1023
obj_move_line = self.pool.get('account.move.line')
1024
invoice = self.browse(cr, uid, invoice_id, context=context)
1025
line_ids = obj_move_line.search(
1028
('ref', '=', invoice.origin),
1029
# keep ST_ for backward compatibility
1030
# previously the voucher ref
1031
('ref', '=', "ST_%s" % invoice.origin),
1032
('ref', '=', invoice.move_id.ref),
1033
('reconcile_id', '=', False),
1034
('account_id', '=', invoice.account_id.id)],
1037
if len(line_ids) == 2:
1038
lines = obj_move_line.read(
1039
cr, uid, line_ids, ['debit', 'credit'], context=context)
1040
balance = abs((lines[0]['debit'] + lines[0]['credit']) -
1041
(lines[1]['debit'] + lines[1]['credit']))
1042
precision = self.pool.get('decimal.precision').precision_get(
1044
if not round(balance, precision):
1045
obj_move_line.reconcile(cr, uid, line_ids, context=context)
1050
def auto_reconcile(self, cr, uid, ids, context=None):
1051
for invoice_id in ids:
1052
self.auto_reconcile_single(cr, uid, invoice_id, context=context)
1057
class stock_picking(osv.osv):
1058
_inherit = "stock.picking"
1060
def validate_picking_from_order(self, cr, uid, order_id, context=None):
1061
order= self.pool.get('sale.order').browse(cr, uid, order_id, context=context)
1062
if not order.picking_ids:
1063
raise Exception('For an unknow reason the picking for the sale order %s was not created'%order.name)
1064
for picking in order.picking_ids:
1065
picking.validate_picking(context=context)
1068
def validate_picking(self, cr, uid, ids, context=None):
1069
for picking in self.browse(cr, uid, ids, context=context):
1070
self.force_assign(cr, uid, [picking.id])
1072
for move in picking.move_lines:
1073
partial_data["move" + str(move.id)] = {'product_qty': move.product_qty, 'product_uom': move.product_uom.id}
1074
self.do_partial(cr, uid, [picking.id], partial_data)
1077
def validate_manufactoring_order(self, cr, uid, origin, context=None): #we do not create class mrp.production to avoid dependence with the module mrp
1080
wf_service = netsvc.LocalService("workflow")
1081
mrp_prod_obj = self.pool.get('mrp.production')
1082
mrp_product_produce_obj = self.pool.get('mrp.product.produce')
1083
production_ids = mrp_prod_obj.search(cr, uid, [('origin', 'ilike', origin)])
1084
for production in mrp_prod_obj.browse(cr, uid, production_ids):
1085
mrp_prod_obj.force_production(cr, uid, [production.id])
1086
wf_service.trg_validate(uid, 'mrp.production', production.id, 'button_produce', cr)
1087
context.update({'active_model': 'mrp.production', 'active_ids': [production.id], 'search_default_ready': 1, 'active_id': production.id})
1088
produce = mrp_product_produce_obj.create(cr, uid, {'mode': 'consume_produce', 'product_qty': production.product_qty}, context)
1089
mrp_product_produce_obj.do_produce(cr, uid, [produce], context)
1090
self.validate_manufactoring_order(cr, uid, production.name, context)
1094
767
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: