Exemplo n.º 1
0
 def _create_invoice_from_file(self, attachment):
     self = self.with_context(default_journal_id=self.journal_id.id)
     invoice_form = Form(self.env['account.invoice'], view='account.invoice_supplier_form')
     invoice = invoice_form.save()
     attachment.write({'res_model': 'account.invoice', 'res_id': invoice.id})
     invoice.message_post(attachment_ids=[attachment.id])
     return invoice
Exemplo n.º 2
0
    def test_defaults(self):
        """
        Checks that we can load a default form view and perform trivial
        default_get & onchanges & computations
        """
        f = Form(self.env['test_testing_utilities.a'])
        self.assertEqual(f.id, False, "check that our record is not in db (yet)")

        self.assertEqual(f.f2, 42)
        self.assertEqual(f.f3, 21)
        self.assertEqual(f.f4, 42)

        f.f1 = 4
        self.assertEqual(f.f2, 42)
        self.assertEqual(f.f3, 21)
        self.assertEqual(f.f4, 10)

        f.f2 = 8
        self.assertEqual(f.f3, 4)
        self.assertEqual(f.f4, 2)

        r = f.save()
        self.assertEqual(
            (r.f1, r.f2, r.f3, r.f4),
            (4, 8, 4, 2),
        )
Exemplo n.º 3
0
    def test_o2m_editable_list(self):
        """ Tests the o2m proxy when the list view is editable rather than
        delegating to a separate form view
        """
        f = Form(self.env['test_testing_utilities.parent'], view='test_testing_utilities.o2m_parent_ed')
        custom_tree = self.env.ref('test_testing_utilities.editable_external').id

        subs_field = f._view['fields']['subs']
        tree_view = subs_field['views']['tree']
        self.assertEqual(tree_view['type'], 'tree')
        self.assertEqual(
            tree_view['view_id'], custom_tree,
            'check that the tree view is the one referenced by tree_view_ref'
        )
        self.assertIs(subs_field['views']['edition'], tree_view, "check that the edition view is the tree view")
        self.assertEqual(
            subs_field['views']['edition']['view_id'],
            custom_tree
        )

        with f.subs.new() as s:
            s.value = 1
        with f.subs.new() as s:
            s.value = 3
        with f.subs.new() as s:
            s.value = 7

        r = f.save()

        self.assertEqual(r.v, 12)
        self.assertEqual(
            [get(s) for s in r.subs],
            [('1', 1, 1), ('3', 3, 3), ('7', 7, 7)]
        )
Exemplo n.º 4
0
 def create_payment(self, invoices):
     payment_register = Form(self.env['account.payment'].with_context(active_model='account.invoice', active_ids=invoices.ids))
     payment_register.payment_date = time.strftime('%Y') + '-07-15'
     payment_register.journal_id = self.bank_journal
     payment_register.payment_method_id = self.payment_method_check
     payment = payment_register.save()
     payment.post()
     return payment
Exemplo n.º 5
0
    def test_readonly(self):
        """
        Checks that fields with readonly modifiers (marked as readonly or
        computed w/o set) raise an error when set.
        """
        f = Form(self.env['test_testing_utilities.readonly'])

        with self.assertRaises(AssertionError):
            f.f1 = 5
        with self.assertRaises(AssertionError):
            f.f2 = 42
Exemplo n.º 6
0
    def test_readonly_save(self):
        """ Should not save readonly fields unless they're force_save
        """
        f = Form(self.env['test_testing_utilities.a'], view='test_testing_utilities.non_normalized_attrs')

        f.f1 = '1'
        f.f2 = 987
        self.assertEqual(f.f5, 987)
        self.assertEqual(f.f6, 987)
        r = f.save()
        self.assertEqual(r.f5, 0)
        self.assertEqual(r.f6, 987)
Exemplo n.º 7
0
 def test_department_leave(self):
     """ Create a department leave """
     self.employee_hrmanager.write({'department_id': self.hr_dept.id})
     self.assertFalse(self.env['hr.leave'].search([('employee_id', 'in', self.hr_dept.member_ids.ids)]))
     leave_form = Form(self.env['hr.leave'].sudo(self.user_hrmanager), view='hr_holidays.hr_leave_view_form_manager')
     leave_form.holiday_status_id = self.holidays_type_1
     leave_form.holiday_type = 'department'
     leave_form.department_id = self.hr_dept
     leave = leave_form.save()
     leave.action_approve()
     member_ids = self.hr_dept.member_ids.ids
     self.assertEqual(self.env['hr.leave'].search_count([('employee_id', 'in', member_ids)]), len(member_ids), "Leave should be created for members of department")
Exemplo n.º 8
0
    def test_attrs(self):
        """ Checks that attrs/modifiers with non-normalized domains work
        """
        f = Form(self.env['test_testing_utilities.a'], view='test_testing_utilities.non_normalized_attrs')

        # not readonly yet, should work
        f.f2 = 5
        # make f2 readonly
        f.f1 = 63
        f.f3 = 5
        with self.assertRaises(AssertionError):
            f.f2 = 6
    def test_onchange_taxes_2(self):
        '''
        Test the amount of tax account.move.line is adapted when editing the account.move.line amount.
        This test uses the following scenario:
            - Create manually a debit line of 1000 having a tax.
            - Assume a line containing the tax amount is created automatically.
            - Set the debit amount to 2000 in the first created line.
            - Assume the line containing the tax amount has been updated automatically.
            - Create manually a credit line to balance the two previous lines.
            - Save the move.

        tax = 10%

        Name            | Debit     | Credit    | Tax_ids       | Tax_line_id's name
        ----------------|-----------|-----------|---------------|-------------------
        debit_line_1    | 2000      |           | tax           |
        tax_line        | 200       |           |               | tax_line
        debit_line_1    |           | 2200      |               |
        '''
        move_form = Form(self.env['account.move'], view='account.view_move_form')
        move_form.ref = 'azerty'
        move_form.journal_id = self.journal

        # Create a new account.move.line with debit amount.
        with move_form.line_ids.new() as debit_line:
            debit_line.name = 'debit_line_1'
            debit_line.account_id = self.account
            debit_line.debit = 1000
            debit_line.tax_ids.clear()
            debit_line.tax_ids.add(self.percent_tax)

            self.assertTrue(debit_line.recompute_tax_line)

            debit_line.debit = 2000

            self.assertTrue(debit_line.recompute_tax_line)

        # Create a third account.move.line with credit amount.
        with move_form.line_ids.new() as credit_line:
            credit_line.name = 'credit_line_1'
            credit_line.account_id = self.account
            credit_line.credit = 2200

        move = move_form.save()

        self.check_complete_move(move, [
            ['debit_line_1',    2000,   0,      [self.percent_tax.id],  None],
            ['tax_line',        200,    0,      None,                   self.percent_tax.id],
            ['credit_line_1',   0,      2200,   None,                   None],
        ], fields_name=['name', 'debit', 'credit', 'tax_ids', 'tax_line_id'])
Exemplo n.º 10
0
    def test_o2m_default(self):
        """ Tests that default_get can return defaults for the o2m
        """
        f = Form(self.env['test_testing_utilities.default'])

        with f.subs.edit(index=0) as s:
            self.assertEqual(s.v, 5)
            self.assertEqual(s.value, False)

        r = f.save()

        self.assertEqual(
            [get(s) for s in r.subs],
            [("5", 2, 5)]
        )
Exemplo n.º 11
0
    def test_m2m_changed(self):
        Sub = self.env['test_testing_utilities.sub2']
        a = Sub.create({'name': 'a'})
        b = Sub.create({'name': 'b'})
        c = Sub.create({'name': 'c'})
        d = Sub.create({'name': 'd'})

        f = Form(self.env['test_testing_utilities.f'])
        # check default_get
        self.assertEqual(f.m2m[:], a | b)

        f.m2o = c
        self.assertEqual(f.m2m[:], a | b | c)

        f.m2o = d
        self.assertEqual(f.m2m[:], a | b | c | d)
Exemplo n.º 12
0
    def test_o2m_inline(self):
        """ Tests the o2m proxy when the list and form views are provided
        inline rather than fetched separately
        """
        f = Form(self.env['test_testing_utilities.parent'], view='test_testing_utilities.o2m_parent_inline')

        with f.subs.new() as s:
            s.value = 42

        r = f.save()

        self.assertEqual(
            [get(s) for s in r.subs],
            [("0", 42, 0)],
            "should not have set v (and thus not name)"
        )
Exemplo n.º 13
0
    def test_add(self):
        Sub = self.env['test_testing_utilities.sub2']
        f = Form(self.env['test_testing_utilities.e'])

        r1 = Sub.create({'name': "Item"})
        r2 = Sub.create({'name': "Item2"})

        f.m2m.add(r1)
        f.m2m.add(r2)

        r = f.save()

        self.assertEqual(
            r.m2m,
            r1 | r2
        )
Exemplo n.º 14
0
    def test_remove_by_index(self):
        Sub = self.env['test_testing_utilities.sub2']
        f = Form(self.env['test_testing_utilities.e'])

        r1 = Sub.create({'name': "Item"})
        r2 = Sub.create({'name': "Item2"})

        f.m2m.add(r1)
        f.m2m.add(r2)
        f.m2m.remove(index=0)

        r = f.save()

        self.assertEqual(
            r.m2m,
            r2
        )
Exemplo n.º 15
0
    def test_m2m_readonly(self):
        Sub = self.env['test_testing_utilities.sub3']
        a = Sub.create({'name': 'a'})
        b = Sub.create({'name': 'b'})
        r = self.env['test_testing_utilities.g'].create({
            'm2m': [(6, 0, a.ids)]
        })

        f = Form(r)

        with self.assertRaises(AssertionError):
            f.m2m.add(b)
        with self.assertRaises(AssertionError):
            f.m2m.remove(id=a.id)

        f.save()
        self.assertEqual(r.m2m, a)
Exemplo n.º 16
0
    def test_o2m_remove(self):
        def commands():
            return [c[0] for c in f._values['line_ids']]
        f = Form(self.env['test_testing_utilities.onchange_count'])

        self.assertEqual(f.count, 0)
        self.assertEqual(len(f.line_ids), 0)

        f.count = 5
        self.assertEqual(f.count, 5)
        self.assertEqual(len(f.line_ids), 5)

        f.count = 2
        self.assertEqual(f.count, 2)
        self.assertEqual(len(f.line_ids), 2)

        f.count = 4

        r = f.save()

        previous = r.line_ids
        self.assertEqual(len(previous), 4)

        with Form(r) as f:
            f.count = 2
            self.assertEqual(commands(), [0, 0, 2, 2, 2, 2], "Should contain 2 creations and 4 deletions")
        self.assertEqual(len(r.line_ids), 2)

        with Form(r) as f:
            f.line_ids.remove(0)
            self.assertEqual(commands(), [2, 1])
            f.count = 1
            self.assertEqual(commands(), [0, 2, 2], "should contain 1 '0' command and 2 deletions")
        self.assertEqual(len(r.line_ids), 1)
    def test_onchange_taxes_1(self):
        '''
        Test an account.move.line is created automatically when adding a tax.
        This test uses the following scenario:
            - Create manually a debit line of 1000 having a tax.
            - Assume a line containing the tax amount is created automatically.
            - Create manually a credit line to balance the two previous lines.
            - Save the move.

        tax = 10%

        Name            | Debit     | Credit    | Tax_ids       | Tax_line_id's name
        ----------------|-----------|-----------|---------------|-------------------
        debit_line_1    | 1000      |           | tax           |
        tax_line        | 100       |           |               | tax_line
        debit_line_1    |           | 1100      |               |
        '''
        move_form = Form(self.env['account.move'], view='account.view_move_form')
        move_form.ref = 'azerty'
        move_form.journal_id = self.journal

        # Create a new account.move.line with debit amount.
        with move_form.line_ids.new() as debit_line:
            debit_line.name = 'debit_line_1'
            debit_line.account_id = self.account
            debit_line.debit = 1000
            debit_line.tax_ids.clear()
            debit_line.tax_ids.add(self.percent_tax)

            self.assertTrue(debit_line.recompute_tax_line)

        # Create a third account.move.line with credit amount.
        with move_form.line_ids.new() as credit_line:
            credit_line.name = 'credit_line_1'
            credit_line.account_id = self.account
            credit_line.credit = 1100

        move = move_form.save()

        self.assertRecordValues(move.line_ids, [
            {'name': 'credit_line_1',   'debit': 0.0,       'credit': 1100.0,   'tax_ids': [],                      'tax_line_id': False},
            {'name': 'tax_line',        'debit': 100.0,     'credit': 0.0,      'tax_ids': [],                      'tax_line_id': self.percent_tax.id},
            {'name': 'debit_line_1', 'debit': 1000.0, 'credit': 0.0, 'tax_ids': [self.percent_tax.id], 'tax_line_id': False},
        ])
Exemplo n.º 18
0
    def test_basic(self):
        env = self.env(context=dict(self.env.context, journal_type='bank'))

        # select the period and journal for the bank statement
        journal = env['account.bank.statement'].with_context(
            date=time.strftime("%Y/%m/%d"),  # ???
        )._default_journal()
        self.assertTrue(journal, 'Journal has not been selected')

        f = Form(env['account.bank.statement'])
        # necessary as there may be existing bank statements with a non-zero
        # closing balance which will be used to initialise this one.
        f.balance_start = 0.0
        f.balance_end_real = 0.0
        with f.line_ids.new() as line:
            line.name = 'EXT001'
            line.amount = 1000
            line.partner_id = env.ref('base.res_partner_4')

        statement_id = f.save()

        # process the bank statement line
        account = env['account.account'].create({
            'name': 'toto',
            'code': 'bidule',
            'user_type_id': env.ref('account.data_account_type_fixed_assets').id
        })
        statement_id.line_ids[0].process_reconciliation(new_aml_dicts=[{
            'credit': 1000,
            'debit': 0,
            'name': 'toto',
            'account_id': account.id,
        }])

        with Form(statement_id) as f:
            # modify the bank statement and set the Ending Balance.
            f.balance_end_real = 1000.0

        # confirm the bank statement using Validate button
        statement_id.button_confirm_bank()

        self.assertEqual(statement_id.state, 'confirm')
Exemplo n.º 19
0
    def test_default_and_onchange(self):
        """ Checks defaults & onchanges impacting m2o fields
        """
        Sub = self.env['test_testing_utilities.m2o']
        a = Sub.create({'name': "A"})
        b = Sub.create({'name': "B"})

        f = Form(self.env['test_testing_utilities.d'])

        self.assertEqual(
            f.f, a,
            "The default value for the m2o should be the first Sub record"
        )
        f.f2 = "B"
        self.assertEqual(
            f.f, b,
            "The new m2o value should match the second field by name"
        )

        f.save()
Exemplo n.º 20
0
    def test_o2m_editable_list(self):
        """ Tests the o2m proxy when the list view is editable rather than
        delegating to a separate form view
        """
        f = Form(self.env['test_testing_utilities.parent'], view='test_testing_utilities.o2m_parent_ed')

        with f.subs.new() as s:
            s.value = 1
        with f.subs.new() as s:
            s.value = 3
        with f.subs.new() as s:
            s.value = 7

        r = f.save()

        self.assertEqual(r.v, 12)
        self.assertEqual(
            [get(s) for s in r.subs],
            [('1', 1, 1), ('3', 3, 3), ('7', 7, 7)]
        )
Exemplo n.º 21
0
    def test_default_and_onchange(self):
        """ Checks defaults & onchanges impacting m2o fields
        """
        Sub = self.env['test_testing_utilities.m2o']
        a = Sub.create({'name': "A"})
        b = Sub.create({'name': "B"})

        f = Form(self.env['test_testing_utilities.d'])

        self.assertFalse(
            f.f,
            "The default value gets overridden by the onchange"
        )
        f.f2 = "B"
        self.assertEqual(
            f.f, b,
            "The new m2o value should match the second field by name"
        )

        f.save()
Exemplo n.º 22
0
    def test_state(self):
        f = Form(self.env['account.invoice'])
        f.partner_id = self.env.ref('base.res_partner_12')
        with f.invoice_line_ids.new() as l:
            l.product_id = self.env.ref('product.product_product_3')
        invoice = f.save()

        # I check that Initially customer invoice state is "Draft"
        self.assertEqual(invoice.state, 'draft')

        # I called the "Confirm Draft Invoices" wizard
        w = Form(self.env['account.invoice.confirm']).save()
        # I clicked on Confirm Invoices Button
        w.with_context(
            active_model='account.invoice',
            active_id=invoice.id,
            active_ids=invoice.ids,
            type='out_invoice',
        ).invoice_confirm()

        # I check that customer invoice state is "Open"
        self.assertEqual(invoice.state, 'open')

        # Electronic invoice must be present and have the same name as l10n_it_einvoice_name
        self.assertEqual(invoice.l10n_it_einvoice_id.name, invoice.l10n_it_einvoice_name)
Exemplo n.º 23
0
    def test_set(self):
        """
        Checks that we get/set recordsets for m2o & that set correctly
        triggers onchange
        """
        r1 = self.env['test_testing_utilities.m2o'].create({'name': "A"})
        r2 = self.env['test_testing_utilities.m2o'].create({'name': "B"})

        f = Form(self.env['test_testing_utilities.c'])

        # check that basic manipulations work
        f.f2 = r1
        self.assertEqual(f.f2, r1)
        self.assertEqual(f.name, 'A')
        f.f2 = r2
        self.assertEqual(f.name, 'B')

        # can't set an int to an m2o field
        with self.assertRaises(AssertionError):
            f.f2 = r1.id
        self.assertEqual(f.f2, r2)
        self.assertEqual(f.name, 'B')

        # can't set a record of the wrong model
        temp = self.env['test_testing_utilities.readonly'].create({})
        with self.assertRaises(AssertionError):
            f.f2 = temp
        self.assertEqual(f.f2, r2)
        self.assertEqual(f.name, 'B')

        r = f.save()
        self.assertEqual(r.f2, r2)
Exemplo n.º 24
0
    def test_basic_alterations(self):
        """ Tests that the o2m proxy allows adding, removing and editing o2m
        records
        """
        f = Form(self.env['test_testing_utilities.parent'], view='test_testing_utilities.o2m_parent')

        f.subs.new().save()
        f.subs.new().save()
        f.subs.new().save()
        f.subs.remove(index=0)

        r = f.save()

        self.assertEqual(
            [get(s) for s in r.subs],
            [("2", 2, 2), ("2", 2, 2)]
        )
        self.assertEqual(r.v, 5)

        with Form(r, view='test_testing_utilities.o2m_parent') as f:
            with f.subs.new() as sub:
                sub.value = 5
            f.subs.new().save()

            with f.subs.edit(index=2) as sub:
                self.assertEqual(sub.v, 5)

            f.subs.remove(index=0)

        self.assertEqual(
            [get(s) for s in r.subs],
            [("2", 2, 2), ("5", 5, 5), ("2", 2, 2)]
        )
        self.assertEqual(r.v, 10)

        with Form(r, view='test_testing_utilities.o2m_parent') as f, \
            f.subs.edit(index=0) as sub,\
            self.assertRaises(AssertionError):
                sub.name = "whop whop"
Exemplo n.º 25
0
 def _create_product(self, name, uom_id, routes=()):
     p = Form(self.env['product.product'])
     p.name = name
     p.type = 'product'
     p.uom_id = uom_id
     p.uom_po_id = uom_id
     p.route_ids.clear()
     for r in routes:
         p.route_ids.add(r)
     return p.save()
Exemplo n.º 26
0
 def test_required(self):
     f = Form(self.env['test_testing_utilities.a'])
     # f1 no default & no value => should fail
     with self.assertRaisesRegexp(AssertionError, 'f1 is a required field'):
         f.save()
     # set f1 and unset f2 => should work
     f.f1 = 1
     f.f2 = False
     r = f.save()
     self.assertEqual(
         (r.f1, r.f2, r.f3, r.f4),
         (1, 0, 0, 0)
     )
Exemplo n.º 27
0
    def _create_move_quantities(self, qty_to_process, components, warehouse):
        """ Helper to creates moves in order to update the quantities of components
        on a specific warehouse. This ensure that all compute fields are triggered.
        The structure of qty_to_process should be the following :

         qty_to_process = {
            component: (qty, uom),
            ...
        }
        """
        for comp in components:
            f = Form(self.env['stock.move'])
            f.name = 'Test Receipt Components'
            f.location_id = self.env.ref('stock.stock_location_suppliers')
            f.location_dest_id = warehouse.lot_stock_id
            f.product_id = comp
            f.product_uom = qty_to_process[comp][1]
            f.product_uom_qty = qty_to_process[comp][0]
            move = f.save()
            move._action_confirm()
            move._action_assign()
            move_line = move.move_line_ids[0]
            move_line.qty_done = qty_to_process[comp][0]
            move._action_done()
Exemplo n.º 28
0
    def test_state(self):
        # In order to test Confirm Draft Invoice wizard I create an invoice
        # and confirm it with this wizard
        f = Form(self.env['account.invoice'])
        f.partner_id = self.env.ref('base.res_partner_12')
        with f.invoice_line_ids.new() as l:
            l.product_id = self.env.ref('product.product_product_3')
        invoice = f.save()

        # I check that Initially customer invoice state is "Draft"
        self.assertEqual(invoice.state, 'draft')

        # I called the "Confirm Draft Invoices" wizard
        w = Form(self.env['account.invoice.confirm']).save()
        # I clicked on Confirm Invoices Button
        w.with_context(
            active_model='account.invoice',
            active_id=invoice.id,
            active_ids=invoice.ids,
            type='out_invoice',
        ).invoice_confirm()

        # I check that customer invoice state is "Open"
        self.assertEqual(invoice.state, 'open')

        # I check the journal associated and put this journal as not
        moves = self.env['account.move.line'].search([
            ('invoice_id', '=', invoice.id)
        ])
        self.assertGreater(len(moves), 0, 'You should have multiple moves')
        moves[0].journal_id.write({'update_posted': True})

        # I cancelled this open invoice using the button on invoice
        invoice.action_invoice_cancel()
        # I check that customer invoice is in the cancel state
        self.assertEqual(invoice.state, 'cancel')
Exemplo n.º 29
0
                'amount_currency': 208.006,
                'debit': 69.33,
            },
        ], {
            **self.move_vals,
            'currency_id': self.currency_data['currency'].id,
            'date': fields.Date.from_string('2016-01-01'),
            'amount_untaxed': 160.005,
            'amount_tax': 48.001,
            'amount_total': 208.006,
        })

<<<<<<< HEAD
        # The journal forces you to provide a secondary currency.
        with self.assertRaises(UserError), self.cr.savepoint():
            move_form = Form(self.invoice)
            move_form.currency_id = self.company_data['currency']
            move_form.save()

        # Exit the multi-currencies.
        journal.currency_id = False
=======
        # Exit the multi-currencies.
>>>>>>> f0a66d05e70e432d35dc68c9fb1e1cc6e51b40b8
        move_form = Form(self.invoice)
        move_form.currency_id = self.company_data['currency']
        move_form.save()

        self.assertInvoiceValues(self.invoice, [
            {
                **self.product_line_vals_1,
Exemplo n.º 30
0
    def test_out_refund_line_onchange_accounting_fields_1(self):
        move_form = Form(self.invoice)
        with move_form.line_ids.edit(2) as line_form:
            # Custom debit on the first product line.
            line_form.debit = 3000
        with move_form.line_ids.edit(3) as line_form:
            # Custom credit on the second product line. Credit should be reset by onchange.
            # /!\ It's a negative line.
            line_form.credit = 500
        with move_form.line_ids.edit(0) as line_form:
            # Custom debit on the first tax line.
            line_form.debit = 800
        with move_form.line_ids.edit(4) as line_form:
            # Custom debit on the second tax line.
            line_form.debit = 250
        move_form.save()

        self.assertInvoiceValues(
            self.invoice, [
                {
                    **self.product_line_vals_1,
                    'price_unit': 3000.0,
                    'price_subtotal': 3000.0,
                    'price_total': 3450.0,
                    'amount_currency': 3000.0,
                    'debit': 3000.0,
                },
                {
                    **self.product_line_vals_2,
                    'price_unit': -500.0,
                    'price_subtotal': -500.0,
                    'price_total': -650.0,
                    'amount_currency': -500.0,
                    'debit': 0.0,
                    'credit': 500.0,
                },
                {
                    **self.tax_line_vals_1,
                    'price_unit': 800.0,
                    'price_subtotal': 800.0,
                    'price_total': 800.0,
                    'amount_currency': 800.0,
                    'debit': 800.0,
                },
                {
                    **self.tax_line_vals_2,
                    'price_unit': 250.0,
                    'price_subtotal': 250.0,
                    'price_total': 250.0,
                    'amount_currency': 250.0,
                    'debit': 250.0,
                },
                {
                    **self.term_line_vals_1,
                    'price_unit': -3550.0,
                    'price_subtotal': -3550.0,
                    'price_total': -3550.0,
                    'amount_currency': -3550.0,
                    'credit': 3550.0,
                },
            ], {
                **self.move_vals,
                'amount_untaxed': 2500.0,
                'amount_tax': 1050.0,
                'amount_total': 3550.0,
            })
 def _create_stock_location(self, name):
     stock_location_form = Form(self.env["stock.location"])
     stock_location_form.name = name
     stock_location_form.usage = self.env.ref(
         "stock.stock_location_stock").usage
     return stock_location_form.save()
Exemplo n.º 32
0
    def test_reordering_rule_2(self):
        """Test when there is not enough product to assign a picking => automatically run
        reordering rule (RR). Add extra product to already confirmed picking => automatically
        run another RR
        """
        self.productA = self.env['product.product'].create({
            'name': 'Desk Combination',
            'type': 'product',
        })

        self.productB = self.env['product.product'].create({
            'name': 'Desk Decoration',
            'type': 'product',
        })

        warehouse = self.env['stock.warehouse'].search([], limit=1)
        orderpoint_form = Form(self.env['stock.warehouse.orderpoint'])
        orderpoint_form.product_id = self.productA
        orderpoint_form.location_id = warehouse.lot_stock_id
        orderpoint_form.product_min_qty = 0.0
        orderpoint_form.product_max_qty = 5.0
        orderpoint = orderpoint_form.save()

        self.env['stock.warehouse.orderpoint'].create({
            'name':
            'ProductB RR',
            'location_id':
            warehouse.lot_stock_id.id,
            'product_id':
            self.productB.id,
            'product_min_qty':
            0,
            'product_max_qty':
            5,
        })

        self.env['stock.rule'].create({
            'name':
            'Rule Supplier',
            'route_id':
            warehouse.reception_route_id.id,
            'location_id':
            warehouse.lot_stock_id.id,
            'location_src_id':
            self.env.ref('stock.stock_location_suppliers').id,
            'action':
            'pull',
            'delay':
            9.0,
            'procure_method':
            'make_to_stock',
            'picking_type_id':
            warehouse.in_type_id.id,
        })

        delivery_picking = self.env['stock.picking'].create({
            'location_id':
            warehouse.lot_stock_id.id,
            'location_dest_id':
            self.ref('stock.stock_location_customers'),
            'picking_type_id':
            self.ref('stock.picking_type_out'),
        })
        delivery_move = self.env['stock.move'].create({
            'name':
            'Delivery',
            'product_id':
            self.productA.id,
            'product_uom':
            self.uom_unit.id,
            'product_uom_qty':
            12.0,
            'location_id':
            warehouse.lot_stock_id.id,
            'location_dest_id':
            self.ref('stock.stock_location_customers'),
            'picking_id':
            delivery_picking.id,
        })
        delivery_picking.action_confirm()
        delivery_picking.action_assign()

        receipt_move = self.env['stock.move'].search([
            ('product_id', '=', self.productA.id),
            ('location_id', '=',
             self.env.ref('stock.stock_location_suppliers').id)
        ])

        self.assertTrue(receipt_move)
        self.assertEqual(receipt_move.date.date(), date.today())
        self.assertEqual(receipt_move.product_uom_qty, 17.0)

        delivery_picking.write({
            'move_lines': [(0, 0, {
                'name':
                'Extra Move',
                'product_id':
                self.productB.id,
                'product_uom':
                self.uom_unit.id,
                'product_uom_qty':
                5.0,
                'location_id':
                warehouse.lot_stock_id.id,
                'location_dest_id':
                self.ref('stock.stock_location_customers'),
                'picking_id':
                delivery_picking.id,
                'additional':
                True
            })]
        })

        receipt_move2 = self.env['stock.move'].search([
            ('product_id', '=', self.productB.id),
            ('location_id', '=',
             self.env.ref('stock.stock_location_suppliers').id)
        ])

        self.assertTrue(receipt_move2)
        self.assertEqual(receipt_move2.date.date(), date.today())
        self.assertEqual(receipt_move2.product_uom_qty, 10.0)
>>>>>>> f0a66d05e70e432d35dc68c9fb1e1cc6e51b40b8
            'partner_id': cls.partner_2.id,
            'amount': -1000,
            'sequence': 1,
        })

        cls.tax21 = cls.env['account.tax'].create({
            'name': '21%',
            'type_tax_use': 'purchase',
            'amount': 21,
        })

    @classmethod
    def _create_invoice_line(cls, amount, partner, type):
        ''' Create an invoice on the fly.'''
        invoice_form = Form(cls.env['account.move'].with_context(default_move_type=type, default_invoice_date='2019-09-01', default_date='2019-09-01'))
        invoice_form.partner_id = partner
        with invoice_form.invoice_line_ids.new() as invoice_line_form:
            invoice_line_form.name = 'xxxx'
            invoice_line_form.quantity = 1
            invoice_line_form.price_unit = amount
            invoice_line_form.tax_ids.clear()
        invoice = invoice_form.save()
        invoice.post()
        lines = invoice.line_ids
        return lines.filtered(lambda l: l.account_id.user_type_id.type in ('receivable', 'payable'))

    def _post_statements(self):
        self.bank_st.balance_end_real = self.bank_st.balance_end
        self.cash_st.balance_end_real = self.cash_st.balance_end
        (self.bank_st + self.cash_st).button_post()
Exemplo n.º 34
0
    def setUp(self):
        self.env.ref('base.main_company').currency_id = self.env.ref(
            'base.USD')
        super(TestPayment, self).setUp()
        self.register_payments_model = self.env[
            'account.payment.register'].with_context(
                active_model='account.move')
        self.payment_model = self.env['account.payment']
        self.acc_bank_stmt_model = self.env['account.bank.statement']
        self.acc_bank_stmt_line_model = self.env['account.bank.statement.line']

        self.partner_agrolait = self.env['res.partner'].create({
            'name':
            'Agrolait',
            'is_company':
            True
        })
        self.partner_china_exp = self.env['res.partner'].create({
            'name':
            'China Export',
            'is_company':
            True
        })
        self.currency_chf_id = self.env.ref("base.CHF").id
        self.currency_usd_id = self.env.ref("base.USD").id
        self.currency_eur_id = self.env.ref("base.EUR").id

        company = self.env.ref('base.main_company')
        self.cr.execute(
            "UPDATE res_company SET currency_id = %s WHERE id = %s",
            [self.currency_eur_id, company.id])

        self.product = self.env['product.product'].create({
            'name':
            'Product Product 4',
            'standard_price':
            500.0,
            'list_price':
            750.0,
            'type':
            'consu',
            'categ_id':
            self.env.ref('product.product_category_all').id,
        })
        self.payment_method_manual_in = self.env.ref(
            "account.account_payment_method_manual_in")
        self.payment_method_manual_out = self.env.ref(
            "account.account_payment_method_manual_out")

        self.account_receivable = self.env['account.account'].search(
            [('user_type_id', '=',
              self.env.ref('account.data_account_type_receivable').id)],
            limit=1)
        self.account_payable = self.env['account.account'].search(
            [('user_type_id', '=',
              self.env.ref('account.data_account_type_payable').id)],
            limit=1)
        self.account_revenue = self.env['account.account'].search(
            [('user_type_id', '=',
              self.env.ref('account.data_account_type_revenue').id)],
            limit=1)

        self.bank_journal_euro = self.env['account.journal'].create({
            'name':
            'Bank',
            'type':
            'bank',
            'code':
            'BNK67'
        })
        self.account_eur = self.bank_journal_euro.default_debit_account_id

        self.cash_journal_euro = self.env['account.journal'].create({
            'name':
            'Cash',
            'type':
            'cash',
            'code':
            'CASH'
        })

        self.bank_journal_usd = self.env['account.journal'].create({
            'name':
            'Bank US',
            'type':
            'bank',
            'code':
            'BNK68',
            'currency_id':
            self.currency_usd_id
        })
        self.account_usd = self.bank_journal_usd.default_debit_account_id

        if not self.env.user.company_id.transfer_account_id:
            self.env.user.company_id.transfer_account_id = self.usd_bnk
        self.transfer_account = self.env.user.company_id.transfer_account_id
        self.diff_income_account = self.env.user.company_id.income_currency_exchange_account_id
        self.diff_expense_account = self.env.user.company_id.expense_currency_exchange_account_id

        self.form_payment = Form(self.env['account.payment'])

        self.env['res.currency.rate'].create([{
            'currency_id':
            self.env.ref('base.EUR').id,
            'name':
            '2010-01-02',
            'rate':
            1.0,
        }, {
            'currency_id':
            self.env.ref('base.USD').id,
            'name':
            '2010-01-02',
            'rate':
            1.2834,
        }, {
            'currency_id':
            self.env.ref('base.USD').id,
            'name':
            time.strftime('%Y-06-05'),
            'rate':
            1.5289,
        }])
Exemplo n.º 35
0
    def test_report_forecast_2_production_backorder(self):
        """ Creates a manufacturing order and produces half the quantity.
        Then creates a backorder and checks the report.
        """
        # Configures the warehouse.
        warehouse = self.env.ref('stock.warehouse0')
        warehouse.manufacture_steps = 'pbm_sam'
        # Configures a product.
        product_apple_pie = self.env['product.product'].create({
            'name':
            'Apple Pie',
            'type':
            'product',
        })
        product_apple = self.env['product.product'].create({
            'name': 'Apple',
            'type': 'consu',
        })
        bom = self.env['mrp.bom'].create({
            'product_id':
            product_apple_pie.id,
            'product_tmpl_id':
            product_apple_pie.product_tmpl_id.id,
            'product_uom_id':
            product_apple_pie.uom_id.id,
            'product_qty':
            1.0,
            'type':
            'normal',
            'bom_line_ids': [
                (0, 0, {
                    'product_id': product_apple.id,
                    'product_qty': 5
                }),
            ],
        })
        # Creates a MO and validates the pick components.
        mo_form = Form(self.env['mrp.production'])
        mo_form.product_id = product_apple_pie
        mo_form.bom_id = bom
        mo_form.product_qty = 4
        mo_1 = mo_form.save()
        mo_1.action_confirm()
        pick = mo_1.move_raw_ids.move_orig_ids.picking_id
        pick_form = Form(pick)
        with pick_form.move_line_ids_without_package.edit(0) as move_line:
            move_line.qty_done = 20
        pick = pick_form.save()
        pick.button_validate()
        # Produces 3 products then creates a backorder for the remaining product.
        mo_form = Form(mo_1)
        mo_form.qty_producing = 3
        mo_1 = mo_form.save()
        action = mo_1.button_mark_done()
        backorder_form = Form(
            self.env['mrp.production.backorder'].with_context(
                **action['context']))
        backorder = backorder_form.save()
        backorder.action_backorder()

        mo_2 = (mo_1.procurement_group_id.mrp_production_ids - mo_1)
        # Checks the forecast report.
        report_values, docs, lines = self.get_report_forecast(
            product_template_ids=product_apple_pie.product_tmpl_id.ids)
        self.assertEqual(len(lines), 1,
                         "Must have only one line about the backorder")
        self.assertEqual(lines[0]['document_in'].id, mo_2.id)
        self.assertEqual(lines[0]['quantity'], 1)
        self.assertEqual(lines[0]['document_out'], False)

        # Produces the last unit.
        mo_form = Form(mo_2)
        mo_form.qty_producing = 1
        mo_2 = mo_form.save()
        mo_2.button_mark_done()
        # Checks the forecast report.
        report_values, docs, lines = self.get_report_forecast(
            product_template_ids=product_apple_pie.product_tmpl_id.ids)
        self.assertEqual(len(lines), 0, "Must have no line")
Exemplo n.º 36
0
    def test_out_refund_line_onchange_currency_1(self):
        move_form = Form(self.invoice)
        move_form.currency_id = self.currency_data['currency']
        move_form.save()

        self.assertInvoiceValues(
            self.invoice, [
                {
                    **self.product_line_vals_1,
                    'currency_id':
                    self.currency_data['currency'].id,
                    'amount_currency':
                    1000.0,
                    'debit':
                    500.0,
                },
                {
                    **self.product_line_vals_2,
                    'currency_id':
                    self.currency_data['currency'].id,
                    'amount_currency':
                    200.0,
                    'debit':
                    100.0,
                },
                {
                    **self.tax_line_vals_1,
                    'currency_id':
                    self.currency_data['currency'].id,
                    'amount_currency':
                    180.0,
                    'debit':
                    90.0,
                },
                {
                    **self.tax_line_vals_2,
                    'currency_id':
                    self.currency_data['currency'].id,
                    'amount_currency':
                    30.0,
                    'debit':
                    15.0,
                },
                {
                    **self.term_line_vals_1,
                    'currency_id':
                    self.currency_data['currency'].id,
                    'amount_currency':
                    -1410.0,
                    'credit':
                    705.0,
                },
            ], {
                **self.move_vals,
                'currency_id': self.currency_data['currency'].id,
            })

        move_form = Form(self.invoice)
        # Change the date to get another rate: 1/3 instead of 1/2.
        move_form.date = fields.Date.from_string('2016-01-01')
        move_form.save()

        self.assertInvoiceValues(
            self.invoice, [
                {
                    **self.product_line_vals_1,
                    'currency_id':
                    self.currency_data['currency'].id,
                    'amount_currency':
                    1000.0,
                    'debit':
                    333.33,
                },
                {
                    **self.product_line_vals_2,
                    'currency_id':
                    self.currency_data['currency'].id,
                    'amount_currency':
                    200.0,
                    'debit':
                    66.67,
                },
                {
                    **self.tax_line_vals_1,
                    'currency_id':
                    self.currency_data['currency'].id,
                    'amount_currency':
                    180.0,
                    'debit':
                    60.0,
                },
                {
                    **self.tax_line_vals_2,
                    'currency_id':
                    self.currency_data['currency'].id,
                    'amount_currency':
                    30.0,
                    'debit':
                    10.0,
                },
                {
                    **self.term_line_vals_1,
                    'currency_id':
                    self.currency_data['currency'].id,
                    'amount_currency':
                    -1410.0,
                    'credit':
                    470.0,
                },
            ], {
                **self.move_vals,
                'currency_id': self.currency_data['currency'].id,
                'date': fields.Date.from_string('2016-01-01'),
            })

        move_form = Form(self.invoice)
        with move_form.invoice_line_ids.edit(0) as line_form:
            # 0.045 * 0.1 = 0.0045. As the foreign currency has a 0.001 rounding,
            # the result should be 0.005 after rounding.
            line_form.quantity = 0.1
            line_form.price_unit = 0.045
        move_form.save()

        self.assertInvoiceValues(
            self.invoice, [
                {
                    **self.product_line_vals_1,
                    'quantity': 0.1,
                    'price_unit': 0.05,
                    'price_subtotal': 0.005,
                    'price_total': 0.006,
                    'currency_id': self.currency_data['currency'].id,
                    'amount_currency': 0.005,
                    'debit': 0.0,
                },
                {
                    **self.product_line_vals_2,
                    'currency_id':
                    self.currency_data['currency'].id,
                    'amount_currency':
                    200.0,
                    'debit':
                    66.67,
                },
                {
                    **self.tax_line_vals_1,
                    'price_unit': 30.0,
                    'price_subtotal': 30.001,
                    'price_total': 30.001,
                    'currency_id': self.currency_data['currency'].id,
                    'amount_currency': 30.001,
                    'debit': 10.0,
                },
                {
                    **self.tax_line_vals_2,
                    'currency_id':
                    self.currency_data['currency'].id,
                    'amount_currency':
                    30.0,
                    'debit':
                    10.0,
                },
                {
                    **self.term_line_vals_1,
                    'currency_id':
                    self.currency_data['currency'].id,
                    'price_unit':
                    -260.01,
                    'price_subtotal':
                    -260.006,
                    'price_total':
                    -260.006,
                    'amount_currency':
                    -260.006,
                    'credit':
                    86.67,
                },
            ], {
                **self.move_vals,
                'currency_id': self.currency_data['currency'].id,
                'date': fields.Date.from_string('2016-01-01'),
                'amount_untaxed': 200.005,
                'amount_tax': 60.001,
                'amount_total': 260.006,
            })

        # Exit the multi-currencies.
        move_form = Form(self.invoice)
        move_form.currency_id = self.company_data['currency']
        move_form.save()

        self.assertInvoiceValues(
            self.invoice, [
                {
                    **self.product_line_vals_1,
                    'quantity': 0.1,
                    'price_unit': 0.05,
                    'price_subtotal': 0.01,
                    'price_total': 0.01,
                    'amount_currency': 0.01,
                    'debit': 0.01,
                },
                self.product_line_vals_2,
                {
                    **self.tax_line_vals_1,
                    'price_unit': 30.0,
                    'price_subtotal': 30.0,
                    'price_total': 30.0,
                    'amount_currency': 30.0,
                    'debit': 30.0,
                },
                self.tax_line_vals_2,
                {
                    **self.term_line_vals_1,
                    'price_unit': -260.01,
                    'price_subtotal': -260.01,
                    'price_total': -260.01,
                    'amount_currency': -260.01,
                    'credit': 260.01,
                },
            ], {
                **self.move_vals,
                'currency_id': self.company_data['currency'].id,
                'date': fields.Date.from_string('2016-01-01'),
                'amount_untaxed': 200.01,
                'amount_tax': 60.0,
                'amount_total': 260.01,
            })
Exemplo n.º 37
0
 def test_default_picking_type(self):
     with Form(self.purchase_request_obj) as f:
         f.name = "Test Purchase"
         f.requested_by = self.env.user
     f.save()
Exemplo n.º 38
0
    def test_out_refund_line_onchange_cash_rounding_1(self):
        move_form = Form(self.invoice)
        # Add a cash rounding having 'add_invoice_line'.
        move_form.invoice_cash_rounding_id = self.cash_rounding_a
        move_form.save()

        # The cash rounding does nothing as the total is already rounded.
        self.assertInvoiceValues(self.invoice, [
            self.product_line_vals_1,
            self.product_line_vals_2,
            self.tax_line_vals_1,
            self.tax_line_vals_2,
            self.term_line_vals_1,
        ], self.move_vals)

        move_form = Form(self.invoice)
        with move_form.invoice_line_ids.edit(0) as line_form:
            line_form.price_unit = 999.99
        move_form.save()

        self.assertInvoiceValues(self.invoice, [
            {
                'name': 'add_invoice_line',
                'product_id': False,
                'account_id': self.cash_rounding_a.loss_account_id.id,
                'partner_id': self.partner_a.id,
                'product_uom_id': False,
                'quantity': 1.0,
                'discount': 0.0,
                'price_unit': 0.01,
                'price_subtotal': 0.01,
                'price_total': 0.01,
                'tax_ids': [],
                'tax_line_id': False,
                'currency_id': self.company_data['currency'].id,
                'amount_currency': 0.01,
                'debit': 0.01,
                'credit': 0.0,
                'date_maturity': False,
                'tax_exigible': True,
            },
            {
                **self.product_line_vals_1,
                'price_unit': 999.99,
                'price_subtotal': 999.99,
                'price_total': 1149.99,
                'amount_currency': 999.99,
                'debit': 999.99,
            },
            self.product_line_vals_2,
            self.tax_line_vals_1,
            self.tax_line_vals_2,
            self.term_line_vals_1,
        ], self.move_vals)

        move_form = Form(self.invoice)
        # Change the cash rounding to one having 'biggest_tax'.
        move_form.invoice_cash_rounding_id = self.cash_rounding_b
        move_form.save()

        self.assertInvoiceValues(
            self.invoice, [
                {
                    **self.product_line_vals_1,
                    'price_unit': 999.99,
                    'price_subtotal': 999.99,
                    'price_total': 1149.99,
                    'amount_currency': 999.99,
                    'debit': 999.99,
                },
                self.product_line_vals_2,
                self.tax_line_vals_1,
                self.tax_line_vals_2,
                {
                    'name': '%s (rounding)' % self.tax_sale_a.name,
                    'product_id': False,
                    'account_id':
                    self.company_data['default_account_tax_sale'].id,
                    'partner_id': self.partner_a.id,
                    'product_uom_id': False,
                    'quantity': 1.0,
                    'discount': 0.0,
                    'price_unit': -0.04,
                    'price_subtotal': -0.04,
                    'price_total': -0.04,
                    'tax_ids': [],
                    'tax_line_id': self.tax_sale_a.id,
                    'currency_id': self.company_data['currency'].id,
                    'amount_currency': -0.04,
                    'debit': 0.0,
                    'credit': 0.04,
                    'date_maturity': False,
                    'tax_exigible': True,
                },
                {
                    **self.term_line_vals_1,
                    'price_unit': -1409.95,
                    'price_subtotal': -1409.95,
                    'price_total': -1409.95,
                    'amount_currency': -1409.95,
                    'credit': 1409.95,
                },
            ], {
                **self.move_vals,
                'amount_untaxed': 1199.99,
                'amount_tax': 209.96,
                'amount_total': 1409.95,
            })
Exemplo n.º 39
0
    def _import_ubl(self, tree, invoice):
        """ Decodes an UBL invoice into an invoice.

        :param tree:    the UBL tree to decode.
        :param invoice: the invoice to update or an empty recordset.
        :returns:       the invoice where the UBL data was imported.
        """

        def _get_ubl_namespaces():
            ''' If the namespace is declared with xmlns='...', the namespaces map contains the 'None' key that causes an
            TypeError: empty namespace prefix is not supported in XPath
            Then, we need to remap arbitrarily this key.

            :param tree: An instance of etree.
            :return: The namespaces map without 'None' key.
            '''
            namespaces = tree.nsmap
            namespaces['inv'] = namespaces.pop(None)
            return namespaces

        namespaces = _get_ubl_namespaces()

        with Form(invoice.with_context(account_predictive_bills_disable_prediction=True)) as invoice_form:

            # Reference
            elements = tree.xpath('//cbc:ID', namespaces=namespaces)
            if elements:
                invoice_form.ref = elements[0].text
            elements = tree.xpath('//cbc:InstructionID', namespaces=namespaces)
            if elements:
                invoice_form.payment_reference = elements[0].text

            # Dates
            elements = tree.xpath('//cbc:IssueDate', namespaces=namespaces)
            if elements:
                invoice_form.invoice_date = elements[0].text
            elements = tree.xpath('//cbc:PaymentDueDate', namespaces=namespaces)
            if elements:
                invoice_form.invoice_date_due = elements[0].text
            # allow both cbc:PaymentDueDate and cbc:DueDate
            elements = tree.xpath('//cbc:DueDate', namespaces=namespaces)
            invoice_form.invoice_date_due = invoice_form.invoice_date_due or elements and elements[0].text

            # Currency
            elements = tree.xpath('//cbc:DocumentCurrencyCode', namespaces=namespaces)
            currency_code = elements and elements[0].text or ''
            currency = self.env['res.currency'].search([('name', '=', currency_code.upper())], limit=1)
            if elements:
                invoice_form.currency_id = currency

            # Incoterm
            elements = tree.xpath('//cbc:TransportExecutionTerms/cac:DeliveryTerms/cbc:ID', namespaces=namespaces)
            if elements:
                invoice_form.invoice_incoterm_id = self.env['account.incoterms'].search([('code', '=', elements[0].text)], limit=1)

            # Partner
            partner_element = tree.xpath('//cac:AccountingSupplierParty/cac:Party', namespaces=namespaces)
            if partner_element:
                domains = []
                partner_element = partner_element[0]
                elements = partner_element.xpath('//cac:AccountingSupplierParty/cac:Party//cbc:Name', namespaces=namespaces)
                if elements:
                    partner_name = elements[0].text
                    domains.append([('name', 'ilike', partner_name)])
                else:
                    partner_name = ''
                elements = partner_element.xpath('//cac:AccountingSupplierParty/cac:Party//cbc:Telephone', namespaces=namespaces)
                if elements:
                    partner_telephone = elements[0].text
                    domains.append([('phone', '=', partner_telephone), ('mobile', '=', partner_telephone)])
                elements = partner_element.xpath('//cac:AccountingSupplierParty/cac:Party//cbc:ElectronicMail', namespaces=namespaces)
                if elements:
                    partner_mail = elements[0].text
                    domains.append([('email', '=', partner_mail)])
                elements = partner_element.xpath('//cac:AccountingSupplierParty/cac:Party//cbc:CompanyID', namespaces=namespaces)
                if elements:
                    partner_id = elements[0].text
                    domains.append([('vat', 'like', partner_id)])

                if domains:
                    partner = self.env['res.partner'].search(expression.OR(domains), limit=1)
                    if partner:
                        invoice_form.partner_id = partner
                        partner_name = partner.name
                    else:
                        invoice_form.partner_id = self.env['res.partner']

            # Regenerate PDF
            attachments = self.env['ir.attachment']
            elements = tree.xpath('//cac:AdditionalDocumentReference', namespaces=namespaces)
            for element in elements:
                attachment_name = element.xpath('cbc:ID', namespaces=namespaces)
                attachment_data = element.xpath('cac:Attachment//cbc:EmbeddedDocumentBinaryObject', namespaces=namespaces)
                if attachment_name and attachment_data:
                    text = attachment_data[0].text
                    # Normalize the name of the file : some e-fff emitters put the full path of the file
                    # (Windows or Linux style) and/or the name of the xml instead of the pdf.
                    # Get only the filename with a pdf extension.
                    name = PureWindowsPath(attachment_name[0].text).stem + '.pdf'
                    attachments |= self.env['ir.attachment'].create({
                        'name': name,
                        'res_id': invoice.id,
                        'res_model': 'account.move',
                        'datas': text + '=' * (len(text) % 3),  # Fix incorrect padding
                        'type': 'binary',
                        'mimetype': 'application/pdf',
                    })
            if attachments:
                invoice.with_context(no_new_invoice=True).message_post(attachment_ids=attachments.ids)

            # Lines
            lines_elements = tree.xpath('//cac:InvoiceLine', namespaces=namespaces)
            for eline in lines_elements:
                with invoice_form.invoice_line_ids.new() as invoice_line_form:
                    # Product
                    elements = eline.xpath('cac:Item/cac:SellersItemIdentification/cbc:ID', namespaces=namespaces)
                    domains = []
                    if elements:
                        product_code = elements[0].text
                        domains.append([('default_code', '=', product_code)])
                    elements = eline.xpath('cac:Item/cac:StandardItemIdentification/cbc:ID[@schemeID=\'GTIN\']', namespaces=namespaces)
                    if elements:
                        product_ean13 = elements[0].text
                        domains.append([('barcode', '=', product_ean13)])
                    if domains:
                        product = self.env['product.product'].search(expression.OR(domains), limit=1)
                        if product:
                            invoice_line_form.product_id = product

                    # Quantity
                    elements = eline.xpath('cbc:InvoicedQuantity', namespaces=namespaces)
                    quantity = elements and float(elements[0].text) or 1.0
                    invoice_line_form.quantity = quantity

                    # Price Unit
                    elements = eline.xpath('cac:Price/cbc:PriceAmount', namespaces=namespaces)
                    price_unit = elements and float(elements[0].text) or 0.0
                    elements = eline.xpath('cbc:LineExtensionAmount', namespaces=namespaces)
                    line_extension_amount = elements and float(elements[0].text) or 0.0
                    invoice_line_form.price_unit = price_unit or line_extension_amount / invoice_line_form.quantity or 0.0

                    # Name
                    elements = eline.xpath('cac:Item/cbc:Description', namespaces=namespaces)
                    if elements and elements[0].text:
                        invoice_line_form.name = elements[0].text
                        invoice_line_form.name = invoice_line_form.name.replace('%month%', str(fields.Date.to_date(invoice_form.invoice_date).month))  # TODO: full name in locale
                        invoice_line_form.name = invoice_line_form.name.replace('%year%', str(fields.Date.to_date(invoice_form.invoice_date).year))
                    else:
                        invoice_line_form.name = "%s (%s)" % (partner_name or '', invoice_form.invoice_date)

                    # Taxes
                    taxes_elements = eline.xpath('cac:TaxTotal/cac:TaxSubtotal', namespaces=namespaces)
                    invoice_line_form.tax_ids.clear()
                    for etax in taxes_elements:
                        elements = etax.xpath('cbc:Percent', namespaces=namespaces)
                        if elements:
                            tax = self.env['account.tax'].search([
                                ('company_id', '=', self.env.company.id),
                                ('amount', '=', float(elements[0].text)),
                                ('type_tax_use', '=', invoice_form.journal_id.type),
                            ], order='sequence ASC', limit=1)
                            if tax:
                                invoice_line_form.tax_ids.add(tax)

        return invoice_form.save()
Exemplo n.º 40
0
    def test_out_refund_line_onchange_partner_1(self):
        move_form = Form(self.invoice)
        move_form.partner_id = self.partner_b
        move_form.payment_reference = 'turlututu'
        move_form.save()

        self.assertInvoiceValues(
            self.invoice, [
                {
                    **self.product_line_vals_1,
                    'partner_id': self.partner_b.id,
                },
                {
                    **self.product_line_vals_2,
                    'partner_id': self.partner_b.id,
                },
                {
                    **self.tax_line_vals_1,
                    'partner_id': self.partner_b.id,
                },
                {
                    **self.tax_line_vals_2,
                    'partner_id': self.partner_b.id,
                },
                {
                    **self.term_line_vals_1,
                    'name': 'turlututu',
                    'partner_id': self.partner_b.id,
                    'account_id':
                    self.partner_b.property_account_receivable_id.id,
                    'price_unit': -987.0,
                    'price_subtotal': -987.0,
                    'price_total': -987.0,
                    'amount_currency': -987.0,
                    'credit': 987.0,
                    'date_maturity': fields.Date.from_string('2019-02-28'),
                },
                {
                    **self.term_line_vals_1,
                    'name': 'turlututu',
                    'partner_id': self.partner_b.id,
                    'account_id':
                    self.partner_b.property_account_receivable_id.id,
                    'price_unit': -423.0,
                    'price_subtotal': -423.0,
                    'price_total': -423.0,
                    'amount_currency': -423.0,
                    'credit': 423.0,
                },
            ], {
                **self.move_vals,
                'partner_id': self.partner_b.id,
                'payment_reference': 'turlututu',
                'fiscal_position_id': self.fiscal_pos_a.id,
                'invoice_payment_term_id': self.pay_terms_b.id,
                'amount_untaxed': 1200.0,
                'amount_tax': 210.0,
                'amount_total': 1410.0,
            })

        # Remove lines and recreate them to apply the fiscal position.
        move_form = Form(self.invoice)
        move_form.invoice_line_ids.remove(0)
        move_form.invoice_line_ids.remove(0)
        with move_form.invoice_line_ids.new() as line_form:
            line_form.product_id = self.product_a
        with move_form.invoice_line_ids.new() as line_form:
            line_form.product_id = self.product_b
        move_form.save()

        self.assertInvoiceValues(
            self.invoice, [
                {
                    **self.product_line_vals_1,
                    'account_id':
                    self.product_b.property_account_income_id.id,
                    'partner_id':
                    self.partner_b.id,
                    'tax_ids':
                    self.tax_sale_b.ids,
                },
                {
                    **self.product_line_vals_2,
                    'partner_id': self.partner_b.id,
                    'price_total': 230.0,
                    'tax_ids': self.tax_sale_b.ids,
                },
                {
                    **self.tax_line_vals_1,
                    'name': self.tax_sale_b.name,
                    'partner_id': self.partner_b.id,
                    'tax_line_id': self.tax_sale_b.id,
                },
                {
                    **self.term_line_vals_1,
                    'name': 'turlututu',
                    'account_id':
                    self.partner_b.property_account_receivable_id.id,
                    'partner_id': self.partner_b.id,
                    'price_unit': -966.0,
                    'price_subtotal': -966.0,
                    'price_total': -966.0,
                    'amount_currency': -966.0,
                    'credit': 966.0,
                    'date_maturity': fields.Date.from_string('2019-02-28'),
                },
                {
                    **self.term_line_vals_1,
                    'name': 'turlututu',
                    'account_id':
                    self.partner_b.property_account_receivable_id.id,
                    'partner_id': self.partner_b.id,
                    'price_unit': -414.0,
                    'price_subtotal': -414.0,
                    'price_total': -414.0,
                    'amount_currency': -414.0,
                    'credit': 414.0,
                },
            ], {
                **self.move_vals,
                'partner_id': self.partner_b.id,
                'payment_reference': 'turlututu',
                'fiscal_position_id': self.fiscal_pos_a.id,
                'invoice_payment_term_id': self.pay_terms_b.id,
                'amount_untaxed': 1200.0,
                'amount_tax': 180.0,
                'amount_total': 1380.0,
            })
Exemplo n.º 41
0
    def _import_facturx_invoice(self, tree):
        ''' Extract invoice values from the Factur-x xml tree passed as parameter.

        :param tree: The tree of the Factur-x xml file.
        :return: A dictionary containing account.invoice values to create/update it.
        '''
        amount_total_import = None

        # type must be present in the context to get the right behavior of the _default_journal method (account.invoice).
        # journal_id must be present in the context to get the right behavior of the _default_account method (account.invoice.line).
        self_ctx = self.with_context(type='in_invoice')
        journal_id = self_ctx._default_journal().id
        self_ctx = self_ctx.with_context(journal_id=journal_id)

        # self could be a single record (editing) or be empty (new).
        with Form(self_ctx,
                  view='account.invoice_supplier_form') as invoice_form:

            # Partner (first step to avoid warning 'Warning! You must first select a partner.').
            elements = tree.xpath(
                '//ram:SellerTradeParty/ram:SpecifiedTaxRegistration/ram:ID',
                namespaces=tree.nsmap)
            partner = elements and self.env['res.partner'].search(
                [('vat', '=', elements[0].text)], limit=1)
            if not partner:
                elements = tree.xpath('//ram:SellerTradeParty/ram:Name',
                                      namespaces=tree.nsmap)
                partner_name = elements and elements[0].text
                partner = elements and self.env['res.partner'].search(
                    [('name', 'ilike', partner_name)], limit=1)
            if not partner:
                elements = tree.xpath(
                    '//ram:SellerTradeParty//ram:URIID[@schemeID=\'SMTP\']',
                    namespaces=tree.nsmap)
                partner = elements and self.env['res.partner'].search(
                    [('email', '=', elements[0].text)], limit=1)
            if partner:
                invoice_form.partner_id = partner

            # Reference.
            elements = tree.xpath('//rsm:ExchangedDocument/ram:ID',
                                  namespaces=tree.nsmap)
            if elements:
                invoice_form.reference = elements[0].text

            # Name.
            elements = tree.xpath(
                '//ram:BuyerOrderReferencedDocument/ram:IssuerAssignedID',
                namespaces=tree.nsmap)
            if elements:
                invoice_form.name = elements[0].text

            # Comment.
            elements = tree.xpath('//ram:IncludedNote/ram:Content',
                                  namespaces=tree.nsmap)
            if elements:
                invoice_form.comment = elements[0].text

            # Refund type.
            # There is two modes to handle refund in Factur-X:
            # a) type_code == 380 for invoice, type_code == 381 for refund, all positive amounts.
            # b) type_code == 380, negative amounts in case of refund.
            # To handle both, we consider the 'a' mode and switch to 'b' if a negative amount is encountered.
            elements = tree.xpath('//rsm:ExchangedDocument/ram:TypeCode',
                                  namespaces=tree.nsmap)
            type_code = elements[0].text
            refund_sign = 1

            # Total amount.
            elements = tree.xpath('//ram:GrandTotalAmount',
                                  namespaces=tree.nsmap)
            if elements:
                total_amount = float(elements[0].text)

                # Handle 'a & b' refund mode.
                if (total_amount < 0
                        and type_code == '380') or type_code == '381':
                    refund_sign = -1

                # Currency.
                if elements[0].attrib.get('currencyID'):
                    currency_str = elements[0].attrib['currencyID']
                    currency = self.env.ref('base.%s' % currency_str.upper(),
                                            raise_if_not_found=False)
                    if currency != self.env.user.company_id.currency_id and currency.active:
                        invoice_form.currency_id = currency

                    # Store xml total amount.
                    amount_total_import = total_amount * refund_sign

            # Date.
            elements = tree.xpath(
                '//rsm:ExchangedDocument/ram:IssueDateTime/udt:DateTimeString',
                namespaces=tree.nsmap)
            if elements:
                date_str = elements[0].text
                date_obj = datetime.strptime(date_str,
                                             DEFAULT_FACTURX_DATE_FORMAT)
                invoice_form.date_invoice = date_obj.strftime(
                    DEFAULT_SERVER_DATE_FORMAT)

            # Due date.
            elements = tree.xpath(
                '//ram:SpecifiedTradePaymentTerms/ram:DueDateDateTime/udt:DateTimeString',
                namespaces=tree.nsmap)
            if elements:
                date_str = elements[0].text
                date_obj = datetime.strptime(date_str,
                                             DEFAULT_FACTURX_DATE_FORMAT)
                date_due = date_obj.strftime(DEFAULT_SERVER_DATE_FORMAT)
                if date_due:
                    invoice_form.payment_term_id = self.env[
                        'account.payment.term']
                    invoice_form.date_due = date_due

            # Invoice lines.
            elements = tree.xpath('//ram:IncludedSupplyChainTradeLineItem',
                                  namespaces=tree.nsmap)
            if elements:
                for element in elements:
                    with invoice_form.invoice_line_ids.new(
                    ) as invoice_line_form:

                        # Sequence.
                        line_elements = element.xpath(
                            './/ram:AssociatedDocumentLineDocument/ram:LineID',
                            namespaces=tree.nsmap)
                        if line_elements:
                            invoice_line_form.sequence = int(
                                line_elements[0].text)

                        # Product.
                        line_elements = element.xpath(
                            './/ram:SpecifiedTradeProduct/ram:Name',
                            namespaces=tree.nsmap)
                        if line_elements:
                            invoice_line_form.name = line_elements[0].text
                        line_elements = element.xpath(
                            './/ram:SpecifiedTradeProduct/ram:SellerAssignedID',
                            namespaces=tree.nsmap)
                        if line_elements and line_elements[0].text:
                            product = self.env['product.product'].search([
                                ('default_code', '=', line_elements[0].text)
                            ])
                            if product:
                                invoice_line_form.product_id = product
                        if not invoice_line_form.product_id:
                            line_elements = element.xpath(
                                './/ram:SpecifiedTradeProduct/ram:GlobalID',
                                namespaces=tree.nsmap)
                            if line_elements and line_elements[0].text:
                                product = self.env['product.product'].search([
                                    ('barcode', '=', line_elements[0].text)
                                ])
                                if product:
                                    invoice_line_form.product_id = product

                        # Quantity.
                        line_elements = element.xpath(
                            './/ram:SpecifiedLineTradeDelivery/ram:BilledQuantity',
                            namespaces=tree.nsmap)
                        if line_elements:
                            invoice_line_form.quantity = float(
                                line_elements[0].text) * refund_sign

                        # Price Unit.
                        line_elements = element.xpath(
                            './/ram:GrossPriceProductTradePrice/ram:ChargeAmount',
                            namespaces=tree.nsmap)
                        if line_elements:
                            invoice_line_form.price_unit = float(
                                line_elements[0].text
                            ) / invoice_line_form.quantity
                        else:
                            line_elements = element.xpath(
                                './/ram:NetPriceProductTradePrice/ram:ChargeAmount',
                                namespaces=tree.nsmap)
                            if line_elements:
                                invoice_line_form.price_unit = float(
                                    line_elements[0].text
                                ) / invoice_line_form.quantity

                        # Discount.
                        line_elements = element.xpath(
                            './/ram:AppliedTradeAllowanceCharge/ram:CalculationPercent',
                            namespaces=tree.nsmap)
                        if line_elements:
                            invoice_line_form.discount = float(
                                line_elements[0].text)

                        # Taxes
                        line_elements = element.xpath(
                            './/ram:SpecifiedLineTradeSettlement/ram:ApplicableTradeTax/ram:RateApplicablePercent',
                            namespaces=tree.nsmap)
                        invoice_line_form.invoice_line_tax_ids.clear()
                        for tax_element in line_elements:
                            percentage = float(tax_element.text)

                            tax = self.env['account.tax'].search([
                                ('company_id', '=',
                                 invoice_form.company_id.id),
                                ('amount_type', '=', 'percent'),
                                ('type_tax_use', '=', 'purchase'),
                                ('amount', '=', percentage),
                            ],
                                                                 limit=1)

                            if tax:
                                invoice_line_form.invoice_line_tax_ids.add(tax)
            elif amount_total_import:
                # No lines in BASICWL.
                with invoice_form.invoice_line_ids.new() as invoice_line_form:
                    invoice_line_form.name = invoice_form.comment or '/'
                    invoice_line_form.quantity = 1
                    invoice_line_form.price_unit = amount_total_import

            # Refund.
            invoice_form.type = 'in_refund' if refund_sign == -1 else 'in_invoice'

        return invoice_form.save()
Exemplo n.º 42
0
    def test_in_refund_line_onchange_currency_1(self):
        move_form = Form(self.invoice)
        move_form.currency_id = self.currency_data['currency']
        move_form.save()

        self.assertInvoiceValues(self.invoice, [
            {
                **self.product_line_vals_1,
                'currency_id': self.currency_data['currency'].id,
                'amount_currency': -800.0,
                'credit': 400.0,
            },
            {
                **self.product_line_vals_2,
                'currency_id': self.currency_data['currency'].id,
                'amount_currency': -160.0,
                'credit': 80.0,
            },
            {
                **self.tax_line_vals_1,
                'currency_id': self.currency_data['currency'].id,
                'amount_currency': -144.0,
                'credit': 72.0,
            },
            {
                **self.tax_line_vals_2,
                'currency_id': self.currency_data['currency'].id,
                'amount_currency': -24.0,
                'credit': 12.0,
            },
            {
                **self.term_line_vals_1,
                'currency_id': self.currency_data['currency'].id,
                'amount_currency': 1128.0,
                'debit': 564.0,
            },
        ], {
            **self.move_vals,
            'currency_id': self.currency_data['currency'].id,
        })

        move_form = Form(self.invoice)
        # Change the date to get another rate: 1/3 instead of 1/2.
        move_form.date = fields.Date.from_string('2016-01-01')
        move_form.save()

        self.assertInvoiceValues(self.invoice, [
            {
                **self.product_line_vals_1,
                'currency_id': self.currency_data['currency'].id,
                'amount_currency': -800.0,
                'credit': 266.67,
            },
            {
                **self.product_line_vals_2,
                'currency_id': self.currency_data['currency'].id,
                'amount_currency': -160.0,
                'credit': 53.33,
            },
            {
                **self.tax_line_vals_1,
                'currency_id': self.currency_data['currency'].id,
                'amount_currency': -144.0,
                'credit': 48.0,
            },
            {
                **self.tax_line_vals_2,
                'currency_id': self.currency_data['currency'].id,
                'amount_currency': -24.0,
                'credit': 8.0,
            },
            {
                **self.term_line_vals_1,
                'currency_id': self.currency_data['currency'].id,
                'amount_currency': 1128.0,
                'debit': 376.0,
            },
        ], {
            **self.move_vals,
            'currency_id': self.currency_data['currency'].id,
            'date': fields.Date.from_string('2016-01-01'),
        })

        move_form = Form(self.invoice)
        with move_form.invoice_line_ids.edit(0) as line_form:
            # 0.045 * 0.1 = 0.0045. As the foreign currency has a 0.001 rounding,
            # the result should be 0.005 after rounding.
            line_form.quantity = 0.1
            line_form.price_unit = 0.045
        move_form.save()

        self.assertInvoiceValues(self.invoice, [
            {
                **self.product_line_vals_1,
                'quantity': 0.1,
                'price_unit': 0.05,
                'price_subtotal': 0.005,
                'price_total': 0.006,
                'currency_id': self.currency_data['currency'].id,
                'amount_currency': -0.005,
                'credit': 0.0,
            },
            {
                **self.product_line_vals_2,
                'currency_id': self.currency_data['currency'].id,
                'amount_currency': -160.0,
                'credit': 53.33,
            },
            {
                **self.tax_line_vals_1,
                'price_unit': 24.0,
                'price_subtotal': 24.001,
                'price_total': 24.001,
                'currency_id': self.currency_data['currency'].id,
                'amount_currency': -24.001,
                'credit': 8.0,
            },
            {
                **self.tax_line_vals_2,
                'currency_id': self.currency_data['currency'].id,
                'amount_currency': -24.0,
                'credit': 8.0,
            },
            {
                **self.term_line_vals_1,
                'currency_id': self.currency_data['currency'].id,
                'price_unit': -208.01,
                'price_subtotal': -208.006,
                'price_total': -208.006,
                'amount_currency': 208.006,
                'debit': 69.33,
            },
        ], {
            **self.move_vals,
            'currency_id': self.currency_data['currency'].id,
            'date': fields.Date.from_string('2016-01-01'),
            'amount_untaxed': 160.005,
            'amount_tax': 48.001,
            'amount_total': 208.006,
        })
Exemplo n.º 43
0
    def test_subkit_in_delivery_slip(self):
        """
        Suppose this structure:
        Super Kit --|- Compo 01 x1
                    |- Sub Kit x1 --|- Compo 02 x1
                    |               |- Compo 03 x1

        This test ensures that, when delivering one Super Kit, one Sub Kit, one Compo 01 and one Compo 02,
        and when putting in pack the third component of the Super Kit, the delivery report is correct.
        """
        compo01, compo02, compo03, subkit, superkit = self.env[
            'product.product'].create(
                [{
                    'name': n,
                    'type': 'consu',
                } for n in
                 ['Compo 01', 'Compo 02', 'Compo 03', 'Sub Kit', 'Super Kit']])

        self.env['mrp.bom'].create([{
            'product_tmpl_id':
            subkit.product_tmpl_id.id,
            'product_qty':
            1,
            'type':
            'phantom',
            'bom_line_ids': [
                (0, 0, {
                    'product_id': compo02.id,
                    'product_qty': 1
                }),
                (0, 0, {
                    'product_id': compo03.id,
                    'product_qty': 1
                }),
            ],
        }, {
            'product_tmpl_id':
            superkit.product_tmpl_id.id,
            'product_qty':
            1,
            'type':
            'phantom',
            'bom_line_ids': [
                (0, 0, {
                    'product_id': compo01.id,
                    'product_qty': 1
                }),
                (0, 0, {
                    'product_id': subkit.id,
                    'product_qty': 1
                }),
            ],
        }])

        picking_form = Form(self.env['stock.picking'])
        picking_form.picking_type_id = self.picking_type_out
        picking_form.partner_id = self.partner
        with picking_form.move_ids_without_package.new() as move:
            move.product_id = superkit
            move.product_uom_qty = 1
        with picking_form.move_ids_without_package.new() as move:
            move.product_id = subkit
            move.product_uom_qty = 1
        with picking_form.move_ids_without_package.new() as move:
            move.product_id = compo01
            move.product_uom_qty = 1
        with picking_form.move_ids_without_package.new() as move:
            move.product_id = compo02
            move.product_uom_qty = 1
        picking = picking_form.save()
        picking.action_confirm()

        picking.move_lines.quantity_done = 1
        move = picking.move_lines.filtered(
            lambda m: m.name == "Super Kit" and m.product_id == compo03)
        move.move_line_ids.result_package_id = self.env[
            'stock.quant.package'].create({'name': 'Package0001'})
        picking.button_validate()

        report = self.env['ir.actions.report']._get_report_from_name(
            'stock.report_deliveryslip')
        html_report = report._render_qweb_html(
            picking.ids)[0].decode('utf-8').split('\n')
        keys = [
            "Package0001",
            "Compo 03",
            "Products with no package assigned",
            "Compo 01",
            "Compo 02",
            "Super Kit",
            "Compo 01",
            "Compo 02",
            "Sub Kit",
            "Compo 02",
            "Compo 03",
        ]
        for line in html_report:
            if not keys:
                break
            if keys[0] in line:
                keys = keys[1:]
        self.assertFalse(
            keys, "All keys should be in the report with the defined order")
Exemplo n.º 44
0
    def test_in_refund_line_onchange_partner_1(self):
        move_form = Form(self.invoice)
        move_form.partner_id = self.partner_b
        move_form.payment_reference = 'turlututu'
        move_form.save()

        self.assertInvoiceValues(self.invoice, [
            {
                **self.product_line_vals_1,
                'partner_id': self.partner_b.id,
            },
            {
                **self.product_line_vals_2,
                'partner_id': self.partner_b.id,
            },
            {
                **self.tax_line_vals_1,
                'partner_id': self.partner_b.id,
            },
            {
                **self.tax_line_vals_2,
                'partner_id': self.partner_b.id,
            },
            {
                **self.term_line_vals_1,
                'name': 'turlututu',
                'partner_id': self.partner_b.id,
                'account_id': self.partner_b.property_account_payable_id.id,
                'price_unit': -338.4,
                'price_subtotal': -338.4,
                'price_total': -338.4,
                'debit': 338.4,
            },
            {
                **self.term_line_vals_1,
                'name': 'turlututu',
                'partner_id': self.partner_b.id,
                'account_id': self.partner_b.property_account_payable_id.id,
                'price_unit': -789.6,
                'price_subtotal': -789.6,
                'price_total': -789.6,
                'debit': 789.6,
                'date_maturity': fields.Date.from_string('2019-02-28'),
            },
        ], {
            **self.move_vals,
            'partner_id': self.partner_b.id,
            'payment_reference': 'turlututu',
            'fiscal_position_id': self.fiscal_pos_a.id,
            'invoice_payment_term_id': self.pay_terms_b.id,
            'amount_untaxed': 960.0,
            'amount_tax': 168.0,
            'amount_total': 1128.0,
        })

        # Remove lines and recreate them to apply the fiscal position.
        move_form = Form(self.invoice)
        move_form.invoice_line_ids.remove(0)
        move_form.invoice_line_ids.remove(0)
        with move_form.invoice_line_ids.new() as line_form:
            line_form.product_id = self.product_a
        with move_form.invoice_line_ids.new() as line_form:
            line_form.product_id = self.product_b
        move_form.save()

        self.assertInvoiceValues(self.invoice, [
            {
                **self.product_line_vals_1,
                'account_id': self.product_b.property_account_expense_id.id,
                'partner_id': self.partner_b.id,
                'tax_ids': self.tax_purchase_b.ids,
            },
            {
                **self.product_line_vals_2,
                'partner_id': self.partner_b.id,
                'price_total': 184.0,
                'tax_ids': self.tax_purchase_b.ids,
            },
            {
                **self.tax_line_vals_1,
                'name': self.tax_purchase_b.name,
                'partner_id': self.partner_b.id,
                'tax_line_id': self.tax_purchase_b.id,
            },
            {
                **self.term_line_vals_1,
                'name': 'turlututu',
                'account_id': self.partner_b.property_account_payable_id.id,
                'partner_id': self.partner_b.id,
                'price_unit': -331.2,
                'price_subtotal': -331.2,
                'price_total': -331.2,
                'debit': 331.2,
            },
            {
                **self.term_line_vals_1,
                'name': 'turlututu',
                'account_id': self.partner_b.property_account_payable_id.id,
                'partner_id': self.partner_b.id,
                'price_unit': -772.8,
                'price_subtotal': -772.8,
                'price_total': -772.8,
                'debit': 772.8,
                'date_maturity': fields.Date.from_string('2019-02-28'),
            },
        ], {
            **self.move_vals,
            'partner_id': self.partner_b.id,
            'payment_reference': 'turlututu',
            'fiscal_position_id': self.fiscal_pos_a.id,
            'invoice_payment_term_id': self.pay_terms_b.id,
            'amount_untaxed': 960.0,
            'amount_tax': 144.0,
            'amount_total': 1104.0,
        })
Exemplo n.º 45
0
    def test_sale_mrp(self):
        warehouse0 = self.env.ref('stock.warehouse0')
        # In order to test the sale_mrp module in OpenERP, I start by creating a new product 'Slider Mobile'
        # I define product category Mobile Products Sellable.

        with mute_logger('odoo.tests.common.onchange'):
            # Suppress warning on "Changing your cost method" when creating a
            # product category
            pc = Form(self.env['product.category'])
        pc.name = 'Mobile Products Sellable'
        product_category_allproductssellable0 = pc.save()

        uom_unit = self.env.ref('uom.product_uom_unit')

        self.assertIn("seller_ids", self.env['product.template'].fields_get())

        # I define product for Slider Mobile.
        product = Form(self.env['product.template'])

        product.categ_id = product_category_allproductssellable0
        product.list_price = 200.0
        product.name = 'Slider Mobile'
        product.standard_price = 189.0
        product.type = 'product'
        product.uom_id = uom_unit
        product.uom_po_id = uom_unit
        product.route_ids.clear()
        product.route_ids.add(warehouse0.manufacture_pull_id.route_id)
        product.route_ids.add(warehouse0.mto_pull_id.route_id)
        product_template_slidermobile0 = product.save()

        with Form(self.env['mrp.bom']) as bom:
            bom.product_tmpl_id = product_template_slidermobile0

        # I create a sale order for product Slider mobile
        so_form = Form(self.env['sale.order'])
        so_form.partner_id = self.env.ref('base.res_partner_4')
        with so_form.order_line.new() as line:
            line.product_id = product_template_slidermobile0.product_variant_ids
            line.price_unit = 200
            line.product_uom_qty = 500.0
            line.customer_lead = 7.0
        sale_order_so0 = so_form.save()

        # I confirm the sale order
        sale_order_so0.action_confirm()

        # I verify that a manufacturing order has been generated, and that its name and reference are correct
        mo = self.env['mrp.production'].search(
            [('origin', 'like', sale_order_so0.name)], limit=1)
        self.assertTrue(mo, 'Manufacturing order has not been generated')
    def test_onchange_taxes_3(self):
        '''
        Test the amount of tax account.move.line is still editable manually.
        Test the amount of tax account.move.line is cumulative for the same tax.
        This test uses the following scenario:
            - Create manually a debit line of 1000 having a tax.
            - Assume a line containing the tax amount is created automatically.
            - Edit the tax line amount of the auto-generated line by adding 5.
            - Create manually a credit line to balance the two previous lines.
            - Save the move.
            - Edit the move.
            - Create manually a debit line of 2000 having the same tax.
            - Assume the line containing the tax amount has been updated (no new line created).
            - Create manually a credit line to balance the four previous lines.
            - Save the move.

        tax = 10%

        Name            | Debit     | Credit    | Tax_ids       | Tax_line_id's name
        ----------------|-----------|-----------|---------------|-------------------
        debit_line_1    | 1000      |           | tax           |
        tax_line        | 300       |           |               | tax_line
        credit_line_1   |           | 1105      |               |
        debit_line_2    | 2000      |           | tax           |
        credit_line_2   |           | 2195      |               |
        '''
        move_form = Form(self.env['account.move'], view='account.view_move_form')
        move_form.ref = 'azerty'
        move_form.journal_id = self.journal

        # Create a new account.move.line with debit amount.
        with move_form.line_ids.new() as debit_line:
            debit_line.name = 'debit_line_1'
            debit_line.account_id = self.account
            debit_line.debit = 1000
            debit_line.tax_ids.clear()
            debit_line.tax_ids.add(self.percent_tax)

            self.assertTrue(debit_line.recompute_tax_line)

        # Edit the tax account.move.line
        with move_form.line_ids.edit(index=1) as tax_line:
            tax_line.debit = 105  # Was 100

        # Create a third account.move.line with credit amount.
        with move_form.line_ids.new() as credit_line:
            credit_line.name = 'credit_line_1'
            credit_line.account_id = self.account
            credit_line.credit = 1105

        move = move_form.save()

        move_form = Form(move, view='account.view_move_form')
        # Create a new account.move.line with debit amount.
        with move_form.line_ids.new() as debit_line2:
            debit_line2.name = 'debit_line_2'
            debit_line2.account_id = self.account
            debit_line2.debit = 2000
            debit_line2.tax_ids.clear()
            debit_line2.tax_ids.add(self.percent_tax)

            self.assertTrue(debit_line2.recompute_tax_line)

        with move_form.line_ids.new() as credit_line2:
            credit_line2.name = 'credit_line_2'
            credit_line2.account_id = self.account
            credit_line2.credit = 2195

        move = move_form.save()

        self.check_complete_move(move, [
            ['debit_line_1',    1000,   0,      [self.percent_tax.id],  None],
            ['tax_line',        300,    0,      None,                   self.percent_tax.id],
            ['credit_line_1',   0,      1105,   None,                   None],
            ['debit_line_2',    2000,   0,      [self.percent_tax.id],  None],
            ['credit_line_2',   0,      2195,   None,                   None],
        ], fields_name=['name', 'debit', 'credit', 'tax_ids', 'tax_line_id'])
Exemplo n.º 47
0
    def test_recurrence_cron_repeat_after(self):
        domain = [('project_id', '=', self.project_recurring.id)]
        with freeze_time("2020-01-01"):
            form = Form(self.env['project.task'])
            form.name = 'test recurring task'
            form.description = 'my super recurring task bla bla bla'
            form.project_id = self.project_recurring
            form.date_deadline = datetime(2020, 2, 1)

            form.recurring_task = True
            form.repeat_interval = 1
            form.repeat_unit = 'month'
            form.repeat_type = 'after'
            form.repeat_number = 2
            form.repeat_on_month = 'date'
            form.repeat_day = '15'
            task = form.save()
            task.planned_hours = 2

            self.assertEqual(task.recurrence_id.next_recurrence_date,
                             date(2020, 1, 15))
            self.assertEqual(self.env['project.task'].search_count(domain), 1)
            self.env['project.task.recurrence']._cron_create_recurring_tasks()
            self.assertEqual(self.env['project.task'].search_count(domain), 1,
                             'no extra task should be created')
            self.assertEqual(task.recurrence_id.recurrence_left, 2)

        with freeze_time("2020-01-15"):
            self.assertEqual(self.env['project.task'].search_count(domain), 1)
            self.env['project.task.recurrence']._cron_create_recurring_tasks()
            self.assertEqual(self.env['project.task'].search_count(domain), 2)
            self.assertEqual(task.recurrence_id.recurrence_left, 1)

        with freeze_time("2020-02-15"):
            self.env['project.task.recurrence']._cron_create_recurring_tasks()
            self.assertEqual(self.env['project.task'].search_count(domain), 3)
            self.assertEqual(task.recurrence_id.recurrence_left, 0)
            self.env['project.task.recurrence']._cron_create_recurring_tasks()
            self.assertEqual(self.env['project.task'].search_count(domain), 3)
            self.assertEqual(task.recurrence_id.recurrence_left, 0)

        tasks = self.env['project.task'].search(domain)
        self.assertEqual(len(tasks), 3)

        self.assertTrue(bool(tasks[2].date_deadline))
        self.assertFalse(tasks[1].date_deadline,
                         "Deadline should not be copied")

        for f in self.env['project.task.recurrence']._get_recurring_fields():
            self.assertTrue(tasks[0][f] == tasks[1][f] == tasks[2][f],
                            "Field %s should have been copied" % f)
Exemplo n.º 48
0
    def _import_facturx(self, tree, invoice):
        """ Decodes a factur-x invoice into an invoice.

        :param tree:    the factur-x tree to decode.
        :param invoice: the invoice to update or an empty recordset.
        :returns:       the invoice where the factur-x data was imported.
        """

        def _find_value(xpath, element=tree):
            return self._find_value(xpath, element, tree.nsmap)

        amount_total_import = None

        default_move_type = False
        if invoice._context.get('default_journal_id'):
            journal = self.env['account.journal'].browse(self.env.context['default_journal_id'])
            default_move_type = 'out_invoice' if journal.type == 'sale' else 'in_invoice'
        elif invoice._context.get('default_move_type'):
            default_move_type = self._context['default_move_type']
        elif invoice.move_type in self.env['account.move'].get_invoice_types(include_receipts=True):
            # in case an attachment is saved on a draft invoice previously created, we might
            # have lost the default value in context but the type was already set
            default_move_type = invoice.move_type

        if not default_move_type:
            raise UserError(_("No information about the journal or the type of invoice is passed"))
        if default_move_type == 'entry':
            return

        # Total amount.
        elements = tree.xpath('//ram:GrandTotalAmount', namespaces=tree.nsmap)
        total_amount = elements and float(elements[0].text) or 0.0

        # Refund type.
        # There is two modes to handle refund in Factur-X:
        # a) type_code == 380 for invoice, type_code == 381 for refund, all positive amounts.
        # b) type_code == 380, negative amounts in case of refund.
        # To handle both, we consider the 'a' mode and switch to 'b' if a negative amount is encountered.
        elements = tree.xpath('//rsm:ExchangedDocument/ram:TypeCode', namespaces=tree.nsmap)
        type_code = elements[0].text

        default_move_type.replace('_refund', '_invoice')
        if type_code == '381':
            default_move_type = 'out_refund' if default_move_type == 'out_invoice' else 'in_refund'
            refund_sign = -1
        else:
            # Handle 'b' refund mode.
            if total_amount < 0:
                default_move_type = 'out_refund' if default_move_type == 'out_invoice' else 'in_refund'
            refund_sign = -1 if 'refund' in default_move_type else 1

        # Write the type as the journal entry is already created.
        invoice.move_type = default_move_type

        # self could be a single record (editing) or be empty (new).
        with Form(invoice.with_context(default_move_type=default_move_type,
                                       account_predictive_bills_disable_prediction=True)) as invoice_form:
            partner_type = invoice_form.journal_id.type == 'purchase' and 'SellerTradeParty' or 'BuyerTradeParty'
            invoice_form.partner_id = self._retrieve_partner(
                name=_find_value(f"/ram:{partner_type}/ram:Name"),
                mail=_find_value(f"//ram:{partner_type}//ram:URIID[@schemeID='SMTP']"),
                vat=_find_value(f"//ram:{partner_type}/ram:SpecifiedTaxRegistration/ram:ID"),
            )

            # Delivery partner
            if 'partner_shipping_id' in invoice._fields:
                invoice_form.partner_shipping_id = self._retrieve_partner(
                    name=_find_value("//ram:ShipToTradeParty/ram:Name"),
                    mail=_find_value("//ram:ShipToTradeParty//ram:URIID[@schemeID='SMTP']"),
                    vat=_find_value("//ram:ShipToTradeParty/ram:SpecifiedTaxRegistration/ram:ID"),
                )

            # Reference.
            elements = tree.xpath('//rsm:ExchangedDocument/ram:ID', namespaces=tree.nsmap)
            if elements:
                invoice_form.ref = elements[0].text

            # Name.
            elements = tree.xpath('//ram:BuyerOrderReferencedDocument/ram:IssuerAssignedID', namespaces=tree.nsmap)
            if elements:
                invoice_form.payment_reference = elements[0].text

            # Comment.
            elements = tree.xpath('//ram:IncludedNote/ram:Content', namespaces=tree.nsmap)
            if elements:
                invoice_form.narration = elements[0].text

            # Total amount.
            elements = tree.xpath('//ram:GrandTotalAmount', namespaces=tree.nsmap)
            if elements:

                # Currency.
                currency_str = elements[0].attrib.get('currencyID', None)
                if currency_str:
                    invoice_form.currency_id = self._retrieve_currency(currency_str)

                    # Store xml total amount.
                    amount_total_import = total_amount * refund_sign

            # Date.
            elements = tree.xpath('//rsm:ExchangedDocument/ram:IssueDateTime/udt:DateTimeString', namespaces=tree.nsmap)
            if elements:
                date_str = elements[0].text
                date_obj = datetime.strptime(date_str, DEFAULT_FACTURX_DATE_FORMAT)
                invoice_form.invoice_date = date_obj.strftime(DEFAULT_SERVER_DATE_FORMAT)

            # Due date.
            elements = tree.xpath('//ram:SpecifiedTradePaymentTerms/ram:DueDateDateTime/udt:DateTimeString', namespaces=tree.nsmap)
            if elements:
                date_str = elements[0].text
                date_obj = datetime.strptime(date_str, DEFAULT_FACTURX_DATE_FORMAT)
                invoice_form.invoice_date_due = date_obj.strftime(DEFAULT_SERVER_DATE_FORMAT)

            # Invoice lines.
            elements = tree.xpath('//ram:IncludedSupplyChainTradeLineItem', namespaces=tree.nsmap)
            if elements:
                for element in elements:
                    with invoice_form.invoice_line_ids.new() as invoice_line_form:

                        # Sequence.
                        line_elements = element.xpath('.//ram:AssociatedDocumentLineDocument/ram:LineID', namespaces=tree.nsmap)
                        if line_elements:
                            invoice_line_form.sequence = int(line_elements[0].text)

                        # Product.
                        name = _find_value('.//ram:SpecifiedTradeProduct/ram:Name', element)
                        if name:
                            invoice_line_form.name = name
                        invoice_line_form.product_id = self._retrieve_product(
                            default_code=_find_value('.//ram:SpecifiedTradeProduct/ram:SellerAssignedID', element),
                            name=_find_value('.//ram:SpecifiedTradeProduct/ram:Name', element),
                            barcode=_find_value('.//ram:SpecifiedTradeProduct/ram:GlobalID', element)
                        )

                        # Quantity.
                        line_elements = element.xpath('.//ram:SpecifiedLineTradeDelivery/ram:BilledQuantity', namespaces=tree.nsmap)
                        if line_elements:
                            invoice_line_form.quantity = float(line_elements[0].text)

                        # Price Unit.
                        line_elements = element.xpath('.//ram:GrossPriceProductTradePrice/ram:ChargeAmount', namespaces=tree.nsmap)
                        if line_elements:
                            quantity_elements = element.xpath('.//ram:GrossPriceProductTradePrice/ram:BasisQuantity', namespaces=tree.nsmap)
                            if quantity_elements:
                                invoice_line_form.price_unit = float(line_elements[0].text) / float(quantity_elements[0].text)
                            else:
                                invoice_line_form.price_unit = float(line_elements[0].text)
                        else:
                            line_elements = element.xpath('.//ram:NetPriceProductTradePrice/ram:ChargeAmount', namespaces=tree.nsmap)
                            if line_elements:
                                quantity_elements = element.xpath('.//ram:NetPriceProductTradePrice/ram:BasisQuantity', namespaces=tree.nsmap)
                                if quantity_elements:
                                    invoice_line_form.price_unit = float(line_elements[0].text) / float(quantity_elements[0].text)
                                else:
                                    invoice_line_form.price_unit = float(line_elements[0].text)
                        # Discount.
                        line_elements = element.xpath('.//ram:AppliedTradeAllowanceCharge/ram:CalculationPercent', namespaces=tree.nsmap)
                        if line_elements:
                            invoice_line_form.discount = float(line_elements[0].text)

                        # Taxes
                        tax_element = element.xpath('.//ram:SpecifiedLineTradeSettlement/ram:ApplicableTradeTax/ram:RateApplicablePercent', namespaces=tree.nsmap)
                        invoice_line_form.tax_ids.clear()
                        for eline in tax_element:
                            tax = self._retrieve_tax(
                                amount=eline.text,
                                type_tax_use=invoice_form.journal_id.type
                            )
                            if tax:
                                invoice_line_form.tax_ids.add(tax)

            elif amount_total_import:
                # No lines in BASICWL.
                with invoice_form.invoice_line_ids.new() as invoice_line_form:
                    invoice_line_form.name = invoice_form.comment or '/'
                    invoice_line_form.quantity = 1
                    invoice_line_form.price_unit = amount_total_import

        return invoice_form.save()
    def test_in_refund_line_onchange_currency_1(self):
        # New journal having a foreign currency set.
        journal = self.company_data['default_journal_purchase'].copy()
        journal.currency_id = self.currency_data['currency']

        move_form = Form(self.invoice)
        move_form.journal_id = journal
        move_form.save()

        self.assertInvoiceValues(
            self.invoice, [
                {
                    **self.product_line_vals_1,
                    'currency_id':
                    journal.currency_id.id,
                    'amount_currency':
                    -800.0,
                    'credit':
                    400.0,
                },
                {
                    **self.product_line_vals_2,
                    'currency_id':
                    journal.currency_id.id,
                    'amount_currency':
                    -160.0,
                    'credit':
                    80.0,
                },
                {
                    **self.tax_line_vals_1,
                    'currency_id': journal.currency_id.id,
                    'amount_currency': -144.0,
                    'credit': 72.0,
                },
                {
                    **self.tax_line_vals_2,
                    'currency_id': journal.currency_id.id,
                    'amount_currency': -24.0,
                    'credit': 12.0,
                },
                {
                    **self.term_line_vals_1,
                    'currency_id': journal.currency_id.id,
                    'amount_currency': 1128.0,
                    'debit': 564.0,
                },
            ], {
                **self.move_vals,
                'currency_id': journal.currency_id.id,
                'journal_id': journal.id,
            })

        move_form = Form(self.invoice)
        # Change the date to get another rate: 1/3 instead of 1/2.
        move_form.date = fields.Date.from_string('2016-01-01')
        move_form.save()

        self.assertInvoiceValues(
            self.invoice, [
                {
                    **self.product_line_vals_1,
                    'currency_id':
                    journal.currency_id.id,
                    'amount_currency':
                    -800.0,
                    'credit':
                    266.67,
                },
                {
                    **self.product_line_vals_2,
                    'currency_id':
                    journal.currency_id.id,
                    'amount_currency':
                    -160.0,
                    'credit':
                    53.33,
                },
                {
                    **self.tax_line_vals_1,
                    'currency_id': journal.currency_id.id,
                    'amount_currency': -144.0,
                    'credit': 48.0,
                },
                {
                    **self.tax_line_vals_2,
                    'currency_id': journal.currency_id.id,
                    'amount_currency': -24.0,
                    'credit': 8.0,
                },
                {
                    **self.term_line_vals_1,
                    'currency_id': journal.currency_id.id,
                    'amount_currency': 1128.0,
                    'debit': 376.0,
                },
            ], {
                **self.move_vals,
                'currency_id': journal.currency_id.id,
                'journal_id': journal.id,
                'date': fields.Date.from_string('2016-01-01'),
            })

        move_form = Form(self.invoice)
        with move_form.invoice_line_ids.edit(0) as line_form:
            # 0.045 * 0.1 = 0.0045. As the foreign currency has a 0.001 rounding,
            # the result should be 0.005 after rounding.
            line_form.quantity = 0.1
            line_form.price_unit = 0.045
        move_form.save()

        self.assertInvoiceValues(
            self.invoice, [
                {
                    **self.product_line_vals_1,
                    'quantity': 0.1,
                    'price_unit': 0.05,
                    'price_subtotal': 0.005,
                    'price_total': 0.006,
                    'currency_id': journal.currency_id.id,
                    'amount_currency': -0.005,
                    'credit': 0.0,
                },
                {
                    **self.product_line_vals_2,
                    'currency_id':
                    journal.currency_id.id,
                    'amount_currency':
                    -160.0,
                    'credit':
                    53.33,
                },
                {
                    **self.tax_line_vals_1,
                    'price_unit': 24.0,
                    'price_subtotal': 24.001,
                    'price_total': 24.001,
                    'currency_id': journal.currency_id.id,
                    'amount_currency': -24.001,
                    'credit': 8.0,
                },
                {
                    **self.tax_line_vals_2,
                    'currency_id': journal.currency_id.id,
                    'amount_currency': -24.0,
                    'credit': 8.0,
                },
                {
                    **self.term_line_vals_1,
                    'currency_id': journal.currency_id.id,
                    'price_unit': -208.01,
                    'price_subtotal': -208.006,
                    'price_total': -208.006,
                    'amount_currency': 208.006,
                    'debit': 69.33,
                },
            ], {
                **self.move_vals,
                'currency_id': journal.currency_id.id,
                'journal_id': journal.id,
                'date': fields.Date.from_string('2016-01-01'),
                'amount_untaxed': 160.005,
                'amount_tax': 48.001,
                'amount_total': 208.006,
            })

        # The journal forces you to provide a secondary currency.
        with self.assertRaises(UserError), self.cr.savepoint():
            move_form = Form(self.invoice)
            move_form.currency_id = self.company_data['currency']
            move_form.save()

        # Exit the multi-currencies.
        journal.currency_id = False
        move_form = Form(self.invoice)
        move_form.currency_id = self.company_data['currency']
        move_form.save()

        self.assertInvoiceValues(
            self.invoice, [
                {
                    **self.product_line_vals_1,
                    'quantity': 0.1,
                    'price_unit': 0.05,
                    'price_subtotal': 0.01,
                    'price_total': 0.01,
                    'credit': 0.01,
                },
                self.product_line_vals_2,
                {
                    **self.tax_line_vals_1,
                    'price_unit': 24.0,
                    'price_subtotal': 24.0,
                    'price_total': 24.0,
                    'credit': 24.0,
                },
                self.tax_line_vals_2,
                {
                    **self.term_line_vals_1,
                    'price_unit': -208.01,
                    'price_subtotal': -208.01,
                    'price_total': -208.01,
                    'debit': 208.01,
                },
            ], {
                **self.move_vals,
                'currency_id': self.company_data['currency'].id,
                'journal_id': journal.id,
                'date': fields.Date.from_string('2016-01-01'),
                'amount_untaxed': 160.01,
                'amount_tax': 48.0,
                'amount_total': 208.01,
            })
    def test_invoice_shipment(self):
        """ Tests the case into which we make the invoice first, and then receive the goods.
        """
        # Create a PO and an invoice for it
        test_product = self.test_product_order
        purchase_order = self._create_purchase(test_product, '2017-12-01')

        invoice = self._create_invoice_for_po(purchase_order, '2017-12-23')
        move_form = Form(invoice)
        with move_form.invoice_line_ids.edit(0) as line_form:
            line_form.quantity = 1
        invoice = move_form.save()

        # The currency rate changes
        self.env['res.currency.rate'].create({
            'currency_id': self.currency_one.id,
            'company_id': self.company.id,
            'rate': 13.834739702,
            'name': '2017-12-22',
        })

        # Validate the invoice and refund the goods
        invoice.post()
        self._process_pickings(purchase_order.picking_ids, date='2017-12-24')
        picking = self.env['stock.picking'].search([('purchase_id', '=',
                                                     purchase_order.id)])
        self.check_reconciliation(invoice, picking)

        # The currency rate changes again
        self.env['res.currency.rate'].create({
            'currency_id': self.currency_one.id,
            'company_id': self.company.id,
            'rate': 10.54739702,
            'name': '2018-01-01',
        })

        # Return the goods and refund the invoice
        stock_return_picking_form = Form(
            self.env['stock.return.picking'].with_context(
                active_ids=picking.ids,
                active_id=picking.ids[0],
                active_model='stock.picking'))
        stock_return_picking = stock_return_picking_form.save()
        stock_return_picking.product_return_moves.quantity = 1.0
        stock_return_picking_action = stock_return_picking.create_returns()
        return_pick = self.env['stock.picking'].browse(
            stock_return_picking_action['res_id'])
        return_pick.action_assign()
        return_pick.move_lines.quantity_done = 1
        return_pick._action_done()
        self._change_pickings_date(return_pick, '2018-01-13')

        # The currency rate changes again
        self.env['res.currency.rate'].create({
            'currency_id': self.currency_one.id,
            'company_id': self.company.id,
            'rate': 9.56564564,
            'name': '2018-03-12',
        })

        # Refund the invoice
        refund_invoice_wiz = self.env['account.move.reversal'].with_context(
            active_model="account.move", active_ids=[invoice.id]).create({
                'reason':
                'test_invoice_shipment_refund',
                'refund_method':
                'cancel',
                'date':
                '2018-03-15',
            })
        refund_invoice = self.env['account.move'].browse(
            refund_invoice_wiz.reverse_moves()['res_id'])

        # Check the result
        self.assertTrue(invoice.invoice_payment_state ==
                        refund_invoice.invoice_payment_state == 'paid'
                        ), "Invoice and refund should both be in 'Paid' state"
        self.check_reconciliation(refund_invoice, return_pick)
 def _create_product(self, name):
     product_form = Form(self.env["product.product"])
     product_form.name = name
     product_form.type = "product"
     return product_form.save()
Exemplo n.º 52
0
    def _decode_bis3(self, tree, invoice):
        """ Decodes an EN16931 invoice into an invoice.
        :param tree:    the UBL (EN16931) tree to decode.
        :param invoice: the invoice to update or an empty recordset.
        :returns:       the invoice where the UBL (EN16931) data was imported.
        """
        def _find_value(path, root=tree):
            element = root.find(path)
            return element.text if element is not None else None

        element = tree.find('./{*}InvoiceTypeCode')
        if element is not None:
            type_code = element.text
            move_type = 'in_refund' if type_code == '381' else 'in_invoice'
        else:
            move_type = 'in_invoice'

        default_journal = invoice.with_context(
            default_move_type=move_type)._get_default_journal()

        with Form(
                invoice.with_context(
                    default_move_type=move_type,
                    default_journal_id=default_journal.id)) as invoice_form:
            # Reference
            element = tree.find('./{*}ID')
            if element is not None:
                invoice_form.ref = element.text

            # Dates
            element = tree.find('./{*}IssueDate')
            if element is not None:
                invoice_form.invoice_date = element.text
            element = tree.find('./{*}DueDate')
            if element is not None:
                invoice_form.invoice_date_due = element.text

            # Currency
            currency = self._retrieve_currency(
                _find_value('./{*}DocumentCurrencyCode'))
            if currency:
                invoice_form.currency_id = currency

            # Partner
            specific_domain = self._bis3_get_extra_partner_domains(tree)
            invoice_form.partner_id = self._retrieve_partner(
                name=_find_value(
                    './{*}AccountingSupplierParty/{*}Party/*/{*}Name'),
                phone=_find_value(
                    './{*}AccountingSupplierParty/{*}Party/*/{*}Telephone'),
                mail=_find_value(
                    './{*}AccountingSupplierParty/{*}Party/*/{*}ElectronicMail'
                ),
                vat=_find_value(
                    './{*}AccountingSupplierParty/{*}Party/{*}PartyTaxScheme/{*}CompanyID'
                ),
                domain=specific_domain,
            )

            # Lines
            for eline in tree.findall('.//{*}InvoiceLine'):
                with invoice_form.invoice_line_ids.new() as invoice_line_form:
                    # Product
                    invoice_line_form.product_id = self._retrieve_product(
                        default_code=_find_value(
                            './{*}Item/{*}SellersItemIdentification/{*}ID',
                            eline),
                        name=_find_value('./{*}Item/{*}Name', eline),
                        barcode=_find_value(
                            './{*}Item/{*}StandardItemIdentification/{*}ID[@schemeID=\'0160\']',
                            eline))

                    # Quantity
                    element = eline.find('./{*}InvoicedQuantity')
                    quantity = element is not None and float(
                        element.text) or 1.0
                    invoice_line_form.quantity = quantity

                    # Price Unit
                    element = eline.find('./{*}Price/{*}PriceAmount')
                    price_unit = element is not None and float(
                        element.text) or 0.0
                    line_extension_amount = element is not None and float(
                        element.text) or 0.0
                    invoice_line_form.price_unit = price_unit or line_extension_amount / invoice_line_form.quantity or 0.0

                    # Name
                    element = eline.find('./{*}Item/{*}Description')
                    invoice_line_form.name = element is not None and element.text or ''

                    # Taxes
                    tax_elements = eline.findall(
                        './{*}Item/{*}ClassifiedTaxCategory')
                    invoice_line_form.tax_ids.clear()
                    for tax_element in tax_elements:
                        invoice_line_form.tax_ids.add(
                            self._retrieve_tax(
                                amount=_find_value('./{*}Percent',
                                                   tax_element),
                                type_tax_use=invoice_form.journal_id.type))

        return invoice_form.save()
Exemplo n.º 53
0
    def test_partial_payment(self):
        """ Create test to pay invoices (cust. inv + vendor bill) with partial payment """
        # Test Customer Invoice
        inv_1 = self.create_invoice(amount=600)
        payment_register = Form(self.env['account.payment'].with_context(
            active_model='account.move', active_ids=inv_1.ids))
        payment_register.payment_date = time.strftime('%Y') + '-07-15'
        payment_register.journal_id = self.bank_journal_euro
        payment_register.payment_method_id = self.payment_method_manual_in

        # Perform the partial payment by setting the amount at 550 instead of 600
        payment_register.amount = 550

        payment = payment_register.save()

        self.assertEqual(len(payment), 1)
        self.assertEqual(payment.invoice_ids[0].id, inv_1.id)
        self.assertAlmostEqual(payment.amount, 550)
        self.assertEqual(payment.payment_type, 'inbound')
        self.assertEqual(payment.partner_id, self.partner_agrolait)
        self.assertEqual(payment.partner_type, 'customer')

        # Test Vendor Bill
        inv_2 = self.create_invoice(amount=500,
                                    type='in_invoice',
                                    partner=self.partner_china_exp.id)
        payment_register = Form(self.env['account.payment'].with_context(
            active_model='account.move', active_ids=inv_2.ids))
        payment_register.payment_date = time.strftime('%Y') + '-07-15'
        payment_register.journal_id = self.bank_journal_euro
        payment_register.payment_method_id = self.payment_method_manual_in

        # Perform the partial payment by setting the amount at 300 instead of 500
        payment_register.amount = 300

        payment = payment_register.save()

        self.assertEqual(len(payment), 1)
        self.assertEqual(payment.invoice_ids[0].id, inv_2.id)
        self.assertAlmostEqual(payment.amount, 300)
        self.assertEqual(payment.payment_type, 'outbound')
        self.assertEqual(payment.partner_id, self.partner_china_exp)
        self.assertEqual(payment.partner_type, 'supplier')
Exemplo n.º 54
0
    def test_out_refund_line_onchange_business_fields_1(self):
        move_form = Form(self.invoice)
        with move_form.invoice_line_ids.edit(0) as line_form:
            # Current price_unit is 1000.
            # We set quantity = 4, discount = 50%, price_unit = 400. The debit/credit fields don't change because (4 * 500) * 0.5 = 1000.
            line_form.quantity = 4
            line_form.discount = 50
            line_form.price_unit = 500
        move_form.save()

        self.assertInvoiceValues(self.invoice, [
            {
                **self.product_line_vals_1,
                'quantity': 4,
                'discount': 50.0,
                'price_unit': 500.0,
            },
            self.product_line_vals_2,
            self.tax_line_vals_1,
            self.tax_line_vals_2,
            self.term_line_vals_1,
        ], self.move_vals)

        move_form = Form(self.invoice)
        with move_form.line_ids.edit(2) as line_form:
            # Reset field except the discount that becomes 100%.
            # /!\ The modification is made on the accounting tab.
            line_form.quantity = 1
            line_form.discount = 100
            line_form.price_unit = 1000
        move_form.save()

        self.assertInvoiceValues(
            self.invoice, [
                {
                    **self.product_line_vals_1,
                    'discount': 100.0,
                    'price_subtotal': 0.0,
                    'price_total': 0.0,
                    'amount_currency': 0.0,
                    'debit': 0.0,
                },
                self.product_line_vals_2,
                {
                    **self.tax_line_vals_1,
                    'price_unit': 30.0,
                    'price_subtotal': 30.0,
                    'price_total': 30.0,
                    'amount_currency': 30.0,
                    'debit': 30.0,
                },
                self.tax_line_vals_2,
                {
                    **self.term_line_vals_1,
                    'price_unit': -260.0,
                    'price_subtotal': -260.0,
                    'price_total': -260.0,
                    'amount_currency': -260.0,
                    'credit': 260.0,
                },
            ], {
                **self.move_vals,
                'amount_untaxed': 200.0,
                'amount_tax': 60.0,
                'amount_total': 260.0,
            })
Exemplo n.º 55
0
    def _import_xml_invoice(self, content, attachment):
        ''' Extract invoice values from the E-Invoice xml tree passed as parameter.

        :param content: The tree of the xml file.
        :return: A dictionary containing account.invoice values to create/update it.
        '''

        try:
            tree = etree.fromstring(content)
        except:
            _logger.info('Error during decoding XML file')
            return self.env['account.invoice']

        invoices = self.env['account.invoice']

        # possible to have multiple invoices in the case of an invoice batch, the batch itself is repeated for every invoice of the batch
        for body_tree in tree.xpath('//FatturaElettronicaBody',
                                    namespaces=tree.nsmap):

            elements = tree.xpath('//DatiGeneraliDocumento/TipoDocumento',
                                  namespaces=tree.nsmap)
            if elements and elements[0].text and elements[0].text == 'TD01':
                self_ctx = self.with_context(type='in_invoice')
            elif elements and elements[0].text and elements[0].text == 'TD04':
                self_ctx = self.with_context(type='in_refund')
            else:
                _logger.info(
                    _('Document type not managed: %s.') % (elements[0].text))

            # type must be present in the context to get the right behavior of the _default_journal method (account.invoice).
            # journal_id must be present in the context to get the right behavior of the _default_account method (account.invoice.line).

            elements = tree.xpath('//CessionarioCommittente//IdCodice',
                                  namespaces=tree.nsmap)
            company = elements and self.env['res.company'].search(
                [('vat', 'ilike', elements[0].text)], limit=1)
            if not company:
                elements = tree.xpath(
                    '//CessionarioCommittente//CodiceFiscale',
                    namespaces=tree.nsmap)
                company = elements and self.env['res.company'].search(
                    [('l10n_it_codice_fiscale', 'ilike', elements[0].text)],
                    limit=1)

            if company:
                self_ctx = self_ctx.with_context(company_id=company.id)
            else:
                company = self.env.user.company_id
                if elements:
                    _logger.info(
                        _('Company not found with codice fiscale: %s. The company\'s user is set by default.'
                          ) % elements[0].text)
                else:
                    _logger.info(
                        _('Company not found. The company\'s user is set by default.'
                          ))

            if not self.env.user._is_superuser():
                if self.env.user.company_id != company:
                    raise UserError(
                        _("You can only import invoice concern your current company: %s"
                          ) % self.env.user.company_id.display_name)

            journal_id = self_ctx._default_journal().id
            self_ctx = self_ctx.with_context(journal_id=journal_id)

            # self could be a single record (editing) or be empty (new).
            with Form(self_ctx,
                      view='account.invoice_supplier_form') as invoice_form:
                message_to_log = []

                invoice_form.company_id = company

                # Refund type.
                # TD01 == invoice
                # TD02 == advance/down payment on invoice
                # TD03 == advance/down payment on fee
                # TD04 == credit note
                # TD05 == debit note
                # TD06 == fee
                elements = tree.xpath('//DatiGeneraliDocumento/TipoDocumento',
                                      namespaces=tree.nsmap)
                if elements and elements[0].text and elements[0].text == 'TD01':
                    invoice_form.type = 'in_invoice'
                elif elements and elements[0].text and elements[
                        0].text == 'TD04':
                    invoice_form.type = 'in_refund'

                # Partner (first step to avoid warning 'Warning! You must first select a partner.'). <1.2>
                elements = tree.xpath('//CedentePrestatore//IdCodice',
                                      namespaces=tree.nsmap)
                partner = elements and self.env['res.partner'].search([
                    '&', ('vat', 'ilike', elements[0].text), '|',
                    ('company_id', '=', company.id), ('company_id', '=', False)
                ],
                                                                      limit=1)
                if not partner:
                    elements = tree.xpath('//CedentePrestatore//CodiceFiscale',
                                          namespaces=tree.nsmap)
                    partner = elements and self.env['res.partner'].search(
                        [
                            '&',
                            ('l10n_it_codice_fiscale', '=', elements[0].text),
                            '|', ('company_id', '=', company.id),
                            ('company_id', '=', False)
                        ],
                        limit=1)
                if not partner:
                    elements = tree.xpath('//DatiTrasmissione//Email',
                                          namespaces=tree.nsmap)
                    partner = elements and self.env['res.partner'].search(
                        [
                            '&', '|', ('email', '=', elements[0].text),
                            ('l10n_it_pec_email', '=', elements[0].text), '|',
                            ('company_id', '=', company.id),
                            ('company_id', '=', False)
                        ],
                        limit=1)
                if partner:
                    invoice_form.partner_id = partner
                else:
                    message_to_log.append("%s<br/>%s" % (_(
                        "Vendor not found, useful informations from XML file:"
                    ), self._compose_info_message(tree,
                                                  './/CedentePrestatore')))

                # Numbering attributed by the transmitter. <1.1.2>
                elements = tree.xpath('//ProgressivoInvio',
                                      namespaces=tree.nsmap)
                if elements:
                    invoice_form.name = elements[0].text

                elements = body_tree.xpath('.//DatiGeneraliDocumento//Numero',
                                           namespaces=body_tree.nsmap)
                if elements:
                    invoice_form.reference = elements[0].text

                # Currency. <2.1.1.2>
                elements = body_tree.xpath('.//DatiGeneraliDocumento/Divisa',
                                           namespaces=body_tree.nsmap)
                if elements:
                    currency_str = elements[0].text
                    currency = self.env.ref('base.%s' % currency_str.upper(),
                                            raise_if_not_found=False)
                    if currency != self.env.user.company_id.currency_id and currency.active:
                        invoice_form.currency_id = currency

                # Date. <2.1.1.3>
                elements = body_tree.xpath('.//DatiGeneraliDocumento/Data',
                                           namespaces=body_tree.nsmap)
                if elements:
                    date_str = elements[0].text
                    date_obj = datetime.strptime(
                        date_str, DEFAULT_FACTUR_ITALIAN_DATE_FORMAT)
                    invoice_form.date_invoice = date_obj.strftime(
                        DEFAULT_FACTUR_ITALIAN_DATE_FORMAT)

                #  Dati Bollo. <2.1.1.6>
                elements = body_tree.xpath(
                    './/DatiGeneraliDocumento/DatiBollo/ImportoBollo',
                    namespaces=body_tree.nsmap)
                if elements:
                    invoice_form.l10n_it_stamp_duty = float(elements[0].text)

                # List of all amount discount (will be add after all article to avoid to have a negative sum)
                discount_list = []
                percentage_global_discount = 1.0

                # Global discount. <2.1.1.8>
                discount_elements = body_tree.xpath(
                    './/DatiGeneraliDocumento/ScontoMaggiorazione',
                    namespaces=body_tree.nsmap)
                total_discount_amount = 0.0
                if discount_elements:
                    for discount_element in discount_elements:
                        discount_line = discount_element.xpath(
                            './/Tipo', namespaces=body_tree.nsmap)
                        discount_sign = -1
                        if discount_line and discount_line[0].text == 'SC':
                            discount_sign = 1
                        discount_percentage = discount_element.xpath(
                            './/Percentuale', namespaces=body_tree.nsmap)
                        if discount_percentage and discount_percentage[0].text:
                            percentage_global_discount *= 1 - float(
                                discount_percentage[0].text
                            ) / 100 * discount_sign

                        discount_amount_text = discount_element.xpath(
                            './/Importo', namespaces=body_tree.nsmap)
                        if discount_amount_text and discount_amount_text[
                                0].text:
                            discount_amount = float(discount_amount_text[0].
                                                    text) * discount_sign * -1
                            discount = {}
                            discount["seq"] = 0

                            if discount_amount < 0:
                                discount["name"] = _('GLOBAL DISCOUNT')
                            else:
                                discount["name"] = _('GLOBAL EXTRA CHARGE')
                            discount["amount"] = discount_amount
                            discount["tax"] = []
                            discount_list.append(discount)

                # Comment. <2.1.1.11>
                elements = body_tree.xpath('.//DatiGeneraliDocumento//Causale',
                                           namespaces=body_tree.nsmap)
                for element in elements:
                    invoice_form.comment = '%s%s\n' % (invoice_form.comment
                                                       or '', element.text)

                # Informations relative to the purchase order, the contract, the agreement,
                # the reception phase or invoices previously transmitted
                # <2.1.2> - <2.1.6>
                for document_type in [
                        'DatiOrdineAcquisto', 'DatiContratto',
                        'DatiConvenzione', 'DatiRicezione',
                        'DatiFattureCollegate'
                ]:
                    elements = body_tree.xpath('.//DatiGenerali/' +
                                               document_type,
                                               namespaces=body_tree.nsmap)
                    if elements:
                        for element in elements:
                            message_to_log.append(
                                "%s %s<br/>%s" %
                                (document_type, _("from XML file:"),
                                 self._compose_info_message(element, '.')))

                #  Dati DDT. <2.1.8>
                elements = body_tree.xpath('.//DatiGenerali/DatiDDT',
                                           namespaces=body_tree.nsmap)
                if elements:
                    message_to_log.append(
                        "%s<br/>%s" %
                        (_("Transport informations from XML file:"),
                         self._compose_info_message(
                             body_tree, './/DatiGenerali/DatiDDT')))

                # Due date. <2.4.2.5>
                elements = body_tree.xpath(
                    './/DatiPagamento/DettaglioPagamento/DataScadenzaPagamento',
                    namespaces=body_tree.nsmap)
                if elements:
                    date_str = elements[0].text
                    date_obj = datetime.strptime(
                        date_str, DEFAULT_FACTUR_ITALIAN_DATE_FORMAT)
                    invoice_form.date_due = fields.Date.to_string(date_obj)

                # Total amount. <2.4.2.6>
                elements = body_tree.xpath('.//ImportoPagamento',
                                           namespaces=body_tree.nsmap)
                amount_total_import = 0
                for element in elements:
                    amount_total_import += float(element.text)
                if amount_total_import:
                    message_to_log.append(
                        _("Total amount from the XML File: %s") %
                        (amount_total_import))

                # Bank account. <2.4.2.13>
                elements = body_tree.xpath(
                    './/DatiPagamento/DettaglioPagamento/IBAN',
                    namespaces=body_tree.nsmap)
                if elements:
                    if invoice_form.partner_id and invoice_form.partner_id.commercial_partner_id:
                        bank = self.env['res.partner.bank'].search([
                            ('acc_number', '=', elements[0].text),
                            ('partner_id.id', '=',
                             invoice_form.partner_id.commercial_partner_id.id)
                        ])
                    else:
                        bank = self.env['res.partner.bank'].search([
                            ('acc_number', '=', elements[0].text)
                        ])
                    if bank:
                        invoice_form.partner_bank_id = bank
                    else:
                        message_to_log.append("%s<br/>%s" % (
                            _("Bank account not found, useful informations from XML file:"
                              ),
                            self._compose_multi_info_message(
                                body_tree, [
                                    './/DatiPagamento//Beneficiario',
                                    './/DatiPagamento//IstitutoFinanziario',
                                    './/DatiPagamento//IBAN',
                                    './/DatiPagamento//ABI',
                                    './/DatiPagamento//CAB',
                                    './/DatiPagamento//BIC',
                                    './/DatiPagamento//ModalitaPagamento'
                                ])))
                else:
                    elements = body_tree.xpath(
                        './/DatiPagamento/DettaglioPagamento',
                        namespaces=body_tree.nsmap)
                    if elements:
                        message_to_log.append("%s<br/>%s" % (
                            _("Bank account not found, useful informations from XML file:"
                              ),
                            self._compose_info_message(body_tree,
                                                       './/DatiPagamento')))

                # Invoice lines. <2.2.1>
                elements = body_tree.xpath('.//DettaglioLinee',
                                           namespaces=body_tree.nsmap)
                if elements:
                    for element in elements:
                        with invoice_form.invoice_line_ids.new(
                        ) as invoice_line_form:

                            # Sequence.
                            line_elements = element.xpath(
                                './/NumeroLinea', namespaces=body_tree.nsmap)
                            if line_elements:
                                invoice_line_form.sequence = int(
                                    line_elements[0].text) * 2

                            # Product.
                            line_elements = element.xpath(
                                './/Descrizione', namespaces=body_tree.nsmap)
                            if line_elements:
                                invoice_line_form.name = " ".join(
                                    line_elements[0].text.split())

                            elements_code = element.xpath(
                                './/CodiceArticolo',
                                namespaces=body_tree.nsmap)
                            if elements_code:
                                for element_code in elements_code:
                                    type_code = element_code.xpath(
                                        './/CodiceTipo',
                                        namespaces=body_tree.nsmap)[0]
                                    code = element_code.xpath(
                                        './/CodiceValore',
                                        namespaces=body_tree.nsmap)[0]
                                    if type_code.text == 'EAN':
                                        product = self.env[
                                            'product.product'].search([
                                                ('barcode', '=', code.text)
                                            ])
                                        if product:
                                            invoice_line_form.product_id = product
                                            break
                                    if partner:
                                        product_supplier = self.env[
                                            'product.supplierinfo'].search([
                                                ('name', '=', partner.id),
                                                ('product_code', '=',
                                                 code.text)
                                            ])
                                        if product_supplier and product_supplier.product_id:
                                            invoice_line_form.product_id = product_supplier.product_id
                                            break
                                if not invoice_line_form.product_id:
                                    for element_code in elements_code:
                                        code = element_code.xpath(
                                            './/CodiceValore',
                                            namespaces=body_tree.nsmap)[0]
                                        product = self.env[
                                            'product.product'].search([
                                                ('default_code', '=',
                                                 code.text)
                                            ])
                                        if product:
                                            invoice_line_form.product_id = product
                                            break

                            # Price Unit.
                            line_elements = element.xpath(
                                './/PrezzoUnitario',
                                namespaces=body_tree.nsmap)
                            if line_elements:
                                invoice_line_form.price_unit = float(
                                    line_elements[0].text)

                            # Quantity.
                            line_elements = element.xpath(
                                './/Quantita', namespaces=body_tree.nsmap)
                            if line_elements:
                                invoice_line_form.quantity = float(
                                    line_elements[0].text)
                            else:
                                invoice_line_form.quantity = 1

                            # Taxes
                            tax_element = element.xpath(
                                './/AliquotaIVA', namespaces=body_tree.nsmap)
                            natura_element = element.xpath(
                                './/Natura', namespaces=body_tree.nsmap)
                            invoice_line_form.invoice_line_tax_ids.clear()
                            if tax_element and tax_element[0].text:
                                percentage = float(tax_element[0].text)
                                if natura_element and natura_element[0].text:
                                    l10n_it_kind_exoneration = natura_element[
                                        0].text
                                    tax = self.env['account.tax'].search(
                                        [
                                            ('company_id', '=',
                                             invoice_form.company_id.id),
                                            ('amount_type', '=', 'percent'),
                                            ('type_tax_use', '=', 'purchase'),
                                            ('amount', '=', percentage),
                                            ('l10n_it_kind_exoneration', '=',
                                             l10n_it_kind_exoneration),
                                        ],
                                        limit=1)
                                else:
                                    tax = self.env['account.tax'].search(
                                        [
                                            ('company_id', '=',
                                             invoice_form.company_id.id),
                                            ('amount_type', '=', 'percent'),
                                            ('type_tax_use', '=', 'purchase'),
                                            ('amount', '=', percentage),
                                        ],
                                        limit=1)
                                    l10n_it_kind_exoneration = ''

                                if tax:
                                    invoice_line_form.invoice_line_tax_ids.add(
                                        tax)
                                else:
                                    if l10n_it_kind_exoneration:
                                        message_to_log.append(
                                            _("Tax not found with percentage: %s and exoneration %s for the article: %s"
                                              ) % (percentage,
                                                   l10n_it_kind_exoneration,
                                                   invoice_line_form.name))
                                    else:
                                        message_to_log.append(
                                            _("Tax not found with percentage: %s for the article: %s"
                                              ) % (percentage,
                                                   invoice_line_form.name))

                            # Discount in cascade mode.
                            # if 3 discounts : -10% -50€ -20%
                            # the result must be : (((price -10%)-50€) -20%)
                            # Generic form : (((price -P1%)-A1€) -P2%)
                            # It will be split in two parts: fix amount and pourcent amount
                            # example: (((((price - A1€) -P2%) -A3€) -A4€) -P5€)
                            # pourcent: 1-(1-P2)*(1-P5)
                            # fix amount: A1*(1-P2)*(1-P5)+A3*(1-P5)+A4*(1-P5) (we must take account of all
                            # percentage present after the fix amount)
                            line_elements = element.xpath(
                                './/ScontoMaggiorazione',
                                namespaces=body_tree.nsmap)
                            total_discount_amount = 0.0
                            total_discount_percentage = percentage_global_discount
                            if line_elements:
                                for line_element in line_elements:
                                    discount_line = line_element.xpath(
                                        './/Tipo', namespaces=body_tree.nsmap)
                                    discount_sign = -1
                                    if discount_line and discount_line[
                                            0].text == 'SC':
                                        discount_sign = 1
                                    discount_percentage = line_element.xpath(
                                        './/Percentuale',
                                        namespaces=body_tree.nsmap)
                                    if discount_percentage and discount_percentage[
                                            0].text:
                                        pourcentage_actual = 1 - float(
                                            discount_percentage[0].text
                                        ) / 100 * discount_sign
                                        total_discount_percentage *= pourcentage_actual
                                        total_discount_amount *= pourcentage_actual

                                    discount_amount = line_element.xpath(
                                        './/Importo',
                                        namespaces=body_tree.nsmap)
                                    if discount_amount and discount_amount[
                                            0].text:
                                        total_discount_amount += float(
                                            discount_amount[0].text
                                        ) * discount_sign * -1

                                # Save amount discount.
                                if total_discount_amount != 0:
                                    discount = {}
                                    discount[
                                        "seq"] = invoice_line_form.sequence + 1

                                    if total_discount_amount < 0:
                                        discount["name"] = _(
                                            'DISCOUNT: '
                                        ) + invoice_line_form.name
                                    else:
                                        discount["name"] = _(
                                            'EXTRA CHARGE: '
                                        ) + invoice_line_form.name
                                    discount["amount"] = total_discount_amount
                                    discount["tax"] = []
                                    for tax in invoice_line_form.invoice_line_tax_ids:
                                        discount["tax"].append(tax)
                                    discount_list.append(discount)
                            invoice_line_form.discount = (
                                1 - total_discount_percentage) * 100

                # Apply amount discount.
                for discount in discount_list:
                    with invoice_form.invoice_line_ids.new(
                    ) as invoice_line_form_discount:
                        invoice_line_form_discount.invoice_line_tax_ids.clear()
                        invoice_line_form_discount.sequence = discount["seq"]
                        invoice_line_form_discount.name = discount["name"]
                        invoice_line_form_discount.price_unit = discount[
                            "amount"]

            new_invoice = invoice_form.save()
            new_invoice.l10n_it_send_state = "other"

            elements = body_tree.xpath('.//Allegati',
                                       namespaces=body_tree.nsmap)
            if elements:
                for element in elements:
                    name_attachment = element.xpath(
                        './/NomeAttachment',
                        namespaces=body_tree.nsmap)[0].text
                    attachment_64 = str.encode(
                        element.xpath('.//Attachment',
                                      namespaces=body_tree.nsmap)[0].text)
                    attachment_64 = self.env['ir.attachment'].create({
                        'name':
                        name_attachment,
                        'datas':
                        attachment_64,
                        'datas_fname':
                        name_attachment,
                        'type':
                        'binary',
                    })

                    # default_res_id is had to context to avoid facturx to import his content
                    new_invoice.with_context(
                        default_res_id=new_invoice.id).message_post(
                            body=(_("Attachment from XML")),
                            attachment_ids=[attachment_64.id])

            for message in message_to_log:
                new_invoice.message_post(body=message)

            if attachment:
                new_invoice.l10n_it_einvoice_name = attachment.name
                attachment.write({
                    'res_model': 'account.invoice',
                    'res_id': new_invoice.id
                })
                new_invoice.message_post(attachment_ids=[attachment.id])
            invoices += new_invoice
        return invoices
Exemplo n.º 56
0
    def test_sale_mrp_kit_bom_cogs(self):
        """Check invoice COGS aml after selling and delivering a product
        with Kit BoM having another product with Kit BoM as component"""

        # ----------------------------------------------
        # BoM of Kit A:
        #   - BoM Type: Kit
        #   - Quantity: 3
        #   - Components:
        #     * 2 x Kit B
        #     * 1 x Component A (Cost: $3, Storable)
        #
        # BoM of Kit B:
        #   - BoM Type: Kit
        #   - Quantity: 10
        #   - Components:
        #     * 2 x Component B (Cost: $4, Storable)
        #     * 3 x Component BB (Cost: $5, Consumable)
        # ----------------------------------------------

        self.env.user.company_id.anglo_saxon_accounting = True
        self.env.ref('base.USD').active = True

        self.stock_input_account = self.env['account.account'].create({
            'name': 'Stock Input',
            'code': 'StockIn',
            'user_type_id': self.env.ref('account.data_account_type_current_assets').id,
        })
        self.stock_output_account = self.env['account.account'].create({
            'name': 'Stock Output',
            'code': 'StockOut',
            'reconcile': True,
            'user_type_id': self.env.ref('account.data_account_type_current_assets').id,
        })
        self.stock_valuation_account = self.env['account.account'].create({
            'name': 'Stock Valuation',
            'code': 'StockVal',
            'user_type_id': self.env.ref('account.data_account_type_current_assets').id,
        })
        self.expense_account = self.env['account.account'].create({
            'name': 'Expense Account',
            'code': 'Exp',
            'user_type_id': self.env.ref('account.data_account_type_expenses').id,
        })
        self.income_account = self.env['account.account'].create({
            'name': 'Income Account',
            'code': 'Inc',
            'user_type_id': self.env.ref('account.data_account_type_expenses').id,
        })
        self.stock_journal = self.env['account.journal'].create({
            'name': 'Stock Journal',
            'code': 'STJTEST',
            'type': 'general',
        })
        self.recv_account = self.env['account.account'].create({
            'name': 'account receivable',
            'code': 'RECV',
            'user_type_id': self.env.ref('account.data_account_type_receivable').id,
            'reconcile': True,
        })
        self.pay_account = self.env['account.account'].create({
            'name': 'account payable',
            'code': 'PAY',
            'user_type_id': self.env.ref('account.data_account_type_payable').id,
            'reconcile': True,
        })
        self.customer = self.env['res.partner'].create({
            'name': 'customer',
            'property_account_receivable_id': self.recv_account.id,
            'property_account_payable_id': self.pay_account.id,
        })
        self.journal_sale = self.env['account.journal'].create({
            'name': 'Sale Journal - Test',
            'code': 'AJ-SALE',
            'type': 'sale',
            'company_id': self.env.user.company_id.id,
        })

        self.component_a = self._create_product('Component A', 'product', 3.00)
        self.component_b = self._create_product('Component B', 'product', 4.00)
        self.component_bb = self._create_product('Component BB', 'consu', 5.00)
        self.kit_a = self._create_product('Kit A', 'product', 0.00)
        self.kit_b = self._create_product('Kit B', 'consu', 0.00)

        self.kit_a.write({
            'categ_id': self.env.ref('product.product_category_all').id,
            'property_account_expense_id': self.expense_account.id,
            'property_account_income_id': self.income_account.id,
        })
        self.kit_a.categ_id.write({
            'property_stock_account_input_categ_id': self.stock_input_account.id,
            'property_stock_account_output_categ_id': self.stock_output_account.id,
            'property_stock_valuation_account_id': self.stock_valuation_account.id,
            'property_stock_journal': self.stock_journal.id,
            'property_valuation': 'real_time',
        })

        # Create BoM for Kit A
        bom_product_form = Form(self.env['mrp.bom'])
        bom_product_form.product_id = self.kit_a
        bom_product_form.product_tmpl_id = self.kit_a.product_tmpl_id
        bom_product_form.product_qty = 3.0
        bom_product_form.type = 'phantom'
        with bom_product_form.bom_line_ids.new() as bom_line:
            bom_line.product_id = self.kit_b
            bom_line.product_qty = 2.0
        with bom_product_form.bom_line_ids.new() as bom_line:
            bom_line.product_id = self.component_a
            bom_line.product_qty = 1.0
        self.bom_a = bom_product_form.save()

        # Create BoM for Kit B
        bom_product_form = Form(self.env['mrp.bom'])
        bom_product_form.product_id = self.kit_b
        bom_product_form.product_tmpl_id = self.kit_b.product_tmpl_id
        bom_product_form.product_qty = 10.0
        bom_product_form.type = 'phantom'
        with bom_product_form.bom_line_ids.new() as bom_line:
            bom_line.product_id = self.component_b
            bom_line.product_qty = 2.0
        with bom_product_form.bom_line_ids.new() as bom_line:
            bom_line.product_id = self.component_bb
            bom_line.product_qty = 3.0
        self.bom_b = bom_product_form.save()

        so = self.env['sale.order'].create({
            'partner_id': self.customer.id,
            'order_line': [
                (0, 0, {
                    'name': self.kit_a.name,
                    'product_id': self.kit_a.id,
                    'product_uom_qty': 1.0,
                    'product_uom': self.kit_a.uom_id.id,
                    'price_unit': 1,
                    'tax_id': False,
                })],
        })
        so.action_confirm()
        so.picking_ids.move_ids.quantity_done = 1
        so.picking_ids.button_validate()

        invoice = so.with_context(default_journal_id=self.journal_sale.id)._create_invoices()
        invoice.action_post()

        # Check the resulting accounting entries
        amls = invoice.line_ids
        self.assertEqual(len(amls), 4)
        stock_out_aml = amls.filtered(lambda aml: aml.account_id == self.stock_output_account)
        self.assertEqual(stock_out_aml.debit, 0)
        self.assertAlmostEqual(stock_out_aml.credit, 1.53, "Should not include the value of consumable component")
        cogs_aml = amls.filtered(lambda aml: aml.account_id == self.expense_account)
        self.assertAlmostEqual(cogs_aml.debit, 1.53, "Should not include the value of consumable component")
        self.assertEqual(cogs_aml.credit, 0)
    def test_invoice_shipment(self):
        """ Tests the case into which we make the invoice first, and then receive the goods.
        """
        # Create a PO and an invoice for it
        test_product = self.test_product_order
        purchase_order = self._create_purchase(test_product, '2017-12-01')

        invoice = self._create_invoice_for_po(purchase_order, '2017-12-23')
        invoice_line = self.env['account.invoice.line'].search([('invoice_id', '=', invoice.id)])
        invoice_line.quantity = 1

        # The currency rate changes
        self.env['res.currency.rate'].create({
            'currency_id': self.currency_one.id,
            'company_id': self.company.id,
            'rate': 13.834739702,
            'name': '2017-12-22',
        })

        # Validate the invoice and refund the goods
        invoice.action_invoice_open()
        self._process_pickings(purchase_order.picking_ids, date='2017-12-24')
        picking = self.env['stock.picking'].search([('purchase_id', '=', purchase_order.id)])
        self.check_reconciliation(invoice, picking)

        # The currency rate changes again
        self.env['res.currency.rate'].create({
            'currency_id': self.currency_one.id,
            'company_id': self.company.id,
            'rate': 10.54739702,
            'name': '2018-01-01',
        })

        # Return the goods and refund the invoice
        stock_return_picking_form = Form(self.env['stock.return.picking']
            .with_context(active_ids=picking.ids, active_id=picking.ids[0],
            active_model='stock.picking'))
        stock_return_picking = stock_return_picking_form.save()
        stock_return_picking.product_return_moves.quantity = 1.0
        stock_return_picking_action = stock_return_picking.create_returns()
        return_pick = self.env['stock.picking'].browse(stock_return_picking_action['res_id'])
        return_pick.action_assign()
        return_pick.move_lines.quantity_done = 1
        return_pick.action_done()
        self._change_pickings_date(return_pick, '2018-01-13')

        # The currency rate changes again
        self.env['res.currency.rate'].create({
            'currency_id': self.currency_one.id,
            'company_id': self.company.id,
            'rate': 9.56564564,
            'name': '2018-03-12',
        })

        # Refund the invoice
        refund_invoice_wiz = self.env['account.invoice.refund'].with_context(active_ids=[invoice.id]).create({
            'description': 'test_invoice_shipment_refund',
            'filter_refund': 'cancel',
            'date': '2018-03-15',
            'date_invoice': '2018-03-15',
        })
        refund_invoice_wiz.invoice_refund()

        # Check the result
        refund_invoice = self.env['account.invoice'].search([('name', '=', 'test_invoice_shipment_refund')])[0]
        self.assertTrue(invoice.state == refund_invoice.state == 'paid'), "Invoice and refund should both be in 'Paid' state"
        self.check_reconciliation(refund_invoice, return_pick)
Exemplo n.º 58
0
    def test_reset_avco_kit(self):
        """
        Test a specific use case : One product with 2 variant, each variant has its own BoM with either component_1 or
        component_2. Create a SO for one of the variant, confirm, cancel, reset to draft and then change the product of
        the SO -> There should be no traceback
        """
        component_1 = self.env['product.product'].create({'name': 'compo 1'})
        component_2 = self.env['product.product'].create({'name': 'compo 2'})

        product_category = self.env['product.category'].create({
            'name': 'test avco kit',
            'property_cost_method': 'average'
        })
        attributes = self.env['product.attribute'].create({'name': 'Legs'})
        steel_legs = self.env['product.attribute.value'].create({'attribute_id': attributes.id, 'name': 'Steel'})
        aluminium_legs = self.env['product.attribute.value'].create(
            {'attribute_id': attributes.id, 'name': 'Aluminium'})

        product = self.env['product.product'].create({
            'name': 'test product',
            'categ_id': product_category.id,
            'attribute_line_ids': [(0, 0, {
                'attribute_id': attributes.id,
                'value_ids': [(6, 0, [steel_legs.id, aluminium_legs.id])]
            })]
        })
        product_variant_ids = product.product_variant_ids.search([('id', '!=', product.id)])
        product_variant_ids[0].categ_id.property_cost_method = 'average'
        product_variant_ids[1].categ_id.property_cost_method = 'average'
        # BoM 1 with component_1
        self.env['mrp.bom'].create({
            'product_id': product_variant_ids[0].id,
            'product_tmpl_id': product_variant_ids[0].product_tmpl_id.id,
            'product_qty': 1.0,
            'consumption': 'flexible',
            'type': 'phantom',
            'bom_line_ids': [(0, 0, {'product_id': component_1.id, 'product_qty': 1})]
        })
        # BoM 2 with component_2
        self.env['mrp.bom'].create({
            'product_id': product_variant_ids[1].id,
            'product_tmpl_id': product_variant_ids[1].product_tmpl_id.id,
            'product_qty': 1.0,
            'consumption': 'flexible',
            'type': 'phantom',
            'bom_line_ids': [(0, 0, {'product_id': component_2.id, 'product_qty': 1})]
        })
        partner = self.env['res.partner'].create({'name': 'Testing Man'})
        so = self.env['sale.order'].create({
            'partner_id': partner.id,
        })
        # Create the order line
        self.env['sale.order.line'].create({
            'name': "Order line",
            'product_id': product_variant_ids[0].id,
            'order_id': so.id,
        })
        so.action_confirm()
        so.action_cancel()
        so.action_draft()
        with Form(so) as so_form:
            with so_form.order_line.edit(0) as order_line_change:
                # The actual test, there should be no traceback here
                order_line_change.product_id = product_variant_ids[1]
Exemplo n.º 59
0
    def test_out_refund_line_onchange_taxes_1(self):
        move_form = Form(self.invoice)
        with move_form.invoice_line_ids.edit(0) as line_form:
            line_form.price_unit = 1200
            line_form.tax_ids.add(self.tax_armageddon)
        move_form.save()

        child_tax_1 = self.tax_armageddon.children_tax_ids[0]
        child_tax_2 = self.tax_armageddon.children_tax_ids[1]

        self.assertInvoiceValues(
            self.invoice, [
                {
                    **self.product_line_vals_1,
                    'price_unit': 1200.0,
                    'price_subtotal': 1000.0,
                    'price_total': 1470.0,
                    'tax_ids': (self.tax_sale_a + self.tax_armageddon).ids,
                    'tax_exigible': False,
                },
                self.product_line_vals_2,
                self.tax_line_vals_1,
                self.tax_line_vals_2,
                {
                    'name': child_tax_1.name,
                    'product_id': False,
                    'account_id':
                    self.company_data['default_account_tax_sale'].id,
                    'partner_id': self.partner_a.id,
                    'product_uom_id': False,
                    'quantity': 1.0,
                    'discount': 0.0,
                    'price_unit': 80.0,
                    'price_subtotal': 80.0,
                    'price_total': 88.0,
                    'tax_ids': child_tax_2.ids,
                    'tax_line_id': child_tax_1.id,
                    'currency_id': self.company_data['currency'].id,
                    'amount_currency': 80.0,
                    'debit': 80.0,
                    'credit': 0.0,
                    'date_maturity': False,
                    'tax_exigible': False,
                },
                {
                    'name': child_tax_1.name,
                    'product_id': False,
                    'account_id':
                    self.company_data['default_account_revenue'].id,
                    'partner_id': self.partner_a.id,
                    'product_uom_id': False,
                    'quantity': 1.0,
                    'discount': 0.0,
                    'price_unit': 120.0,
                    'price_subtotal': 120.0,
                    'price_total': 132.0,
                    'tax_ids': child_tax_2.ids,
                    'tax_line_id': child_tax_1.id,
                    'currency_id': self.company_data['currency'].id,
                    'amount_currency': 120.0,
                    'debit': 120.0,
                    'credit': 0.0,
                    'date_maturity': False,
                    'tax_exigible': False,
                },
                {
                    'name': child_tax_2.name,
                    'product_id': False,
                    'account_id':
                    child_tax_2.cash_basis_transition_account_id.id,
                    'partner_id': self.partner_a.id,
                    'product_uom_id': False,
                    'quantity': 1.0,
                    'discount': 0.0,
                    'price_unit': 120.0,
                    'price_subtotal': 120.0,
                    'price_total': 120.0,
                    'tax_ids': [],
                    'tax_line_id': child_tax_2.id,
                    'currency_id': self.company_data['currency'].id,
                    'amount_currency': 120.0,
                    'debit': 120.0,
                    'credit': 0.0,
                    'date_maturity': False,
                    'tax_exigible': False,
                },
                {
                    **self.term_line_vals_1,
                    'price_unit': -1730.0,
                    'price_subtotal': -1730.0,
                    'price_total': -1730.0,
                    'amount_currency': -1730.0,
                    'credit': 1730.0,
                },
            ], {
                **self.move_vals,
                'amount_untaxed': 1200.0,
                'amount_tax': 530.0,
                'amount_total': 1730.0,
            })
Exemplo n.º 60
0
    def test_report_forecast_1_mo_count(self):
        """ Creates and configures a product who could be produce and could be a component.
        Plans some producing and consumming MO and check the report values.
        """
        # Create a variant attribute.
        product_chocolate = self.env['product.product'].create({
            'name': 'Chocolate',
            'type': 'consu',
        })
        product_chococake = self.env['product.product'].create({
            'name':
            'Choco Cake',
            'type':
            'product',
        })
        product_double_chococake = self.env['product.product'].create({
            'name':
            'Double Choco Cake',
            'type':
            'product',
        })

        # Creates two BOM: one creating a regular slime, one using regular slimes.
        bom_chococake = self.env['mrp.bom'].create({
            'product_id':
            product_chococake.id,
            'product_tmpl_id':
            product_chococake.product_tmpl_id.id,
            'product_uom_id':
            product_chococake.uom_id.id,
            'product_qty':
            1.0,
            'type':
            'normal',
            'bom_line_ids': [
                (0, 0, {
                    'product_id': product_chocolate.id,
                    'product_qty': 4
                }),
            ],
        })
        bom_double_chococake = self.env['mrp.bom'].create({
            'product_id':
            product_double_chococake.id,
            'product_tmpl_id':
            product_double_chococake.product_tmpl_id.id,
            'product_uom_id':
            product_double_chococake.uom_id.id,
            'product_qty':
            1.0,
            'type':
            'normal',
            'bom_line_ids': [
                (0, 0, {
                    'product_id': product_chococake.id,
                    'product_qty': 2
                }),
            ],
        })

        # Creates two MO: one for each BOM.
        mo_form = Form(self.env['mrp.production'])
        mo_form.product_id = product_chococake
        mo_form.bom_id = bom_chococake
        mo_form.product_qty = 10
        mo_1 = mo_form.save()
        mo_form = Form(self.env['mrp.production'])
        mo_form.product_id = product_double_chococake
        mo_form.bom_id = bom_double_chococake
        mo_form.product_qty = 2
        mo_2 = mo_form.save()

        report_values, docs, lines = self.get_report_forecast(
            product_template_ids=product_chococake.product_tmpl_id.ids)
        draft_picking_qty = docs['draft_picking_qty']
        draft_production_qty = docs['draft_production_qty']
        self.assertEqual(len(lines), 0, "Must have 0 line.")
        self.assertEqual(draft_picking_qty['in'], 0)
        self.assertEqual(draft_picking_qty['out'], 0)
        self.assertEqual(draft_production_qty['in'], 10)
        self.assertEqual(draft_production_qty['out'], 4)

        # Confirms the MO and checks the report lines.
        mo_1.action_confirm()
        mo_2.action_confirm()
        report_values, docs, lines = self.get_report_forecast(
            product_template_ids=product_chococake.product_tmpl_id.ids)
        draft_picking_qty = docs['draft_picking_qty']
        draft_production_qty = docs['draft_production_qty']
        self.assertEqual(len(lines), 2, "Must have two line.")
        line_1 = lines[0]
        line_2 = lines[1]
        self.assertEqual(line_1['document_in'].id, mo_1.id)
        self.assertEqual(line_1['quantity'], 4)
        self.assertEqual(line_1['document_out'].id, mo_2.id)
        self.assertEqual(line_2['document_in'].id, mo_1.id)
        self.assertEqual(line_2['quantity'], 6)
        self.assertEqual(line_2['document_out'], False)
        self.assertEqual(draft_picking_qty['in'], 0)
        self.assertEqual(draft_picking_qty['out'], 0)
        self.assertEqual(draft_production_qty['in'], 0)
        self.assertEqual(draft_production_qty['out'], 0)