Exemple #1
0
    def test_recurrence_week_day(self):
        form = Form(self.env['project.task'])

        form.name = 'test recurring task'
        form.project_id = self.project_recurring
        form.recurring_task = True
        form.repeat_unit = 'week'

        form.mon = False
        form.tue = False
        form.wed = False
        form.thu = False
        form.fri = False
        form.sat = False
        form.sun = False

        with self.assertRaises(ValidationError), self.cr.savepoint():
            form.save()
    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,
                'amount_currency': 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,
                'amount_currency': 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,
                'amount_currency': 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,
                'amount_currency': 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,
        })
    def test_in_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 = 799.99
        move_form.save()

        self.assertInvoiceValues(self.invoice, [
            {
                'name': 'add_invoice_line',
                'product_id': False,
                'account_id': self.cash_rounding_a.profit_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.0,
                'credit': 0.01,
                'date_maturity': False,
                'tax_exigible': True,
            },
            {
                **self.product_line_vals_1,
                'price_unit': 799.99,
                'price_subtotal': 799.99,
                'price_total': 919.99,
                'amount_currency': -799.99,
                'credit': 799.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': 799.99,
                'price_subtotal': 799.99,
                'price_total': 919.99,
                'amount_currency': -799.99,
                'credit': 799.99,
            },
            self.product_line_vals_2,
            self.tax_line_vals_1,
            self.tax_line_vals_2,
            {
                'name': '%s (rounding)' % self.tax_purchase_a.name,
                'product_id': False,
                'account_id': self.company_data['default_account_tax_purchase'].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_purchase_a.id,
                'currency_id': self.company_data['currency'].id,
                'amount_currency': 0.04,
                'debit': 0.04,
                'credit': 0.0,
                'date_maturity': False,
                'tax_exigible': True,
            },
            {
                **self.term_line_vals_1,
                'price_unit': -1127.95,
                'price_subtotal': -1127.95,
                'price_total': -1127.95,
                'amount_currency': 1127.95,
                'debit': 1127.95,
            },
        ], {
            **self.move_vals,
            'amount_untaxed': 959.99,
            'amount_tax': 167.96,
            'amount_total': 1127.95,
        })
    def test_subcontracting_account_backorder(self):
        """ This test uses tracked (serial and lot) component and tracked (serial) finished product
        The original subcontracting production order will be split into 4 backorders. This test
        ensure the extra cost asked from the subcontractor is added correctly on all the finished
        product valuation layer. Not only the first one. """
        todo_nb = 4
        self.comp2.tracking = 'lot'
        self.comp1.tracking = 'serial'
        self.comp2.standard_price = 100
        self.finished.tracking = 'serial'
        self.env.ref(
            'product.product_category_all').property_cost_method = 'fifo'

        # Create a receipt picking from the subcontractor
        picking_form = Form(self.env['stock.picking'])
        picking_form.picking_type_id = self.env.ref('stock.picking_type_in')
        picking_form.partner_id = self.subcontractor_partner1
        with picking_form.move_ids_without_package.new() as move:
            move.product_id = self.finished
            move.product_uom_qty = todo_nb
        picking_receipt = picking_form.save()
        # Mimic the extra cost on the po line
        picking_receipt.move_lines.price_unit = 50
        picking_receipt.action_confirm()

        # We should be able to call the 'record_components' button
        self.assertTrue(picking_receipt.display_action_record_components)

        # Check the created manufacturing order
        mo = self.env['mrp.production'].search([('bom_id', '=', self.bom.id)])
        wh = picking_receipt.picking_type_id.warehouse_id

        lot_comp2 = self.env['stock.production.lot'].create({
            'name':
            'lot_comp2',
            'product_id':
            self.comp2.id,
            'company_id':
            self.env.company.id,
        })
        serials_finished = []
        serials_comp1 = []
        for i in range(todo_nb):
            serials_finished.append(self.env['stock.production.lot'].create({
                'name':
                'serial_fin_%s' % i,
                'product_id':
                self.finished.id,
                'company_id':
                self.env.company.id,
            }))
            serials_comp1.append(self.env['stock.production.lot'].create({
                'name':
                'serials_comp1_%s' % i,
                'product_id':
                self.comp1.id,
                'company_id':
                self.env.company.id,
            }))

        for i in range(todo_nb):
            action = picking_receipt.action_record_components()
            mo = self.env['mrp.production'].browse(action['res_id'])
            mo_form = Form(mo.with_context(**action['context']),
                           view=action['view_id'])
            mo_form.lot_producing_id = serials_finished[i]
            with mo_form.move_line_raw_ids.edit(0) as ml:
                self.assertEqual(ml.product_id, self.comp1)
                ml.lot_id = serials_comp1[i]
            with mo_form.move_line_raw_ids.edit(1) as ml:
                self.assertEqual(ml.product_id, self.comp2)
                ml.lot_id = lot_comp2
            mo = mo_form.save()
            mo.subcontracting_record_component()

        # We should not be able to call the 'record_components' button

        picking_receipt.button_validate()

        f_layers = self.finished.stock_valuation_layer_ids
        self.assertEqual(len(f_layers), 4)
        for layer in f_layers:
            self.assertEqual(layer.value, 100 + 50)
    def test_in_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 800.
            # We set quantity = 4, discount = 50%, price_unit = 400. The debit/credit fields don't change because (4 * 400) * 0.5 = 800.
            line_form.quantity = 4
            line_form.discount = 50
            line_form.price_unit = 400
        move_form.save()

        self.assertInvoiceValues(self.invoice, [
            {
                **self.product_line_vals_1,
                'quantity': 4,
                'discount': 50.0,
                'price_unit': 400.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 = 800
        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,
                'credit': 0.0,
            },
            self.product_line_vals_2,
            {
                **self.tax_line_vals_1,
                'price_unit': 24.0,
                'price_subtotal': 24.0,
                'price_total': 24.0,
                'amount_currency': -24.0,
                'credit': 24.0,
            },
            self.tax_line_vals_2,
            {
                **self.term_line_vals_1,
                'price_unit': -208.0,
                'price_subtotal': -208.0,
                'price_total': -208.0,
                'amount_currency': 208.0,
                'debit': 208.0,
            },
        ], {
            **self.move_vals,
            'amount_untaxed': 160.0,
            'amount_tax': 48.0,
            'amount_total': 208.0,
        })
    def test_intercom_lot_pull(self):
        """Use warehouse of comany a to resupply warehouse of company b. Check
        pull rule works correctly in two companies and moves are unchained from
        inter-company transit location."""
        customer_location = self.env.ref('stock.stock_location_customers')
        supplier_location = self.env.ref('stock.stock_location_suppliers')
        intercom_location = self.env.ref('stock.stock_location_inter_wh')
        intercom_location.write({'active': True})
        partner = self.env['res.partner'].create({'name': 'Deco Addict'})
        self.warehouse_a.resupply_wh_ids = [(6, 0, [self.warehouse_b.id])]
        resupply_route = self.env['stock.location.route'].search([
            ('supplier_wh_id', '=', self.warehouse_b.id),
            ('supplied_wh_id', '=', self.warehouse_a.id)
        ])
        self.assertTrue(resupply_route, "Resupply route not found")

        product_lot = self.env['product.product'].create({
            'type':
            'product',
            'tracking':
            'lot',
            'name':
            'product lot',
            'route_ids': [(4, resupply_route.id),
                          (4, self.env.ref('stock.route_warehouse0_mto').id)],
        })

        move_sup_to_whb = self.env['stock.move'].create({
            'company_id':
            self.company_b.id,
            'name':
            'from_supplier_to_whb',
            'location_id':
            supplier_location.id,
            'location_dest_id':
            self.warehouse_b.lot_stock_id.id,
            'product_id':
            product_lot.id,
            'product_uom':
            product_lot.uom_id.id,
            'product_uom_qty':
            1.0,
            'picking_type_id':
            self.warehouse_b.in_type_id.id,
        })
        move_sup_to_whb._action_confirm()
        move_line_1 = move_sup_to_whb.move_line_ids[0]
        move_line_1.lot_name = 'lot b'
        move_line_1.qty_done = 1.0
        move_sup_to_whb._action_done()
        lot_b = move_line_1.lot_id

        picking_out = self.env['stock.picking'].create({
            'company_id':
            self.company_a.id,
            'partner_id':
            partner.id,
            'picking_type_id':
            self.warehouse_a.out_type_id.id,
            'location_id':
            self.stock_location_a.id,
            'location_dest_id':
            customer_location.id,
        })
        move_wha_to_cus = self.env['stock.move'].create({
            'name':
            "WH_A to Customer",
            'product_id':
            product_lot.id,
            'product_uom_qty':
            1,
            'product_uom':
            product_lot.uom_id.id,
            'picking_id':
            picking_out.id,
            'location_id':
            self.stock_location_a.id,
            'location_dest_id':
            customer_location.id,
            'warehouse_id':
            self.warehouse_a.id,
            'procure_method':
            'make_to_order',
            'company_id':
            self.company_a.id,
        })
        picking_out.action_confirm()

        move_whb_to_transit = self.env['stock.move'].search([
            ('location_id', '=', self.stock_location_b.id),
            ('product_id', '=', product_lot.id)
        ])
        move_transit_to_wha = self.env['stock.move'].search([
            ('location_id', '=', intercom_location.id),
            ('product_id', '=', product_lot.id)
        ])
        self.assertTrue(move_whb_to_transit, "No move created by pull rule")
        self.assertTrue(move_transit_to_wha, "No move created by pull rule")
        self.assertTrue(move_wha_to_cus in move_transit_to_wha.move_dest_ids,
                        "Moves are not chained")
        self.assertFalse(
            move_transit_to_wha in move_whb_to_transit.move_dest_ids,
            "Chained move created in transit location")
        self.assertEqual(move_wha_to_cus.state, "waiting")
        self.assertEqual(move_transit_to_wha.state, "waiting")
        self.assertEqual(move_whb_to_transit.state, "confirmed")

        (move_wha_to_cus + move_whb_to_transit +
         move_transit_to_wha).picking_id.action_assign()
        self.assertEqual(move_wha_to_cus.state, "waiting")
        self.assertEqual(move_transit_to_wha.state, "assigned")
        self.assertEqual(move_whb_to_transit.state, "assigned")

        res_dict = move_whb_to_transit.picking_id.button_validate()
        self.assertEqual(res_dict.get('res_model'), 'stock.immediate.transfer')
        wizard = Form(self.env[res_dict['res_model']].with_context(
            res_dict['context'])).save()
        wizard.process()
        self.assertEqual(
            self.env['stock.quant']._get_available_quantity(
                product_lot, intercom_location, lot_b), 1.0)
        with self.assertRaises(UserError):
            move_transit_to_wha.picking_id.button_validate()

        move_line_2 = move_transit_to_wha.move_line_ids[0]
        move_line_2.lot_name = 'lot a'
        move_line_2.qty_done = 1.0
        move_transit_to_wha._action_done()
        lot_a = move_line_2.lot_id

        move_wha_to_cus._action_assign()
        self.assertEqual(move_wha_to_cus.state, "assigned")
        res_dict = move_wha_to_cus.picking_id.button_validate()
        self.assertEqual(res_dict.get('res_model'), 'stock.immediate.transfer')
        wizard = Form(self.env[res_dict['res_model']].with_context(
            res_dict['context'])).save()
        wizard.process()
        self.assertEqual(
            self.env['stock.quant']._get_available_quantity(
                product_lot, customer_location, lot_a), 1.0)

        self.assertEqual(lot_a.company_id, self.company_a)
        self.assertEqual(lot_a.name, 'lot a')
        self.assertEqual(lot_b.company_id, self.company_b)
        self.assertEqual(lot_b.name, 'lot b')
    def _import_fattura_pa(self, tree, invoice):
        """ Decodes a fattura_pa invoice into an invoice.

        :param tree:    the fattura_pa tree to decode.
        :param invoice: the invoice to update or an empty recordset.
        :returns:       the invoice where the fattura_pa data was imported.
        """
        invoices = self.env['account.move']
        first_run = True

        # 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'):
            if not first_run or not invoice:
                # make sure all the iterations create a new invoice record (except the first which could have already created one)
                invoice = self.env['account.move']
            first_run = False

            # Type must be present in the context to get the right behavior of the _default_journal method (account.move).
            # journal_id must be present in the context to get the right behavior of the _default_account method (account.move.line).
            elements = tree.xpath('//CessionarioCommittente//IdCodice')
            company = elements and self.env['res.company'].search(
                [('vat', 'ilike', elements[0].text)], limit=1)
            if not company:
                elements = tree.xpath(
                    '//CessionarioCommittente//CodiceFiscale')
                company = elements and self.env['res.company'].search(
                    [('l10n_it_codice_fiscale', 'ilike', elements[0].text)],
                    limit=1)
                if not company:
                    # Only invoices with a correct VAT or Codice Fiscale can be imported
                    _logger.warning(
                        'No company found with VAT or Codice Fiscale like %r.',
                        elements[0].text)
                    continue

            # Refund type.
            # TD01 == invoice
            # TD02 == advance/down payment on invoice
            # TD03 == advance/down payment on fee
            # TD04 == credit note
            # TD05 == debit note
            # TD06 == fee
            # For unsupported document types, just assume in_invoice, and log that the type is unsupported
            elements = tree.xpath('//DatiGeneraliDocumento/TipoDocumento')
            move_type = 'in_invoice'
            if elements and elements[0].text and elements[0].text == 'TD04':
                move_type = 'in_refund'
            elif elements and elements[0].text and elements[0].text != 'TD01':
                _logger.info(
                    'Document type not managed: %s. Invoice type is set by default.',
                    elements[0].text)

            # Setup the context for the Invoice Form
            invoice_ctx = invoice.with_company(company) \
                                 .with_context(default_move_type=move_type)

            # move could be a single record (editing) or be empty (new).
            with Form(invoice_ctx) as invoice_form:
                message_to_log = []

                # Partner (first step to avoid warning 'Warning! You must first select a partner.'). <1.2>
                elements = tree.xpath('//CedentePrestatore//IdCodice')
                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')
                    if elements:
                        codice = elements[0].text
                        domains = [[('l10n_it_codice_fiscale', '=', codice)]]
                        if re.match(r'^[0-9]{11}$', codice):
                            domains.append([('l10n_it_codice_fiscale', '=',
                                             'IT' + codice)])
                        elif re.match(r'^IT[0-9]{11}$', codice):
                            domains.append([
                                ('l10n_it_codice_fiscale', '=',
                                 self.env['res.partner'].
                                 _l10n_it_normalize_codice_fiscale(codice))
                            ])
                        partner = elements and self.env['res.partner'].search(
                            AND([
                                OR(domains),
                                OR([[('company_id', '=', company.id)],
                                    [('company_id', '=', False)]])
                            ]),
                            limit=1)
                if not partner:
                    elements = tree.xpath('//DatiTrasmissione//Email')
                    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:"
                          ),
                        invoice._compose_info_message(tree,
                                                      './/CedentePrestatore')))

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

                elements = body_tree.xpath('.//DatiGeneraliDocumento//Numero')
                if elements:
                    invoice_form.ref = elements[0].text

                # Currency. <2.1.1.2>
                elements = body_tree.xpath('.//DatiGeneraliDocumento/Divisa')
                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.company.currency_id and currency.active:
                        invoice_form.currency_id = currency

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

                #  Dati Bollo. <2.1.1.6>
                elements = body_tree.xpath(
                    './/DatiGeneraliDocumento/DatiBollo/ImportoBollo')
                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')
                total_discount_amount = 0.0
                if discount_elements:
                    for discount_element in discount_elements:
                        discount_line = discount_element.xpath('.//Tipo')
                        discount_sign = -1
                        if discount_line and discount_line[0].text == 'SC':
                            discount_sign = 1
                        discount_percentage = discount_element.xpath(
                            './/Percentuale')
                        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')
                        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')
                for element in elements:
                    invoice_form.narration = '%s%s\n' % (invoice_form.narration
                                                         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)
                    if elements:
                        for element in elements:
                            message_to_log.append(
                                "%s %s<br/>%s" %
                                (document_type, _("from XML file:"),
                                 invoice._compose_info_message(element, '.')))

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

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

                # Total amount. <2.4.2.6>
                elements = body_tree.xpath('.//ImportoPagamento')
                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>
                if invoice_form.move_type not in ('out_invoice', 'in_refund'):
                    elements = body_tree.xpath(
                        './/DatiPagamento/DettaglioPagamento/IBAN')
                    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:"
                                  ),
                                invoice._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')
                    if elements:
                        message_to_log.append("%s<br/>%s" % (
                            _("Bank account not found, useful informations from XML file:"
                              ),
                            invoice._compose_info_message(
                                body_tree, './/DatiPagamento')))

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

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

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

                            elements_code = element.xpath('.//CodiceArticolo')
                            if elements_code:
                                for element_code in elements_code:
                                    type_code = element_code.xpath(
                                        './/CodiceTipo')[0]
                                    code = element_code.xpath(
                                        './/CodiceValore')[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')[0]
                                        product = self.env[
                                            'product.product'].search(
                                                [('default_code', '=',
                                                  code.text)],
                                                limit=1)
                                        if product:
                                            invoice_line_form.product_id = product
                                            break

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

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

                            # Taxes
                            tax_element = element.xpath('.//AliquotaIVA')
                            natura_element = element.xpath('.//Natura')
                            invoice_line_form.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.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')
                            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')
                                    discount_sign = -1
                                    if discount_line and discount_line[
                                            0].text == 'SC':
                                        discount_sign = 1
                                    discount_percentage = line_element.xpath(
                                        './/Percentuale')
                                    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')
                                    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: %s',
                                            invoice_line_form.name)
                                    else:
                                        discount["name"] = _(
                                            'EXTRA CHARGE: %s',
                                            invoice_line_form.name)
                                    discount["amount"] = total_discount_amount
                                    discount["tax"] = []
                                    for tax in invoice_line_form.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.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')
            if elements:
                for element in elements:
                    name_attachment = element.xpath(
                        './/NomeAttachment')[0].text
                    attachment_64 = str.encode(
                        element.xpath('.//Attachment')[0].text)
                    attachment_64 = self.env['ir.attachment'].create({
                        'name':
                        name_attachment,
                        'datas':
                        attachment_64,
                        'type':
                        'binary',
                    })

                    # default_res_id is had to context to avoid facturx to import his content
                    # no_new_invoice to prevent from looping on the message_post that would create a new invoice without it
                    new_invoice.with_context(
                        no_new_invoice=True,
                        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)

            invoices += new_invoice

        return invoices
Exemple #8
0
    def _create_other_currency_config(cls):
        (cls.other_currency.rate_ids | cls.company_currency.rate_ids).unlink()
        cls.env['res.currency.rate'].create({
            'rate': 0.5,
            'currency_id': cls.other_currency.id,
            'name': datetime.today().date(),
        })
        other_cash_journal = cls.env['account.journal'].create({
            'name':
            'Cash Other',
            'type':
            'cash',
            'company_id':
            cls.company.id,
            'code':
            'CSHO',
            'sequence':
            10,
            'currency_id':
            cls.other_currency.id
        })
        other_invoice_journal = cls.env['account.journal'].create({
            'name':
            'Customer Invoice Other',
            'type':
            'sale',
            'company_id':
            cls.company.id,
            'code':
            'INVO',
            'sequence':
            11,
            'currency_id':
            cls.other_currency.id
        })
        other_sales_journal = cls.env['account.journal'].create({
            'name':
            'PoS Sale Other',
            'type':
            'sale',
            'code':
            'POSO',
            'company_id':
            cls.company.id,
            'sequence':
            12,
            'currency_id':
            cls.other_currency.id
        })
        other_pricelist = cls.env['product.pricelist'].create({
            'name':
            'Public Pricelist Other',
            'currency_id':
            cls.other_currency.id,
        })
        other_cash_payment_method = cls.env['pos.payment.method'].create({
            'name':
            'Cash Other',
            'receivable_account_id':
            cls.pos_receivable_account.id,
            'is_cash_count':
            True,
            'cash_journal_id':
            other_cash_journal.id,
        })
        other_bank_payment_method = cls.env['pos.payment.method'].create({
            'name':
            'Bank Other',
            'receivable_account_id':
            cls.pos_receivable_account.id,
        })

        new_config = Form(cls.env['pos.config'])
        new_config.name = 'Shop Other'
        new_config.invoice_journal_id = other_invoice_journal
        new_config.journal_id = other_sales_journal
        new_config.use_pricelist = True
        new_config.available_pricelist_ids.clear()
        new_config.available_pricelist_ids.add(other_pricelist)
        new_config.pricelist_id = other_pricelist
        new_config.payment_method_ids.clear()
        new_config.payment_method_ids.add(other_cash_payment_method)
        new_config.payment_method_ids.add(other_bank_payment_method)
        config = new_config.save()
        return config
Exemple #9
0
    def _create_taxes(cls):
        """ Create taxes

        tax7: 7%, excluded in product price
        tax10: 10%, included in product price
        tax21: 21%, included in product price
        """
        def create_tag(name):
            return cls.env['account.account.tag'].create({
                'name':
                name,
                'applicability':
                'taxes',
                'country_id':
                cls.env.company.country_id.id
            })

        cls.tax_tag_invoice_base = create_tag('Invoice Base tag')
        cls.tax_tag_invoice_tax = create_tag('Invoice Tax tag')
        cls.tax_tag_refund_base = create_tag('Refund Base tag')
        cls.tax_tag_refund_tax = create_tag('Refund Tax tag')

        def create_tax(percentage, price_include=False):
            return cls.env['account.tax'].create({
                'name':
                f'Tax {percentage}%',
                'amount':
                percentage,
                'price_include':
                price_include,
                'include_base_amount':
                False,
                'invoice_repartition_line_ids': [
                    (0, 0, {
                        'factor_percent': 100,
                        'repartition_type': 'base',
                        'tag_ids': [(6, 0, cls.tax_tag_invoice_base.ids)],
                    }),
                    (0, 0, {
                        'factor_percent': 100,
                        'repartition_type': 'tax',
                        'account_id': cls.tax_received_account.id,
                        'tag_ids': [(6, 0, cls.tax_tag_invoice_tax.ids)],
                    }),
                ],
                'refund_repartition_line_ids': [
                    (0, 0, {
                        'factor_percent': 100,
                        'repartition_type': 'base',
                        'tag_ids': [(6, 0, cls.tax_tag_refund_base.ids)],
                    }),
                    (0, 0, {
                        'factor_percent': 100,
                        'repartition_type': 'tax',
                        'account_id': cls.tax_received_account.id,
                        'tag_ids': [(6, 0, cls.tax_tag_refund_tax.ids)],
                    }),
                ],
            })

        tax7 = create_tax(7, price_include=False)
        tax10 = create_tax(10, price_include=True)
        tax21 = create_tax(21, price_include=True)

        tax_group_7_10 = tax7.copy()
        with Form(tax_group_7_10) as tax:
            tax.name = 'Tax 7+10%'
            tax.amount_type = 'group'
            tax.children_tax_ids.add(tax7)
            tax.children_tax_ids.add(tax10)

        return {
            'tax7': tax7,
            'tax10': tax10,
            'tax21': tax21,
            'tax_group_7_10': tax_group_7_10
        }
Exemple #10
0
    def test_crm_lead_partner_sync_email_phone(self):
        """ Specifically test synchronize between a lead and its partner about
        phone and email fields. Phone especially has some corner cases due to
        automatic formatting (notably with onchange in form view). """
        lead, partner = self.lead_1.with_user(self.env.user), self.contact_2
        lead_form = Form(lead)

        # reset partner phone to a local number and prepare formatted / sanitized values
        partner_phone, partner_mobile = self.test_phone_data[
            2], self.test_phone_data[1]
        partner_phone_formatted = phone_format(partner_phone, 'US', '1')
        partner_phone_sanitized = phone_format(partner_phone,
                                               'US',
                                               '1',
                                               force_format='E164')
        partner_mobile_formatted = phone_format(partner_mobile, 'US', '1')
        partner_mobile_sanitized = phone_format(partner_mobile,
                                                'US',
                                                '1',
                                                force_format='E164')
        partner_email, partner_email_normalized = self.test_email_data[
            2], self.test_email_data_normalized[2]
        self.assertEqual(partner_phone_formatted, '+1 202-555-0888')
        self.assertEqual(partner_phone_sanitized,
                         self.test_phone_data_sanitized[2])
        self.assertEqual(partner_mobile_formatted, '+1 202-555-0999')
        self.assertEqual(partner_mobile_sanitized,
                         self.test_phone_data_sanitized[1])
        # ensure initial data
        self.assertEqual(partner.phone, partner_phone)
        self.assertEqual(partner.mobile, partner_mobile)
        self.assertEqual(partner.email, partner_email)

        # LEAD/PARTNER SYNC: email and phone are propagated to lead
        # as well as mobile (who does not trigger the reverse sync)
        lead_form.partner_id = partner
        self.assertEqual(lead_form.email_from, partner_email)
        self.assertEqual(lead_form.phone, partner_phone_formatted,
                         'Lead: form automatically formats numbers')
        self.assertEqual(lead_form.mobile, partner_mobile_formatted,
                         'Lead: form automatically formats numbers')
        self.assertFalse(lead_form.ribbon_message)

        lead_form.save()
        self.assertEqual(partner.phone, partner_phone,
                         'Lead / Partner: partner values sent to lead')
        self.assertEqual(lead.email_from, partner_email,
                         'Lead / Partner: partner values sent to lead')
        self.assertEqual(
            lead.email_normalized, partner_email_normalized,
            'Lead / Partner: equal emails should lead to equal normalized emails'
        )
        self.assertEqual(
            lead.phone, partner_phone_formatted,
            'Lead / Partner: partner values (formatted) sent to lead')
        self.assertEqual(
            lead.mobile, partner_mobile_formatted,
            'Lead / Partner: partner values (formatted) sent to lead')
        self.assertEqual(lead.phone_sanitized, partner_mobile_sanitized,
                         'Lead: phone_sanitized computed field on mobile')

        # for email_from, if only formatting differs, warning ribbon should
        # not appear and email on partner should not be updated
        lead_form.email_from = '"Hermes Conrad" <%s>' % partner_email_normalized
        self.assertFalse(lead_form.ribbon_message)
        lead_form.save()
        self.assertEqual(lead_form.partner_id.email, partner_email)

        # LEAD/PARTNER SYNC: lead updates partner
        new_email = '"John Zoidberg" <*****@*****.**>'
        new_email_normalized = '*****@*****.**'
        lead_form.email_from = new_email
        self.assertIn('the customer email will', lead_form.ribbon_message)
        new_phone = '+1 202 555 7799'
        new_phone_formatted = phone_format(new_phone, 'US', '1')
        lead_form.phone = new_phone
        self.assertEqual(lead_form.phone, new_phone_formatted)
        self.assertIn('the customer email and phone number will',
                      lead_form.ribbon_message)

        lead_form.save()
        self.assertEqual(partner.email, new_email)
        self.assertEqual(partner.email_normalized, new_email_normalized)
        self.assertEqual(partner.phone, new_phone_formatted)

        # LEAD/PARTNER SYNC: mobile does not update partner
        new_mobile = '+1 202 555 6543'
        new_mobile_formatted = phone_format(new_mobile, 'US', '1')
        lead_form.mobile = new_mobile
        lead_form.save()
        self.assertEqual(lead.mobile, new_mobile_formatted)
        self.assertEqual(partner.mobile, partner_mobile)

        # LEAD/PARTNER SYNC: reseting lead values also resets partner for email
        # and phone, but not for mobile
        lead_form.email_from, lead_form.phone, lead.mobile = False, False, False
        self.assertIn('the customer email and phone number will',
                      lead_form.ribbon_message)
        lead_form.save()
        self.assertFalse(partner.email)
        self.assertFalse(partner.email_normalized)
        self.assertFalse(partner.phone)
        self.assertFalse(lead.phone)
        self.assertFalse(lead.mobile)
        self.assertFalse(lead.phone_sanitized)
        self.assertEqual(partner.mobile, partner_mobile)
        self.assertEqual(partner.phone_sanitized, partner_mobile_sanitized,
                         'Partner sanitized should be computed on mobile')
Exemple #11
0
    def test_crm_lead_partner_sync_email_phone_corner_cases(self):
        """ Test corner cases of email and phone sync (False versus '', formatting
        differences, wrong input, ...) """
        test_email = '*****@*****.**'
        lead = self.lead_1.with_user(self.env.user)
        contact = self.env['res.partner'].create({
            'name': 'NoContact Partner',
            'phone': '',
            'email': '',
            'mobile': '',
        })

        lead_form = Form(lead)
        self.assertEqual(lead_form.email_from, test_email)
        self.assertFalse(lead_form.ribbon_message)

        # email: False versus empty string
        lead_form.partner_id = contact
        self.assertIn('the customer email', lead_form.ribbon_message)
        lead_form.email_from = ''
        self.assertFalse(lead_form.ribbon_message)
        lead_form.email_from = False
        self.assertFalse(lead_form.ribbon_message)

        # phone: False versus empty string
        lead_form.phone = '+1 202-555-0888'
        self.assertIn('the customer phone', lead_form.ribbon_message)
        lead_form.phone = ''
        self.assertFalse(lead_form.ribbon_message)
        lead_form.phone = False
        self.assertFalse(lead_form.ribbon_message)

        # email/phone: formatting should not trigger ribbon
        lead.write({
            'email_from': '"My Name" <%s>' % test_email,
            'phone': '+1 202-555-0888',
        })
        contact.write({
            'email': '"My Name" <%s>' % test_email,
            'phone': '+1 202-555-0888',
        })

        lead_form = Form(lead)
        self.assertFalse(lead_form.ribbon_message)
        lead_form.partner_id = contact
        self.assertFalse(lead_form.ribbon_message)
        lead_form.email_from = '"Another Name" <%s>' % test_email  # same email normalized
        self.assertFalse(lead_form.ribbon_message,
                         'Formatting-only change should not trigger write')
        lead_form.phone = '2025550888'  # same number but another format
        self.assertFalse(lead_form.ribbon_message,
                         'Formatting-only change should not trigger write')

        # wrong value are also propagated
        lead_form.phone = '666 789456789456789456'
        self.assertIn('the customer phone', lead_form.ribbon_message)
    def test_included_tax(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 an included 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.

        included tax = 20%

        Name                   | Debit     | Credit    | Tax_ids       | Tax_line_id's name
        -----------------------|-----------|-----------|---------------|-------------------
        debit_line_1           | 1000      |           | tax           |
        included_tax_line      | 200       |           |               | included_tax_line
        credit_line_1          |           | 1200      |               |
        '''

        self.included_percent_tax = self.env['account.tax'].create({
            'name':
            'included_tax_line',
            'amount_type':
            'percent',
            'amount':
            20,
            'price_include':
            True,
            'include_base_amount':
            False,
        })
        self.account = self.company_data['default_account_revenue']

        move_form = Form(
            self.env['account.move'].with_context(default_move_type='entry'))

        # 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.included_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 = 1200

        move = move_form.save()

        self.assertRecordValues(move.line_ids, [
            {
                'name': 'debit_line_1',
                'debit': 1000.0,
                'credit': 0.0,
                'tax_ids': [self.included_percent_tax.id],
                'tax_line_id': False
            },
            {
                'name': 'included_tax_line',
                'debit': 200.0,
                'credit': 0.0,
                'tax_ids': [],
                'tax_line_id': self.included_percent_tax.id
            },
            {
                'name': 'credit_line_1',
                'debit': 0.0,
                'credit': 1200.0,
                'tax_ids': [],
                'tax_line_id': False
            },
        ])
    def test_misc_move_onchange(self):
        ''' Test the behavior on onchanges for account.move having 'entry' as type. '''

        move_form = Form(self.env['account.move'])
        # Rate 1:3
        move_form.date = fields.Date.from_string('2016-01-01')

        # New line that should get 400.0 as debit.
        with move_form.line_ids.new() as line_form:
            line_form.name = 'debit_line'
            line_form.account_id = self.company_data['default_account_revenue']
            line_form.currency_id = self.currency_data['currency']
            line_form.amount_currency = 1200.0

        # New line that should get 400.0 as credit.
        with move_form.line_ids.new() as line_form:
            line_form.name = 'credit_line'
            line_form.account_id = self.company_data['default_account_revenue']
            line_form.currency_id = self.currency_data['currency']
            line_form.amount_currency = -1200.0
        move = move_form.save()

        self.assertRecordValues(
            move.line_ids.sorted('debit'),
            [
                {
                    'currency_id': self.currency_data['currency'].id,
                    'amount_currency': -1200.0,
                    'debit': 0.0,
                    'credit': 400.0,
                },
                {
                    'currency_id': self.currency_data['currency'].id,
                    'amount_currency': 1200.0,
                    'debit': 400.0,
                    'credit': 0.0,
                },
            ],
        )

        # === Change the date to change the currency conversion's rate ===

        with Form(move) as move_form:
            move_form.date = fields.Date.from_string('2017-01-01')

        self.assertRecordValues(
            move.line_ids.sorted('debit'),
            [
                {
                    'currency_id': self.currency_data['currency'].id,
                    'amount_currency': -1200.0,
                    'debit': 0.0,
                    'credit': 600.0,
                },
                {
                    'currency_id': self.currency_data['currency'].id,
                    'amount_currency': 1200.0,
                    'debit': 600.0,
                    'credit': 0.0,
                },
            ],
        )
Exemple #14
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)
    def test_01_product_level_delay(self):
        """ To check schedule dates of multiple purchase order line of the same purchase order,
            we create two procurements for the two different product with same vendor
            and different Delivery Lead Time."""

        company = self.env.ref('base.main_company')
        company.write({'po_lead': 0.00})

        # Make procurement request from product_1's form view, create procurement and check it's state
        date_planned1 = fields.Datetime.to_string(fields.datetime.now() +
                                                  timedelta(days=10))
        self._create_make_procurement(self.product_1,
                                      10.00,
                                      date_planned=date_planned1)
        purchase1 = self.env['purchase.order.line'].search(
            [('product_id', '=', self.product_1.id)], limit=1).order_id

        # Make procurement request from product_2's form view, create procurement and check it's state
        date_planned2 = fields.Datetime.to_string(fields.datetime.now() +
                                                  timedelta(days=10))
        self._create_make_procurement(self.product_2,
                                      5.00,
                                      date_planned=date_planned2)
        purchase2 = self.env['purchase.order.line'].search(
            [('product_id', '=', self.product_2.id)], limit=1).order_id

        # Check purchase order is same or not
        self.assertEqual(
            purchase1, purchase2,
            'Purchase orders should be same for the two different product with same vendor.'
        )

        # Confirm purchase order
        purchase1.button_confirm()

        # Check order date of purchase order
        order_line_pro_1 = purchase2.order_line.filtered(
            lambda r: r.product_id == self.product_1)
        order_line_pro_2 = purchase2.order_line.filtered(
            lambda r: r.product_id == self.product_2)
        order_date = fields.Datetime.from_string(date_planned1) - timedelta(
            days=self.product_1.seller_ids.delay)
        self.assertEqual(
            purchase2.date_order, order_date,
            'Order date should be equal to: Date of the procurement order - Delivery Lead Time.'
        )

        # Check scheduled date of purchase order line for product_1
        schedule_date_1 = datetime.combine(
            order_date + timedelta(days=self.product_1.seller_ids.delay),
            time.min).replace(tzinfo=None, hour=12)
        self.assertEqual(
            order_line_pro_1.date_planned, schedule_date_1,
            'Schedule date of purchase order line for product_1 should be equal to: Order date of purchase order + Delivery Lead Time of product_1.'
        )

        # Check scheduled date of purchase order line for product_2
        schedule_date_2 = datetime.combine(
            order_date + timedelta(days=self.product_2.seller_ids.delay),
            time.min).replace(tzinfo=None, hour=12)
        self.assertEqual(
            order_line_pro_2.date_planned, schedule_date_2,
            'Schedule date of purchase order line for product_2 should be equal to: Order date of purchase order + Delivery Lead Time of product_2.'
        )

        # Check scheduled date of purchase order
        po_schedule_date = min(schedule_date_1, schedule_date_2)
        self.assertEqual(
            purchase2.order_line[1].date_planned, po_schedule_date,
            'Schedule date of purchase order should be minimum of schedule dates of purchase order lines.'
        )

        # Check the picking created or not
        self.assertTrue(purchase2.picking_ids, "Picking should be created.")

        # Check scheduled date of In Type shipment
        self.assertEqual(
            purchase2.picking_ids.scheduled_date, po_schedule_date,
            'Schedule date of In type shipment should be same as schedule date of purchase order.'
        )

        # Check deadline of pickings
        self.assertEqual(
            purchase2.picking_ids.date_deadline, purchase2.date_planned,
            "Deadline of pickings should be equals to the receipt date of purchase"
        )
        purchase_form = Form(purchase2)
        purchase_form.date_planned = purchase2.date_planned + timedelta(days=2)
        purchase_form.save()
        self.assertEqual(purchase2.picking_ids.date_deadline,
                         purchase2.date_planned,
                         "Deadline of pickings should be propagate")
    def test_01_sale_mrp_kit_qty_delivered(self):
        """ Test that the quantities delivered are correct when
        a kit with subkits is ordered with multiple backorders and returns
        """

        # 'kit_parent' structure:
        # ---------------------------
        #
        # kit_parent --|- kit_2 x2 --|- component_d x1
        #              |             |- kit_1 x2 -------|- component_a   x2
        #              |                                |- component_b   x1
        #              |                                |- component_c   x3
        #              |
        #              |- kit_3 x1 --|- component_f x1
        #              |             |- component_g x2
        #              |
        #              |- component_e x1

        # Creation of a sale order for x7 kit_parent
        partner = self.env['res.partner'].create({'name': 'My Test Partner'})
        f = Form(self.env['purchase.order'])
        f.partner_id = partner
        with f.order_line.new() as line:
            line.product_id = self.kit_parent
            line.product_qty = 7.0
            line.price_unit = 10

        po = f.save()
        po.button_confirm()

        # Check picking creation, its move lines should concern
        # only components. Also checks that the quantities are corresponding
        # to the PO
        self.assertEqual(len(po.picking_ids), 1)
        order_line = po.order_line[0]
        picking_original = po.picking_ids[0]
        move_lines = picking_original.move_lines
        products = move_lines.mapped('product_id')
        kits = [self.kit_parent, self.kit_3, self.kit_2, self.kit_1]
        components = [self.component_a, self.component_b, self.component_c, self.component_d, self.component_e,
                      self.component_f, self.component_g]
        expected_quantities = {
            self.component_a: 56.0,
            self.component_b: 28.0,
            self.component_c: 84.0,
            self.component_d: 14.0,
            self.component_e: 7.0,
            self.component_f: 14.0,
            self.component_g: 28.0
        }

        self.assertEqual(len(move_lines), 7)
        self.assertTrue(not any(kit in products for kit in kits))
        self.assertTrue(all(component in products for component in components))
        self._assert_quantities(move_lines, expected_quantities)

        # Process only 7 units of each component
        qty_to_process = 7
        move_lines.write({'quantity_done': qty_to_process})

        # Create a backorder for the missing componenents
        pick = po.picking_ids[0]
        res = pick.button_validate()
        Form(self.env[res['res_model']].with_context(res['context'])).save().process()

        # Check that a backorded is created
        self.assertEqual(len(po.picking_ids), 2)
        backorder_1 = po.picking_ids - picking_original
        self.assertEqual(backorder_1.backorder_id.id, picking_original.id)

        # Even if some components are received completely,
        # no KitParent should be received
        self.assertEqual(order_line.qty_received, 0)

        # Process just enough components to make 1 kit_parent
        qty_to_process = {
            self.component_a: 1,
            self.component_c: 5,
        }
        self._process_quantities(backorder_1.move_lines, qty_to_process)

        # Create a backorder for the missing componenents
        res = backorder_1.button_validate()
        Form(self.env[res['res_model']].with_context(res['context'])).save().process()

        # Only 1 kit_parent should be received at this point
        self.assertEqual(order_line.qty_received, 1)

        # Check that the second backorder is created
        self.assertEqual(len(po.picking_ids), 3)
        backorder_2 = po.picking_ids - picking_original - backorder_1
        self.assertEqual(backorder_2.backorder_id.id, backorder_1.id)

        # Set the components quantities that backorder_2 should have
        expected_quantities = {
            self.component_a: 48,
            self.component_b: 21,
            self.component_c: 72,
            self.component_d: 7,
            self.component_f: 7,
            self.component_g: 21
        }

        # Check that the computed quantities are matching the theorical ones.
        # Since component_e was totally processed, this componenent shouldn't be
        # present in backorder_2
        self.assertEqual(len(backorder_2.move_lines), 6)
        move_comp_e = backorder_2.move_lines.filtered(lambda m: m.product_id.id == self.component_e.id)
        self.assertFalse(move_comp_e)
        self._assert_quantities(backorder_2.move_lines, expected_quantities)

        # Process enough components to make x3 kit_parents
        qty_to_process = {
            self.component_a: 16,
            self.component_b: 5,
            self.component_c: 24,
            self.component_g: 5
        }
        self._process_quantities(backorder_2.move_lines, qty_to_process)

        # Create a backorder for the missing componenents
        res = backorder_2.button_validate()
        Form(self.env[res['res_model']].with_context(res['context'])).save().process()

        # Check that x3 kit_parents are indeed received
        self.assertEqual(order_line.qty_received, 3)

        # Check that the third backorder is created
        self.assertEqual(len(po.picking_ids), 4)
        backorder_3 = po.picking_ids - (picking_original + backorder_1 + backorder_2)
        self.assertEqual(backorder_3.backorder_id.id, backorder_2.id)

        # Check the components quantities that backorder_3 should have
        expected_quantities = {
            self.component_a: 32,
            self.component_b: 16,
            self.component_c: 48,
            self.component_d: 7,
            self.component_f: 7,
            self.component_g: 16
        }
        self._assert_quantities(backorder_3.move_lines, expected_quantities)

        # Process all missing components
        self._process_quantities(backorder_3.move_lines, expected_quantities)

        # Validating the last backorder now it's complete.
        # All kits should be received
        backorder_3.button_validate()
        self.assertEqual(order_line.qty_received, 7.0)

        # Return all components processed by backorder_3
        stock_return_picking_form = Form(self.env['stock.return.picking']
            .with_context(active_ids=backorder_3.ids, active_id=backorder_3.ids[0],
            active_model='stock.picking'))
        return_wiz = stock_return_picking_form.save()
        for return_move in return_wiz.product_return_moves:
            return_move.write({
                'quantity': expected_quantities[return_move.product_id],
                'to_refund': True
            })
        res = return_wiz.create_returns()
        return_pick = self.env['stock.picking'].browse(res['res_id'])

        # Process all components and validate the picking
        wiz_act = return_pick.button_validate()
        wiz = Form(self.env[wiz_act['res_model']].with_context(wiz_act['context'])).save()
        wiz.process()

        # Now quantity received should be 3 again
        self.assertEqual(order_line.qty_received, 3)

        stock_return_picking_form = Form(self.env['stock.return.picking']
            .with_context(active_ids=return_pick.ids, active_id=return_pick.ids[0],
            active_model='stock.picking'))
        return_wiz = stock_return_picking_form.save()
        for move in return_wiz.product_return_moves:
            move.quantity = expected_quantities[move.product_id]
        res = return_wiz.create_returns()
        return_of_return_pick = self.env['stock.picking'].browse(res['res_id'])

        # Process all components except one of each
        for move in return_of_return_pick.move_lines:
            move.write({
                'quantity_done': expected_quantities[move.product_id] - 1,
                'to_refund': True
            })

        wiz_act = return_of_return_pick.button_validate()
        wiz = Form(self.env[wiz_act['res_model']].with_context(wiz_act['context'])).save()
        wiz.process()

        # As one of each component is missing, only 6 kit_parents should be received
        self.assertEqual(order_line.qty_received, 6)

        # Check that the 4th backorder is created.
        self.assertEqual(len(po.picking_ids), 7)
        backorder_4 = po.picking_ids - (
                    picking_original + backorder_1 + backorder_2 + backorder_3 + return_of_return_pick + return_pick)
        self.assertEqual(backorder_4.backorder_id.id, return_of_return_pick.id)

        # Check the components quantities that backorder_4 should have
        for move in backorder_4.move_lines:
            self.assertEqual(move.product_qty, 1)
    def test_product_1(self):
        """ As an user of Company A, checks we can or cannot create new product
        depending of its `company_id`."""
        # Creates a new product with no company_id and set a responsible.
        # The product must be created as there is no company on the product.
        product_form = Form(self.env['product.template'].with_user(
            self.user_a))
        product_form.name = 'Paramite Pie'
        product_form.responsible_id = self.user_b
        product = product_form.save()

        self.assertEqual(product.company_id.id, False)
        self.assertEqual(product.responsible_id.id, self.user_b.id)

        # Creates a new product belong to Company A and set a responsible belong
        # to Company B. The product mustn't be created as the product and the
        # user don't belong of the same company.
        self.user_b.company_ids = [(6, 0, [self.company_b.id])]
        product_form = Form(self.env['product.template'].with_user(
            self.user_a))
        product_form.name = 'Meech Munchy'
        product_form.company_id = self.company_a
        product_form.responsible_id = self.user_b

        with self.assertRaises(UserError):
            # Raises an UserError for company incompatibility.
            product = product_form.save()

        # Creates a new product belong to Company A and set a responsible belong
        # to Company A & B (default B). The product must be created as the user
        # belongs to product's company.
        self.user_b.company_ids = [(6, 0,
                                    [self.company_a.id, self.company_b.id])]
        product_form = Form(self.env['product.template'].with_user(
            self.user_a))
        product_form.name = 'Scrab Cake'
        product_form.company_id = self.company_a
        product_form.responsible_id = self.user_b
        product = product_form.save()

        self.assertEqual(product.company_id.id, self.company_a.id)
        self.assertEqual(product.responsible_id.id, self.user_b.id)
Exemple #18
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()
Exemple #19
0
    def test_journal_sequence_ordering(self):
        """Entries are correctly sorted when posting multiple at once."""
        self.test_move.name = 'XMISC/2016/00001'
        copies = reduce(
            (lambda x, y: x + y),
            [self.create_move(date=self.test_move.date) for i in range(6)])

        copies[0].date = '2019-03-05'
        copies[1].date = '2019-03-06'
        copies[2].date = '2019-03-07'
        copies[3].date = '2019-03-04'
        copies[4].date = '2019-03-05'
        copies[5].date = '2019-03-05'
        # that entry is actualy the first one of the period, so it already has a name
        # set it to '/' so that it is recomputed at post to be ordered correctly.
        copies[0].name = '/'
        copies.action_post()

        # Ordered by date
        self.assertEqual(copies[0].name, 'XMISC/2019/00002')
        self.assertEqual(copies[1].name, 'XMISC/2019/00005')
        self.assertEqual(copies[2].name, 'XMISC/2019/00006')
        self.assertEqual(copies[3].name, 'XMISC/2019/00001')
        self.assertEqual(copies[4].name, 'XMISC/2019/00003')
        self.assertEqual(copies[5].name, 'XMISC/2019/00004')

        # Can't have twice the same name
        with self.assertRaises(ValidationError):
            copies[0].name = 'XMISC/2019/00001'

        # Lets remove the order by date
        copies[0].name = 'XMISC/2019/10001'
        copies[1].name = 'XMISC/2019/10002'
        copies[2].name = 'XMISC/2019/10003'
        copies[3].name = 'XMISC/2019/10004'
        copies[4].name = 'XMISC/2019/10005'
        copies[5].name = 'XMISC/2019/10006'

        copies[4].button_draft()
        copies[4].with_context(force_delete=True).unlink()
        copies[5].button_draft()

        wizard = Form(
            self.env['account.resequence.wizard'].with_context(
                active_ids=set(copies.ids) - set(copies[4].ids),
                active_model='account.move'), )

        new_values = json.loads(wizard.new_values)
        self.assertEqual(new_values[str(copies[0].id)]['new_by_date'],
                         'XMISC/2019/10002')
        self.assertEqual(new_values[str(copies[0].id)]['new_by_name'],
                         'XMISC/2019/10001')

        self.assertEqual(new_values[str(copies[1].id)]['new_by_date'],
                         'XMISC/2019/10004')
        self.assertEqual(new_values[str(copies[1].id)]['new_by_name'],
                         'XMISC/2019/10002')

        self.assertEqual(new_values[str(copies[2].id)]['new_by_date'],
                         'XMISC/2019/10005')
        self.assertEqual(new_values[str(copies[2].id)]['new_by_name'],
                         'XMISC/2019/10003')

        self.assertEqual(new_values[str(copies[3].id)]['new_by_date'],
                         'XMISC/2019/10001')
        self.assertEqual(new_values[str(copies[3].id)]['new_by_name'],
                         'XMISC/2019/10004')

        self.assertEqual(new_values[str(copies[5].id)]['new_by_date'],
                         'XMISC/2019/10003')
        self.assertEqual(new_values[str(copies[5].id)]['new_by_name'],
                         'XMISC/2019/10005')

        wizard.save().resequence()

        self.assertEqual(copies[3].state, 'posted')
        self.assertEqual(copies[5].name, 'XMISC/2019/10005')
        self.assertEqual(copies[5].state, 'draft')
    def test_replenishment_report_1(self):
        self.product_replenished = self.env['product.product'].create({
            'name':
            'Security razor',
            'type':
            'product',
            'categ_id':
            self.env.ref('product.product_category_all').id,
        })
        # get auto-created pull rule from when warehouse is created
        self.wh.reception_route_id.rule_ids.unlink()
        self.env['stock.rule'].create({
            'name':
            'Rule Supplier',
            'route_id':
            self.wh.reception_route_id.id,
            'location_id':
            self.wh.lot_stock_id.id,
            'location_src_id':
            self.env.ref('stock.stock_location_suppliers').id,
            'action':
            'pull',
            'delay':
            1.0,
            'procure_method':
            'make_to_stock',
            'picking_type_id':
            self.wh.in_type_id.id,
        })
        delivery_picking = self.env['stock.picking'].create({
            'location_id':
            self.wh.lot_stock_id.id,
            'location_dest_id':
            self.ref('stock.stock_location_customers'),
            'picking_type_id':
            self.ref('stock.picking_type_out'),
        })
        self.env['stock.move'].create({
            'name':
            'Delivery',
            'product_id':
            self.product_replenished.id,
            'product_uom_qty':
            500.0,
            'product_uom':
            self.uom_unit.id,
            'location_id':
            self.wh.lot_stock_id.id,
            'location_dest_id':
            self.ref('stock.stock_location_customers'),
            'picking_id':
            delivery_picking.id,
        })
        delivery_picking.action_confirm()

        # Trigger the manual orderpoint creation for missing product
        self.env['stock.move'].flush()
        self.env['stock.warehouse.orderpoint'].action_open_orderpoints()

        orderpoint = self.env['stock.warehouse.orderpoint'].search([
            ('product_id', '=', self.product_replenished.id)
        ])
        self.assertTrue(orderpoint)
        self.assertEqual(orderpoint.location_id, self.wh.lot_stock_id)
        self.assertEqual(orderpoint.qty_to_order, 500.0)
        orderpoint.action_replenish()
        self.env['stock.warehouse.orderpoint'].action_open_orderpoints()

        move = self.env['stock.move'].search([
            ('product_id', '=', self.product_replenished.id),
            ('location_dest_id', '=', self.wh.lot_stock_id.id)
        ])
        # Simulate a supplier delay
        move.date = fields.datetime.now() + timedelta(days=1)
        orderpoint = self.env['stock.warehouse.orderpoint'].search([
            ('product_id', '=', self.product_replenished.id)
        ])
        self.assertFalse(orderpoint)

        orderpoint_form = Form(self.env['stock.warehouse.orderpoint'])
        orderpoint_form.product_id = self.product_replenished
        orderpoint_form.location_id = self.wh.lot_stock_id
        orderpoint = orderpoint_form.save()

        self.assertEqual(orderpoint.qty_to_order, 0.0)
        self.env['stock.warehouse.orderpoint'].action_open_orderpoints()
        self.assertEqual(orderpoint.qty_to_order, 0.0)
    def test_subcontracting_account_flow_1(self):
        self.stock_location = self.env.ref('stock.stock_location_stock')
        self.customer_location = self.env.ref('stock.stock_location_customers')
        self.supplier_location = self.env.ref('stock.stock_location_suppliers')
        self.uom_unit = self.env.ref('uom.product_uom_unit')
        self.env.ref(
            'product.product_category_all').property_cost_method = 'fifo'

        # IN 10@10 comp1 10@20 comp2
        move1 = self.env['stock.move'].create({
            'name':
            'IN 10 units @ 10.00 per unit',
            'location_id':
            self.supplier_location.id,
            'location_dest_id':
            self.env.company.subcontracting_location_id.id,
            'product_id':
            self.comp1.id,
            'product_uom':
            self.uom_unit.id,
            'product_uom_qty':
            10.0,
            'price_unit':
            10.0,
        })
        move1._action_confirm()
        move1._action_assign()
        move1.move_line_ids.qty_done = 10.0
        move1._action_done()
        move2 = self.env['stock.move'].create({
            'name':
            'IN 10 units @ 20.00 per unit',
            'location_id':
            self.supplier_location.id,
            'location_dest_id':
            self.env.company.subcontracting_location_id.id,
            'product_id':
            self.comp2.id,
            'product_uom':
            self.uom_unit.id,
            'product_uom_qty':
            10.0,
            'price_unit':
            20.0,
        })
        move2._action_confirm()
        move2._action_assign()
        move2.move_line_ids.qty_done = 10.0
        move2._action_done()

        picking_form = Form(self.env['stock.picking'])
        picking_form.picking_type_id = self.env.ref('stock.picking_type_in')
        picking_form.partner_id = self.subcontractor_partner1
        with picking_form.move_ids_without_package.new() as move:
            move.product_id = self.finished
            move.product_uom_qty = 1
        picking_receipt = picking_form.save()
        picking_receipt.move_lines.price_unit = 30.0

        picking_receipt.action_confirm()
        picking_receipt.move_lines.quantity_done = 1.0
        picking_receipt._action_done()

        mo = picking_receipt._get_subcontracted_productions()
        # Finished is made of 1 comp1 and 1 comp2.
        # Cost of comp1 = 10
        # Cost of comp2 = 20
        # --> Cost of finished = 10 + 20 = 30
        # Additionnal cost = 30 (from the purchase order line or directly set on the stock move here)
        # Total cost of subcontracting 1 unit of finished = 30 + 30 = 60
        self.assertEqual(mo.move_finished_ids.stock_valuation_layer_ids.value,
                         60)
        self.assertEqual(
            picking_receipt.move_lines.stock_valuation_layer_ids.value, 0)
        self.assertEqual(picking_receipt.move_lines.product_id.value_svl, 60)

        # Do the same without any additionnal cost
        picking_receipt = picking_receipt.copy()
        picking_receipt.move_lines.price_unit = 0

        picking_receipt.action_confirm()
        picking_receipt.move_lines.quantity_done = 1.0
        picking_receipt._action_done()

        mo = picking_receipt._get_subcontracted_productions()
        # In this case, since there isn't any additionnal cost, the total cost of the subcontracting
        # is the sum of the components' costs: 10 + 20 = 30
        self.assertEqual(mo.move_finished_ids.stock_valuation_layer_ids.value,
                         30)
        self.assertEqual(picking_receipt.move_lines.product_id.value_svl, 90)
Exemple #22
0
    def test_generate_04_generate_in_multiple_time(self):
        """ Generates a Serial Number for each move lines (except the last one)
        but with multiple assignments, and checks the generated Serial Numbers
        are what we expect.
        """
        nbre_of_lines = 10
        move = self.get_new_move(nbre_of_lines)

        form_wizard = Form(self.env['stock.assign.serial'].with_context(
            default_move_id=move.id, ))
        # First assignment
        form_wizard.next_serial_count = 3
        form_wizard.next_serial_number = '001'
        wiz = form_wizard.save()
        wiz.generate_serial_numbers()
        # Second assignment
        form_wizard.next_serial_count = 2
        form_wizard.next_serial_number = 'bilou-64'
        wiz = form_wizard.save()
        wiz.generate_serial_numbers()
        # Third assignment
        form_wizard.next_serial_count = 4
        form_wizard.next_serial_number = 'ro-1337-bot'
        wiz = form_wizard.save()
        wiz.generate_serial_numbers()

        # Checks all move lines have the right SN
        generated_numbers = [
            # Correspond to the first assignment
            '001',
            '002',
            '003',
            # Correspond to the second assignment
            'bilou-64',
            'bilou-65',
            # Correspond to the third assignment
            'ro-1337-bot',
            'ro-1338-bot',
            'ro-1339-bot',
            'ro-1340-bot',
        ]
        self.assertEqual(len(move.move_line_ids),
                         nbre_of_lines + len(generated_numbers))
        self.assertEqual(len(move.move_line_nosuggest_ids),
                         len(generated_numbers))
        for move_line in move.move_line_nosuggest_ids:
            self.assertEqual(move_line.qty_done, 1)
            self.assertEqual(move_line.lot_name, generated_numbers.pop(0))
        for move_line in (move.move_line_ids - move.move_line_nosuggest_ids):
            self.assertEqual(move_line.qty_done, 0)
            self.assertEqual(move_line.lot_name, False)
Exemple #23
0
    def test_allow_rule_creation_for_route_without_company(self):
        self.env['res.config.settings'].write({
            'group_stock_adv_location':
            True,
            'group_stock_multi_locations':
            True,
        })

        warehouse = self.env['stock.warehouse'].search(
            [('company_id', '=', self.env.company.id)], limit=1)

        location_1 = self.env['stock.location'].create({
            'name':
            'loc1',
            'location_id':
            warehouse.id
        })

        location_2 = self.env['stock.location'].create({
            'name':
            'loc2',
            'location_id':
            warehouse.id
        })

        receipt_1 = self.env['stock.picking.type'].create({
            'name':
            'Receipts from loc1',
            'sequence_code':
            'IN1',
            'code':
            'incoming',
            'warehouse_id':
            warehouse.id,
            'default_location_dest_id':
            location_1.id,
        })

        receipt_2 = self.env['stock.picking.type'].create({
            'name':
            'Receipts from loc2',
            'sequence_code':
            'IN2',
            'code':
            'incoming',
            'warehouse_id':
            warehouse.id,
            'default_location_dest_id':
            location_2.id,
        })

        route = self.env['stock.location.route'].create({
            'name': 'Buy',
            'company_id': False
        })

        with Form(route) as r:
            with r.rule_ids.new() as line:
                line.name = 'first rule'
                line.action = 'buy'
                line.picking_type_id = receipt_1
            with r.rule_ids.new() as line:
                line.name = 'second rule'
                line.action = 'buy'
                line.picking_type_id = receipt_2
Exemple #24
0
    def test_generate_02_prefix_suffix(self):
        """ Generates some Serial Numbers and checks the prefix and/or suffix
        are correctly used.
        """
        nbre_of_lines = 10
        # Case #1: Prefix, no suffix
        move = self.get_new_move(nbre_of_lines)
        form_wizard = Form(self.env['stock.assign.serial'].with_context(
            default_move_id=move.id,
            default_next_serial_number='bilou-87',
            default_next_serial_count=nbre_of_lines,
        ))
        wiz = form_wizard.save()
        wiz.generate_serial_numbers()
        # Checks all move lines have the right SN
        generated_numbers = [
            'bilou-87', 'bilou-88', 'bilou-89', 'bilou-90', 'bilou-91',
            'bilou-92', 'bilou-93', 'bilou-94', 'bilou-95', 'bilou-96'
        ]
        for move_line in move.move_line_nosuggest_ids:
            # For a product tracked by SN, the `qty_done` is set on 1 when
            # `lot_name` is set.
            self.assertEqual(move_line.qty_done, 1)
            self.assertEqual(move_line.lot_name, generated_numbers.pop(0))

        # Case #2: No prefix, suffix
        move = self.get_new_move(nbre_of_lines)
        form_wizard = Form(self.env['stock.assign.serial'].with_context(
            default_move_id=move.id,
            default_next_serial_number='005-ccc',
            default_next_serial_count=nbre_of_lines,
        ))
        wiz = form_wizard.save()
        wiz.generate_serial_numbers()
        # Checks all move lines have the right SN
        generated_numbers = [
            '005-ccc', '006-ccc', '007-ccc', '008-ccc', '009-ccc', '010-ccc',
            '011-ccc', '012-ccc', '013-ccc', '014-ccc'
        ]
        for move_line in move.move_line_nosuggest_ids:
            # For a product tracked by SN, the `qty_done` is set on 1 when
            # `lot_name` is set.
            self.assertEqual(move_line.qty_done, 1)
            self.assertEqual(move_line.lot_name, generated_numbers.pop(0))

        # Case #3: Prefix + suffix
        move = self.get_new_move(nbre_of_lines)
        form_wizard = Form(self.env['stock.assign.serial'].with_context(
            default_move_id=move.id,
            default_next_serial_number='alpha-012-345-beta',
            default_next_serial_count=nbre_of_lines,
        ))
        wiz = form_wizard.save()
        wiz.generate_serial_numbers()
        # Checks all move lines have the right SN
        generated_numbers = [
            'alpha-012-345-beta', 'alpha-012-346-beta', 'alpha-012-347-beta',
            'alpha-012-348-beta', 'alpha-012-349-beta', 'alpha-012-350-beta',
            'alpha-012-351-beta', 'alpha-012-352-beta', 'alpha-012-353-beta',
            'alpha-012-354-beta'
        ]
        for move_line in move.move_line_nosuggest_ids:
            # For a product tracked by SN, the `qty_done` is set on 1 when
            # `lot_name` is set.
            self.assertEqual(move_line.qty_done, 1)
            self.assertEqual(move_line.lot_name, generated_numbers.pop(0))

        # Case #4: Prefix + suffix, identical number pattern
        move = self.get_new_move(nbre_of_lines)
        form_wizard = Form(self.env['stock.assign.serial'].with_context(
            default_move_id=move.id,
            default_next_serial_number='BAV023B00001S00001',
            default_next_serial_count=nbre_of_lines,
        ))
        wiz = form_wizard.save()
        wiz.generate_serial_numbers()
        # Checks all move lines have the right SN
        generated_numbers = [
            'BAV023B00001S00001', 'BAV023B00001S00002', 'BAV023B00001S00003',
            'BAV023B00001S00004', 'BAV023B00001S00005', 'BAV023B00001S00006',
            'BAV023B00001S00007', 'BAV023B00001S00008', 'BAV023B00001S00009',
            'BAV023B00001S00010'
        ]
        for move_line in move.move_line_nosuggest_ids:
            # For a product tracked by SN, the `qty_done` is set on 1 when
            # `lot_name` is set.
            self.assertEqual(move_line.qty_done, 1)
            self.assertEqual(move_line.lot_name, generated_numbers.pop(0))
    def test_in_refund_line_onchange_accounting_fields_1(self):
        move_form = Form(self.invoice)
        with move_form.line_ids.edit(2) as line_form:
            # Custom credit on the first product line.
            line_form.credit = 3000
        with move_form.line_ids.edit(3) as line_form:
            # Custom debit on the second product line. Credit should be reset by onchange.
            # /!\ It's a negative line.
            line_form.debit = 500
        with move_form.line_ids.edit(0) as line_form:
            # Custom credit on the first tax line.
            line_form.credit = 800
        with move_form.line_ids.edit(4) as line_form:
            # Custom credit on the second tax line.
            line_form.credit = 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,
                'credit': 3000.0,
            },
            {
                **self.product_line_vals_2,
                'price_unit': -500.0,
                'price_subtotal': -500.0,
                'price_total': -650.0,
                'amount_currency': 500.0,
                'debit': 500.0,
                'credit': 0.0,
            },
            {
                **self.tax_line_vals_1,
                'price_unit': 800.0,
                'price_subtotal': 800.0,
                'price_total': 800.0,
                'amount_currency': -800.0,
                'credit': 800.0,
            },
            {
                **self.tax_line_vals_2,
                'price_unit': 250.0,
                'price_subtotal': 250.0,
                'price_total': 250.0,
                'amount_currency': -250.0,
                'credit': 250.0,
            },
            {
                **self.term_line_vals_1,
                'price_unit': -3550.0,
                'price_subtotal': -3550.0,
                'price_total': -3550.0,
                'amount_currency': 3550.0,
                'debit': 3550.0,
            },
        ], {
            **self.move_vals,
            'amount_untaxed': 2500.0,
            'amount_tax': 1050.0,
            'amount_total': 3550.0,
        })
    def test_02_product_route_level_delays(self):
        """ In order to check dates, set product's Delivery Lead Time
            and warehouse route's delay."""

        company = self.env.ref('base.main_company')
        company.write({'po_lead': 1.00})

        # Update warehouse_1 with Incoming Shipments 3 steps
        self.warehouse_1.write({'reception_steps': 'three_steps'})

        # Set delay on push rule
        for push_rule in self.warehouse_1.reception_route_id.rule_ids:
            push_rule.write({'delay': 2})

        rule_delay = sum(
            self.warehouse_1.reception_route_id.rule_ids.mapped('delay'))

        date_planned = fields.Datetime.to_string(fields.datetime.now() +
                                                 timedelta(days=10))
        # Create procurement order of product_1
        self.env['procurement.group'].run([
            self.env['procurement.group'].Procurement(
                self.product_1,
                5.000,
                self.uom_unit,
                self.warehouse_1.lot_stock_id,
                'Test scheduler for RFQ',
                '/',
                self.env.company,
                {
                    'warehouse_id': self.warehouse_1,
                    'date_planned':
                    date_planned,  # 10 days added to current date of procurement to get future schedule date and order date of purchase order.
                    'date_deadline':
                    date_planned,  # 10 days added to current date of procurement to get future schedule date and order date of purchase order.
                    'rule_id': self.warehouse_1.buy_pull_id,
                    'group_id': False,
                    'route_ids': [],
                })
        ])

        # Confirm purchase order
        purchase = self.env['purchase.order.line'].search(
            [('product_id', '=', self.product_1.id)], limit=1).order_id
        purchase.button_confirm()

        # Check order date of purchase order
        order_date = fields.Datetime.from_string(date_planned) - timedelta(
            days=self.product_1.seller_ids.delay + rule_delay +
            company.po_lead)
        self.assertEqual(
            purchase.date_order, order_date,
            'Order date should be equal to: Date of the procurement order - Delivery Lead Time(supplier and pull rules).'
        )

        # Check scheduled date of purchase order
        schedule_date = order_date + timedelta(
            days=self.product_1.seller_ids.delay + rule_delay +
            company.po_lead)
        self.assertEqual(
            date_planned, str(schedule_date),
            'Schedule date should be equal to: Order date of Purchase order + Delivery Lead Time(supplier and pull rules).'
        )

        # Check the picking crated or not
        self.assertTrue(purchase.picking_ids, "Picking should be created.")

        # Check scheduled date of Internal Type shipment
        incoming_shipment1 = self.env['stock.picking'].search([
            ('move_lines.product_id', 'in', (self.product_1.id,
                                             self.product_2.id)),
            ('picking_type_id', '=', self.warehouse_1.int_type_id.id),
            ('location_id', '=', self.warehouse_1.wh_input_stock_loc_id.id),
            ('location_dest_id', '=', self.warehouse_1.wh_qc_stock_loc_id.id)
        ])
        incoming_shipment1_date = order_date + timedelta(
            days=self.product_1.seller_ids.delay + company.po_lead)
        self.assertEqual(
            incoming_shipment1.scheduled_date, incoming_shipment1_date,
            'Schedule date of Internal Type shipment for input stock location should be equal to: schedule date of purchase order + push rule delay.'
        )
        self.assertEqual(incoming_shipment1.date_deadline,
                         incoming_shipment1_date)
        old_deadline1 = incoming_shipment1.date_deadline

        incoming_shipment2 = self.env['stock.picking'].search([
            ('picking_type_id', '=', self.warehouse_1.int_type_id.id),
            ('location_id', '=', self.warehouse_1.wh_qc_stock_loc_id.id),
            ('location_dest_id', '=', self.warehouse_1.lot_stock_id.id)
        ])
        incoming_shipment2_date = schedule_date - timedelta(
            days=incoming_shipment2.move_lines[0].rule_id.delay)
        self.assertEqual(
            incoming_shipment2.scheduled_date, incoming_shipment2_date,
            'Schedule date of Internal Type shipment for quality control stock location should be equal to: schedule date of Internal type shipment for input stock location + push rule delay..'
        )
        self.assertEqual(incoming_shipment2.date_deadline,
                         incoming_shipment2_date)
        old_deadline2 = incoming_shipment2.date_deadline

        # Modify the date_planned of the purchase -> propagate the deadline
        purchase_form = Form(purchase)
        purchase_form.date_planned = purchase.date_planned + timedelta(days=1)
        purchase_form.save()
        self.assertEqual(incoming_shipment2.date_deadline,
                         old_deadline2 + timedelta(days=1),
                         'Deadline should be propagate')
        self.assertEqual(incoming_shipment1.date_deadline,
                         old_deadline1 + timedelta(days=1),
                         'Deadline should be propagate')
    def test_in_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 = 960
            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': 960.0,
                'price_subtotal': 800.0,
                'price_total': 1176.0,
                'tax_ids': (self.tax_purchase_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_expense'].id,
                'partner_id': self.partner_a.id,
                'product_uom_id': False,
                'quantity': 1.0,
                'discount': 0.0,
                'price_unit': 96.0,
                'price_subtotal': 96.0,
                'price_total': 105.6,
                'tax_ids': child_tax_2.ids,
                'tax_line_id': child_tax_1.id,
                'currency_id': self.company_data['currency'].id,
                'amount_currency': -96.0,
                'debit': 0.0,
                'credit': 96.0,
                'date_maturity': False,
                'tax_exigible': False,
            },
            {
                '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': 64.0,
                'price_subtotal': 64.0,
                'price_total': 70.4,
                'tax_ids': child_tax_2.ids,
                'tax_line_id': child_tax_1.id,
                'currency_id': self.company_data['currency'].id,
                'amount_currency': -64.0,
                'debit': 0.0,
                'credit': 64.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': 96.0,
                'price_subtotal': 96.0,
                'price_total': 96.0,
                'tax_ids': [],
                'tax_line_id': child_tax_2.id,
                'currency_id': self.company_data['currency'].id,
                'amount_currency': -96.0,
                'debit': 0.0,
                'credit': 96.0,
                'date_maturity': False,
                'tax_exigible': False,
            },
            {
                **self.term_line_vals_1,
                'price_unit': -1384.0,
                'price_subtotal': -1384.0,
                'price_total': -1384.0,
                'amount_currency': 1384.0,
                'debit': 1384.0,
            },
        ], {
            **self.move_vals,
            'amount_untaxed': 960.0,
            'amount_tax': 424.0,
            'amount_total': 1384.0,
        })
    def test_reordering_days_to_purchase(self):
        company = self.env.ref('base.main_company')
        company2 = self.env['res.company'].create({
            'name': 'Second Company',
        })
        company.write({'po_lead': 0.00})
        self.patcher = patch(
            'flectra.addons.stock.models.stock_orderpoint.fields.Date',
            wraps=fields.Date)
        self.mock_date = self.patcher.start()

        vendor = self.env['res.partner'].create({'name': 'Colruyt'})
        vendor2 = self.env['res.partner'].create({'name': 'Delhaize'})

        self.env.company.days_to_purchase = 2.0

        product = self.env['product.product'].create({
            'name':
            'Chicory',
            'type':
            'product',
            'seller_ids': [(0, 0, {
                'name': vendor2.id,
                'delay': 15.0,
                'company_id': company2.id
            }),
                           (0, 0, {
                               'name': vendor.id,
                               'delay': 1.0,
                               'company_id': company.id
                           })]
        })
        orderpoint_form = Form(self.env['stock.warehouse.orderpoint'])
        orderpoint_form.product_id = product
        orderpoint_form.product_min_qty = 0.0
        orderpoint = orderpoint_form.save()

        orderpoint_form = Form(
            self.env['stock.warehouse.orderpoint'].with_company(company2))
        orderpoint_form.product_id = product
        orderpoint_form.product_min_qty = 0.0
        orderpoint = orderpoint_form.save()

        warehouse = self.env['stock.warehouse'].search([], limit=1)
        delivery_moves = self.env['stock.move']
        for i in range(0, 6):
            delivery_moves |= self.env['stock.move'].create({
                'name':
                'Delivery',
                'date':
                datetime.today() + timedelta(days=i),
                'product_id':
                product.id,
                'product_uom':
                product.uom_id.id,
                'product_uom_qty':
                5.0,
                'location_id':
                warehouse.lot_stock_id.id,
                'location_dest_id':
                self.ref('stock.stock_location_customers'),
            })
        delivery_moves._action_confirm()
        self.env['procurement.group'].run_scheduler()
        po_line = self.env['purchase.order.line'].search([('product_id', '=',
                                                           product.id)])
        self.assertEqual(fields.Date.to_date(po_line.order_id.date_order),
                         fields.Date.today() + timedelta(days=2))
        self.assertEqual(len(po_line), 1)
        self.assertEqual(po_line.product_uom_qty, 20.0)
        self.assertEqual(len(po_line.order_id), 1)
        orderpoint_form = Form(orderpoint)
        orderpoint_form.save()

        self.mock_date.today.return_value = fields.Date.today() + timedelta(
            days=1)
        orderpoint._compute_qty()
        self.env['procurement.group'].run_scheduler()
        po_line = self.env['purchase.order.line'].search([('product_id', '=',
                                                           product.id)])
        self.assertEqual(len(po_line), 2)
        self.assertEqual(len(po_line.order_id), 2)
        new_order = po_line.order_id.sorted('date_order')[-1]
        self.assertEqual(fields.Date.to_date(new_order.date_order),
                         fields.Date.today() + timedelta(days=2))
        self.assertEqual(new_order.order_line.product_uom_qty, 5.0)
        self.patcher.stop()
    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,
        })

        # 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,
                '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,
                'amount_currency': -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,
                'amount_currency': 208.01,
                'debit': 208.01,
            },
        ], {
            **self.move_vals,
            'currency_id': self.company_data['currency'].id,
            'date': fields.Date.from_string('2016-01-01'),
            'amount_untaxed': 160.01,
            'amount_tax': 48.0,
            'amount_total': 208.01,
        })
Exemple #30
0
    def test_recurrence_fields_visibility(self):
        form = Form(self.env['project.task'])

        form.name = 'test recurring task'
        form.project_id = self.project_recurring
        form.recurring_task = True

        form.repeat_unit = 'week'
        self.assertTrue(form.repeat_show_dow)
        self.assertFalse(form.repeat_show_day)
        self.assertFalse(form.repeat_show_week)
        self.assertFalse(form.repeat_show_month)

        form.repeat_unit = 'month'
        form.repeat_on_month = 'date'
        self.assertFalse(form.repeat_show_dow)
        self.assertTrue(form.repeat_show_day)
        self.assertFalse(form.repeat_show_week)
        self.assertFalse(form.repeat_show_month)

        form.repeat_unit = 'month'
        form.repeat_on_month = 'day'
        self.assertFalse(form.repeat_show_dow)
        self.assertFalse(form.repeat_show_day)
        self.assertTrue(form.repeat_show_week)
        self.assertFalse(form.repeat_show_month)

        form.repeat_unit = 'year'
        form.repeat_on_year = 'date'
        self.assertFalse(form.repeat_show_dow)
        self.assertTrue(form.repeat_show_day)
        self.assertFalse(form.repeat_show_week)
        self.assertTrue(form.repeat_show_month)

        form.repeat_unit = 'year'
        form.repeat_on_year = 'day'
        self.assertFalse(form.repeat_show_dow)
        self.assertFalse(form.repeat_show_day)
        self.assertTrue(form.repeat_show_week)
        self.assertTrue(form.repeat_show_month)

        form.recurring_task = False
        self.assertFalse(form.repeat_show_dow)
        self.assertFalse(form.repeat_show_day)
        self.assertFalse(form.repeat_show_week)
        self.assertFalse(form.repeat_show_month)