~ecommerce-addons-core-editors/e-commerce-addons/github-6.1

« back to all changes in this revision

Viewing changes to base_sale_multichannels/sale.py

  • Committer: sebastien beau
  • Date: 2012-04-22 22:07:12 UTC
  • mfrom: (191.1.4)
  • Revision ID: git-v1:79933629afa6be8db5b5c658f8a3db74ce6fee6e
[MERGE]

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# -*- encoding: utf-8 -*-
 
1
o# -*- encoding: utf-8 -*-
2
2
#########################################################################
3
3
#                                                                       #
4
4
# Copyright (C) 2009  RaphaĆ«l Valyi                                     #
281
281
        self.import_resources(cr, uid, ids, 'sale.order', context=context)
282
282
        return True
283
283
 
 
284
    def _check_need_to_update(self, cr, uid, external_session, context=None):
 
285
        """ This function will update the order status in OpenERP for
 
286
        the order which are in the state 'need to update' """
 
287
        so_obj = self.pool.get('sale.order')
 
288
        shop = external_session.sync_from_object
 
289
        orders_to_update = so_obj.search(cr, uid,
 
290
                [('need_to_update', '=', True),
 
291
                 ('shop_id', '=', shop.id)],
 
292
                context=context)
 
293
        so_obj._check_need_to_update(cr, uid, external_session, orders_to_update, context=context)
 
294
        return False
 
295
 
284
296
    def update_orders(self, cr, uid, ids, context=None):
285
297
        if context is None:
286
298
            context = {}
427
439
class sale_order(osv.osv):
428
440
    _inherit = "sale.order"
429
441
 
430
 
    def _get_payment_type_id(self, cr, uid, ids, field_name, args, context=None):
431
 
        res = {}
432
 
        pay_type_obj = self.pool.get('base.sale.payment.type')
433
 
        for sale in self.browse(cr, uid, ids, context=context):
434
 
            res[sale.id] = False
435
 
            if not sale.ext_payment_method:
436
 
                continue
437
 
            pay_type = pay_type_obj.find_by_payment_code(
438
 
                cr, uid, sale.ext_payment_method, context=context)
439
 
            if not pay_type:
440
 
                continue
441
 
            res[sale.id] = pay_type.id
442
 
        return res
443
 
 
444
442
    _columns = {
445
 
        'ext_payment_method': fields.char(
446
 
            'External Payment Method',
447
 
            size=32,
448
 
            help="Spree, Magento, Oscommerce... Payment Method"),
449
443
        'need_to_update': fields.boolean('Need To Update'),
450
444
        'ext_total_amount': fields.float(
451
445
            'Origin External Amount',
452
446
            digits_compute=dp.get_precision('Sale Price'),
453
447
            readonly=True),
454
 
        'base_payment_type_id': fields.function(
455
 
            _get_payment_type_id,
456
 
            string='Payment Type',
457
 
            type='many2one',
458
 
            relation='base.sale.payment.type',
459
 
            store={
460
 
                'sale.order':
461
 
                    (lambda self, cr, uid, ids, c=None: ids, ['ext_payment_method'], 20),
462
 
            }),
463
448
        'referential_id': fields.related(
464
449
                    'shop_id', 'referential_id',
465
450
                    type='many2one', relation='external.referential',
485
470
        return defaults
486
471
 
487
472
    def _import_resources(self, cr, uid, external_session, defaults=None, method="search_then_read", context=None):
488
 
        shop_id = context.get('sale_shop_id')
489
 
        if shop_id:
490
 
            shop = self.pool.get('sale.shop').browse(cr, uid, shop_id, context=context)
491
 
            self._check_need_to_update(cr, uid, external_session, shop_id, context=context)
 
473
        self.pool.get('sale.shop')._check_need_to_update(cr, uid, external_session, context=context)
 
474
        shop = external_session.sync_from_object
 
475
        if shop:
492
476
            context = {
493
477
                    'use_external_tax': shop.use_external_tax,
494
478
                    'is_tax_included': shop.is_tax_included,
497
481
 
498
482
    def _check_need_to_update(self, cr, uid, external_session, ids, context=None):
499
483
        """
500
 
        You should overwrite this fonction in your module in order to update the order
501
 
        with the status need to update
 
484
        For each order, check in external system if it has been paid since last
 
485
        check. If so, it will launch the defined flow based on the
 
486
        payment type (validate order, invoice, ...)
502
487
        """
 
488
        for order in self.browse(cr, uid, ids, context=context):
 
489
            self._check_need_to_update_single(cr, uid, external_session, order, context=context)
 
490
        return True
 
491
 
 
492
    def _check_need_to_update_single(self, cr, uid, external_session, order, context=None):
 
493
        """Not implemented in this abstract module"""
503
494
        return True
504
495
 
505
496
    def _get_kwargs_onchange_partner_id(self, cr, uid, vals, context=None):
527
518
 
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)
534
 
        if payment_settings:
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
 
524
            if workflow_process:
 
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)
541
 
    
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'))
548
 
        return False
549
 
 
550
 
    def _parse_external_payment(self, cr, uid, data, context=None):
551
 
        """
552
 
        Not implemented in this abstract module
553
 
 
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
556
 
 
557
 
        :param dict data: payment information of the magento sale
558
 
            order
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
562
 
        """
563
 
        return False, 0.0
564
 
 
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)
572
 
        return order_id
573
 
    
574
 
    def oe_create(self, cr, uid, vals, external_referential_id, defaults, context):
 
531
 
 
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)
581
539
        return order_id
582
 
    
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]:
585
 
            ids = [ids]
586
 
        res = []
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,
590
 
                                                    order.partner_id.id,
591
 
                                                    order.ext_total_amount or order.amount_total,
592
 
                                                    payment_ref,
593
 
                                                    entry_name or order.name,
594
 
                                                    date or order.date_order,
595
 
                                                    paid,
596
 
                                                    context)
597
 
            id and res.append(id)
598
 
        return res
599
 
 
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,
614
 
                context=context)
 
540
 
 
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:
 
547
            try:
 
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?
 
552
            return True
 
553
 
 
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))
 
567
            else:
 
568
                self.write(cr, uid, order_id, {'need_to_update': True}, context=context)
615
569
        return False
616
 
        
617
 
    def generate_payment_with_journal(self, cr, uid, journal_id, partner_id,
618
 
                                      amount, payment_ref, entry_name,
619
 
                                      date, should_validate, context):
620
 
        """
621
 
        Generate a voucher for the payment
622
 
 
623
 
        It will try to match with the invoice of the order by
624
 
        matching the payment ref and the invoice origin.
625
 
 
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.
630
 
 
631
 
        """
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')
635
 
 
636
 
        journal = self.pool.get('account.journal').browse(
637
 
            cr, uid, journal_id, context=context)
638
 
 
639
 
        voucher_vals = {'reference': entry_name,
640
 
                        'journal_id': journal.id,
641
 
                        'amount': amount,
642
 
                        'date': date,
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,
647
 
                        'type': 'receipt', }
648
 
        voucher_id = voucher_obj.create(cr, uid, voucher_vals, context=context)
649
 
 
650
 
        # call on change to search the invoice lines
651
 
        onchange_voucher = voucher_obj.onchange_partner_id(
652
 
            cr, uid, [],
653
 
            partner_id=partner_id,
654
 
            journal_id=journal.id,
655
 
            amount=amount,
656
 
            currency_id=journal.company_id.currency_id.id,
657
 
            ttype='receipt',
658
 
            date=date,
659
 
            context=context)['value']
660
 
 
661
 
        # keep in the voucher only the move line of the
662
 
        # invoice (eventually) created for this order
663
 
        matching_line = {}
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
672
 
                              in voucher_lines
673
 
                              if line['move_line_id'] in matching_ids]
674
 
            if matching_lines:
675
 
                matching_line = matching_lines[0]
676
 
                matching_line.update({
677
 
                    'amount': amount,
678
 
                    'voucher_id': voucher_id,
679
 
                })
680
 
 
681
 
        if matching_line:
682
 
            voucher_line_obj.create(cr, uid, matching_line, context=context)
683
 
 
684
 
        if should_validate:
685
 
            wf_service = netsvc.LocalService("workflow")
686
 
            wf_service.trg_validate(
687
 
                uid, 'account.voucher', voucher_id, 'proforma_voucher', cr)
688
 
        return voucher_id
689
 
 
690
 
    def oe_status(self, cr, uid, ids, paid=True, context=None):
691
 
        if type(ids) in [int, long]:
692
 
            ids = [ids]
693
 
 
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
698
 
 
699
 
            if payment_settings:
700
 
                if payment_settings.payment_term_id:
701
 
                    self.write(cr, uid, order.id, {'payment_term': payment_settings.payment_term_id.id})
702
 
 
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))
710
 
                    else:
711
 
                        self.write(cr, uid, order.id, {'need_to_update': True})
712
 
                else:
713
 
                    if payment_settings.validate_order:
714
 
                        try:
715
 
                            wf_service.trg_validate(uid, 'sale.order', order.id, 'order_confirm', cr)
716
 
                            self.write(cr, uid, order.id, {'need_to_update': False})
717
 
                        except Exception:
718
 
                            self.log(cr, uid, order.id, "ERROR could not valid order")
719
 
                            raise
720
 
                        
721
 
                        if payment_settings.validate_picking:
722
 
                            self.pool.get('stock.picking').validate_picking_from_order(cr, uid, order.id)
723
 
                        
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)
727
 
 
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)
734
 
            
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)
743
 
            
744
 
                        # IF postpaid DO NOTHING
745
 
            
746
 
                        elif order.order_policy == 'picking':
747
 
                            if payment_settings.create_invoice:
748
 
                                try:
749
 
                                    invoice_id = self.pool.get('stock.picking').action_invoice_create(cr, uid, [picking.id for picking in order.picking_ids])
750
 
                                except Exception, e:
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)
756
 
 
757
 
        return True
 
570
 
 
571
    def create_external_payment(self, cr, uid, external_session, order_id, resource, context):
 
572
        """
 
573
        Fonction that will create a payment from the external resource
 
574
        """
 
575
        vals = self._get_payment_information(cr, uid, external_session, order_id, resource, context=context)
 
576
        if vals.get('paid'):
 
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'])
 
579
                vals['paid'] = False
 
580
            else:
 
581
                self.pay_sale_order(cr, uid, order_id, vals['journal_id'], vals['amount'], vals['date'], context=context)
 
582
        return vals.get('paid')
 
583
 
 
584
    def _get_payment_information(self, cr, uid, external_session, order_id, resource, context=None):
 
585
        """
 
586
        Function that will return the information in order to create the payment
 
587
        """
 
588
        vals = {}
 
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
 
593
        return vals
758
594
 
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
772
608
            vals['journal_id'] = order.shop_id.sale_journal.id
773
609
        return vals
774
610
 
775
 
    def action_invoice_create(self, cr, uid, ids, grouped=False, states=['confirmed', 'done', 'exception'], date_inv = False, context=None):
776
 
        inv_obj = self.pool.get('account.invoice')
777
 
        job_obj = self.pool.get('base.sale.auto.reconcile.job')
778
 
        wf_service = netsvc.LocalService("workflow")
779
 
        res = super(sale_order, self).action_invoice_create(cr, uid, ids, grouped, states, date_inv, context)
780
 
        for order in self.browse(cr, uid, ids, context=context):
781
 
            payment_settings = order.base_payment_type_id
782
 
            if payment_settings and payment_settings.invoice_date_is_order_date:
783
 
                inv_obj.write(cr, uid, [inv.id for inv in order.invoice_ids], {'date_invoice' : order.date_order}, context=context)
784
 
            if order.order_policy == 'postpaid':
785
 
                if payment_settings and payment_settings.validate_invoice:
786
 
                    for invoice in order.invoice_ids:
787
 
                        wf_service.trg_validate(uid, 'account.invoice', invoice.id, 'invoice_open', cr)
788
 
                        if payment_settings.is_auto_reconcile:
789
 
                            # we could not auto-reconcile here because
790
 
                            # action_invoice_create is an action of the activity (subflow)
791
 
                            # invoice, and so the workflow is going crazy, and the
792
 
                            # activity never pass from "invoice" to "invoice_end"
793
 
                            # the signal subflow.paid never move the sale order
794
 
                            # workflow to invoice_end
795
 
                            # sale.order's workflow stucks in "progress"
796
 
                            # even if the invoice is paid and the picking delivered
797
 
                            #
798
 
                            # workaround: create an autoreconcile job to
799
 
                            # reconcile the payment later
800
 
                            # report for the workflow: https://bugs.launchpad.net/openobject-server/+bug/961919
801
 
                            # report for this module: https://bugs.launchpad.net/magentoerpconnect/+bug/957136
802
 
                            job_obj.create(
803
 
                                cr, uid, {'invoice_id': invoice.id}, context=context)
804
 
 
805
 
        return res
806
 
 
807
611
    def oe_update(self, cr, uid, existing_rec_id, vals, each_row, external_referential_id, defaults, context):
808
612
        '''Not implemented in this abstract module, if it's not implemented in your module it will raise an error'''
809
613
        # Explication :
820
624
                                "This feature is not supported. Maybe the import try to reimport an existing sale order"%(existing_rec_id,))))
821
625
        return existing_rec_id
822
626
 
823
 
 
824
627
    def _convert_special_fields(self, cr, uid, vals, referential_id, context=None):
825
628
        """
826
629
        Convert the special 'fake' field into an order line
961
764
        return line
962
765
 
963
766
sale_order_line()
964
 
 
965
 
class base_sale_payment_type(osv.osv):
966
 
    _name = "base.sale.payment.type"
967
 
    _description = "Base Sale Payment Type"
968
 
 
969
 
    _columns = {
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'),
991
 
    }
992
 
    
993
 
    _defaults = {
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,
1001
 
    }
1002
 
 
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
1014
 
                break
1015
 
        return payment_setting
1016
 
 
1017
 
base_sale_payment_type()
1018
 
 
1019
 
class account_invoice(osv.osv):
1020
 
    _inherit = "account.invoice"
1021
 
 
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(
1026
 
            cr, uid,
1027
 
            ['|', '|',
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)],
1035
 
            context=context)
1036
 
 
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(
1043
 
                cr, uid, 'Account')
1044
 
            if not round(balance, precision):
1045
 
                obj_move_line.reconcile(cr, uid, line_ids, context=context)
1046
 
                return True
1047
 
 
1048
 
        return False
1049
 
 
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)
1053
 
        return True
1054
 
 
1055
 
account_invoice()
1056
 
 
1057
 
class stock_picking(osv.osv):
1058
 
    _inherit = "stock.picking"
1059
 
    
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)
1066
 
        return True
1067
 
        
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])
1071
 
            partial_data = {}
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)
1075
 
        return True
1076
 
        
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
1078
 
        if context is None:
1079
 
            context = {}
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)
1091
 
        return True
1092
 
        
1093
 
stock_picking()
1094
767
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: