mirror of
https://github.com/bringout/oca-ocb-core.git
synced 2026-04-22 03:32:04 +02:00
Initial commit: Core packages
This commit is contained in:
commit
12c29a983b
9512 changed files with 8379910 additions and 0 deletions
10
odoo-bringout-oca-ocb-purchase/purchase/tests/__init__.py
Normal file
10
odoo-bringout-oca-ocb-purchase/purchase/tests/__init__.py
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import test_purchase
|
||||
from . import test_purchase_order_report
|
||||
from . import test_purchase_invoice
|
||||
from . import test_access_rights
|
||||
from . import test_accrued_purchase_orders
|
||||
from . import test_purchase_tax_totals
|
||||
from . import test_purchase_dashboard
|
||||
|
|
@ -0,0 +1,127 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
from odoo.addons.account.tests.common import AccountTestInvoicingCommon
|
||||
from odoo.tests import Form, tagged
|
||||
from odoo.exceptions import AccessError
|
||||
|
||||
|
||||
@tagged('post_install', '-at_install')
|
||||
class TestPurchaseInvoice(AccountTestInvoicingCommon):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls, chart_template_ref=None):
|
||||
super().setUpClass(chart_template_ref=chart_template_ref)
|
||||
|
||||
# Create a users
|
||||
group_purchase_user = cls.env.ref('purchase.group_purchase_user')
|
||||
group_employee = cls.env.ref('base.group_user')
|
||||
group_partner_manager = cls.env.ref('base.group_partner_manager')
|
||||
|
||||
cls.purchase_user = cls.env['res.users'].with_context(
|
||||
no_reset_password=True
|
||||
).create({
|
||||
'name': 'Purchase user',
|
||||
'login': 'purchaseUser',
|
||||
'email': 'pu@odoo.com',
|
||||
'groups_id': [(6, 0, [group_purchase_user.id, group_employee.id, group_partner_manager.id])],
|
||||
})
|
||||
|
||||
cls.vendor = cls.env['res.partner'].create({
|
||||
'name': 'Supplier',
|
||||
'email': 'supplier.serv@supercompany.com',
|
||||
})
|
||||
|
||||
cls.account_expense_product = cls.env['account.account'].create({
|
||||
'code': 'EXPENSE.PROD111',
|
||||
'name': 'Expense - Test Account',
|
||||
'account_type': 'expense',
|
||||
})
|
||||
# Create category
|
||||
cls.product_category = cls.env['product.category'].create({
|
||||
'name': 'Product Category with Expense account',
|
||||
'property_account_expense_categ_id': cls.account_expense_product.id
|
||||
})
|
||||
cls.product = cls.env['product.product'].create({
|
||||
'name': "Product",
|
||||
'standard_price': 200.0,
|
||||
'list_price': 180.0,
|
||||
'type': 'service',
|
||||
})
|
||||
|
||||
def test_create_purchase_order(self):
|
||||
"""Check a purchase user can create a vendor bill from a purchase order but not post it"""
|
||||
purchase_order_form = Form(self.env['purchase.order'].with_user(self.purchase_user))
|
||||
purchase_order_form.partner_id = self.vendor
|
||||
with purchase_order_form.order_line.new() as line:
|
||||
line.name = self.product.name
|
||||
line.product_id = self.product
|
||||
line.product_qty = 4
|
||||
line.price_unit = 5
|
||||
|
||||
purchase_order = purchase_order_form.save()
|
||||
purchase_order.button_confirm()
|
||||
|
||||
purchase_order.order_line.qty_received = 4
|
||||
purchase_order.action_create_invoice()
|
||||
invoice = purchase_order.invoice_ids
|
||||
with self.assertRaises(AccessError):
|
||||
invoice.action_post()
|
||||
|
||||
def test_read_purchase_order(self):
|
||||
""" Check that a purchase user can read all purchase order and 'in' invoices"""
|
||||
purchase_user_2 = self.purchase_user.copy({
|
||||
'name': 'Purchase user 2',
|
||||
'login': 'purchaseUser2',
|
||||
'email': 'pu2@odoo.com',
|
||||
})
|
||||
|
||||
purchase_order_form = Form(self.env['purchase.order'].with_user(purchase_user_2))
|
||||
purchase_order_form.partner_id = self.vendor
|
||||
with purchase_order_form.order_line.new() as line:
|
||||
line.name = self.product.name
|
||||
line.product_id = self.product
|
||||
line.product_qty = 4
|
||||
line.price_unit = 5
|
||||
|
||||
purchase_order_user2 = purchase_order_form.save()
|
||||
purchase_order_user2.button_confirm()
|
||||
|
||||
purchase_order_user2.order_line.qty_received = 4
|
||||
purchase_order_user2.action_create_invoice()
|
||||
vendor_bill_user2 = purchase_order_user2.invoice_ids
|
||||
|
||||
# open purchase_order_user2 and vendor_bill_user2 with `self.purchase_user`
|
||||
purchase_order_user1 = Form(purchase_order_user2.with_user(self.purchase_user))
|
||||
purchase_order_user1 = purchase_order_user1.save()
|
||||
vendor_bill_user1 = Form(vendor_bill_user2.with_user(self.purchase_user))
|
||||
vendor_bill_user1 = vendor_bill_user1.save()
|
||||
|
||||
def test_double_validation(self):
|
||||
"""Only purchase managers can approve a purchase order when double
|
||||
validation is enabled"""
|
||||
group_purchase_manager = self.env.ref('purchase.group_purchase_manager')
|
||||
order = self.env['purchase.order'].create({
|
||||
"partner_id": self.vendor.id,
|
||||
"order_line": [
|
||||
(0, 0, {
|
||||
'product_id': self.product.id,
|
||||
'name': f'{self.product.name} {1:05}',
|
||||
'price_unit': 79.80,
|
||||
'product_qty': 15.0,
|
||||
}),
|
||||
]})
|
||||
company = order.sudo().company_id
|
||||
company.po_double_validation = 'two_step'
|
||||
company.po_double_validation_amount = 0
|
||||
self.purchase_user.write({
|
||||
'company_ids': [(4, company.id)],
|
||||
'company_id': company.id,
|
||||
'groups_id': [(3, group_purchase_manager.id)],
|
||||
})
|
||||
order.with_user(self.purchase_user).button_confirm()
|
||||
self.assertEqual(order.state, 'to approve')
|
||||
order.with_user(self.purchase_user).button_approve()
|
||||
self.assertEqual(order.state, 'to approve')
|
||||
self.purchase_user.groups_id += group_purchase_manager
|
||||
order.with_user(self.purchase_user).button_approve()
|
||||
self.assertEqual(order.state, 'purchase')
|
||||
|
|
@ -0,0 +1,205 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from odoo import fields, Command
|
||||
from odoo.addons.account.tests.common import AccountTestInvoicingCommon
|
||||
from odoo.tests import tagged
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
|
||||
@tagged('post_install', '-at_install')
|
||||
class TestAccruedPurchaseOrders(AccountTestInvoicingCommon):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls, chart_template_ref=None):
|
||||
super().setUpClass(chart_template_ref=chart_template_ref)
|
||||
cls.alt_exp_account = cls.company_data['default_account_expense'].copy()
|
||||
# set 'type' to 'service' to allow manualy set 'qty_delivered' even with purchase_stock installed
|
||||
cls.product_a.update({'type': 'service', 'purchase_method': 'receive'})
|
||||
cls.product_b.update({'type': 'service', 'purchase_method': 'receive'})
|
||||
#analytic distribution
|
||||
cls.default_plan = cls.env['account.analytic.plan'].create({'name': 'Default', 'company_id': False})
|
||||
cls.analytic_account_a = cls.env['account.analytic.account'].create({
|
||||
'name': 'analytic_account_a',
|
||||
'plan_id': cls.default_plan.id,
|
||||
'company_id': False,
|
||||
})
|
||||
cls.analytic_account_b = cls.env['account.analytic.account'].create({
|
||||
'name': 'analytic_account_b',
|
||||
'plan_id': cls.default_plan.id,
|
||||
'company_id': False,
|
||||
})
|
||||
cls.product_b.property_account_expense_id = cls.alt_exp_account
|
||||
cls.purchase_order = cls.env['purchase.order'].with_context(tracking_disable=True).create({
|
||||
'partner_id': cls.partner_a.id,
|
||||
'order_line': [
|
||||
Command.create({
|
||||
'name': cls.product_a.name,
|
||||
'product_id': cls.product_a.id,
|
||||
'product_qty': 10.0,
|
||||
'product_uom': cls.product_a.uom_id.id,
|
||||
'price_unit': cls.product_a.list_price,
|
||||
'taxes_id': False,
|
||||
'analytic_distribution': {
|
||||
cls.analytic_account_a.id : 80.0,
|
||||
cls.analytic_account_b.id : 20.0,
|
||||
},
|
||||
}),
|
||||
Command.create({
|
||||
'name': cls.product_b.name,
|
||||
'product_id': cls.product_b.id,
|
||||
'product_qty': 10.0,
|
||||
'product_uom': cls.product_b.uom_id.id,
|
||||
'price_unit': cls.product_b.list_price,
|
||||
'taxes_id': False,
|
||||
'analytic_distribution': {
|
||||
cls.analytic_account_b.id : 100.0,
|
||||
},
|
||||
}),
|
||||
],
|
||||
})
|
||||
cls.purchase_order.button_confirm()
|
||||
cls.account_revenue = cls.company_data['default_account_revenue']
|
||||
cls.account_expense = cls.company_data['default_account_expense']
|
||||
cls.wizard = cls.env['account.accrued.orders.wizard'].with_context({
|
||||
'active_model': 'purchase.order',
|
||||
'active_ids': cls.purchase_order.ids
|
||||
}).create({
|
||||
'account_id': cls.account_revenue.id,
|
||||
})
|
||||
|
||||
def test_accrued_order(self):
|
||||
# nothing to bill : no entries to be created
|
||||
with self.assertRaises(UserError):
|
||||
self.wizard.create_entries()
|
||||
|
||||
# 5 qty of each product billeable
|
||||
self.purchase_order.order_line.qty_received = 5
|
||||
self.assertRecordValues(self.env['account.move'].search(self.wizard.create_entries()['domain']).line_ids, [
|
||||
# reverse move lines
|
||||
{'account_id': self.account_expense.id, 'debit': 0, 'credit': 5000},
|
||||
{'account_id': self.alt_exp_account.id, 'debit': 0, 'credit': 1000},
|
||||
{'account_id': self.account_revenue.id, 'debit': 6000, 'credit': 0},
|
||||
# move lines
|
||||
{'account_id': self.account_expense.id, 'debit': 5000, 'credit': 0},
|
||||
{'account_id': self.alt_exp_account.id, 'debit': 1000, 'credit': 0},
|
||||
{'account_id': self.account_revenue.id, 'debit': 0, 'credit': 6000},
|
||||
])
|
||||
|
||||
# received products billed, nothing to bill left
|
||||
move = self.env['account.move'].browse(self.purchase_order.action_create_invoice()['res_id'])
|
||||
move.invoice_date = '2020-01-01'
|
||||
move.action_post()
|
||||
|
||||
with self.assertRaises(UserError):
|
||||
self.wizard.create_entries()
|
||||
|
||||
def test_multi_currency_accrued_order(self):
|
||||
# 5 qty of each product billeable
|
||||
self.purchase_order.order_line.qty_received = 5
|
||||
# set currency != company currency
|
||||
self.purchase_order.currency_id = self.currency_data['currency']
|
||||
moves = self.env['account.move'].search(self.wizard.create_entries()['domain'])
|
||||
for move in moves:
|
||||
self.assertEqual(move.currency_id, self.purchase_order.currency_id)
|
||||
self.assertRecordValues(moves.line_ids, [
|
||||
# reverse move lines
|
||||
{'account_id': self.account_expense.id, 'debit': 0, 'credit': 5000 / 2, 'amount_currency': -5000},
|
||||
{'account_id': self.alt_exp_account.id, 'debit': 0, 'credit': 1000 / 2, 'amount_currency': -1000},
|
||||
{'account_id': self.account_revenue.id, 'debit': 6000 / 2, 'credit': 0, 'amount_currency': 0.0},
|
||||
# move lines
|
||||
{'account_id': self.account_expense.id, 'debit': 5000 / 2, 'credit': 0, 'amount_currency': 5000},
|
||||
{'account_id': self.alt_exp_account.id, 'debit': 1000 / 2, 'credit': 0, 'amount_currency': 1000},
|
||||
{'account_id': self.account_revenue.id, 'debit': 0, 'credit': 6000 / 2, 'amount_currency': 0.0},
|
||||
])
|
||||
|
||||
def test_analytic_account_accrued_order(self):
|
||||
self.purchase_order.order_line.qty_received = 10
|
||||
|
||||
self.assertRecordValues(self.env['account.move'].search(self.wizard.create_entries()['domain']).line_ids, [
|
||||
# reverse move lines
|
||||
{'account_id': self.account_expense.id, 'debit': 0.0, 'credit': 10000.0, 'analytic_distribution': {str(self.analytic_account_a.id): 80.0, str(self.analytic_account_b.id): 20.0}},
|
||||
{'account_id': self.alt_exp_account.id, 'debit': 0.0, 'credit': 2000.0, 'analytic_distribution': {str(self.analytic_account_b.id): 100.0}},
|
||||
{'account_id': self.account_revenue.id, 'debit': 12000.0, 'credit': 0.0, 'analytic_distribution': {str(self.analytic_account_a.id): 66.67, str(self.analytic_account_b.id): 33.33}},
|
||||
# move lines
|
||||
{'account_id': self.account_expense.id, 'debit': 10000.0, 'credit': 0.0, 'analytic_distribution': {str(self.analytic_account_a.id): 80.0, str(self.analytic_account_b.id): 20.0}},
|
||||
{'account_id': self.alt_exp_account.id, 'debit': 2000.0, 'credit': 0.0, 'analytic_distribution': {str(self.analytic_account_b.id): 100.0}},
|
||||
{'account_id': self.account_revenue.id, 'debit': 0.0, 'credit': 12000.0, 'analytic_distribution': {str(self.analytic_account_a.id): 66.67, str(self.analytic_account_b.id): 33.33}},
|
||||
])
|
||||
|
||||
def test_accrued_order_with_tax_included(self):
|
||||
tax_10_included = self.env['account.tax'].create({
|
||||
'name': 'Tax 10% included',
|
||||
'amount': 10.0,
|
||||
'type_tax_use': 'purchase',
|
||||
'price_include': True,
|
||||
})
|
||||
self.purchase_order.order_line.taxes_id = tax_10_included
|
||||
self.purchase_order.order_line.qty_received = 5
|
||||
self.assertRecordValues(self.env['account.move'].search(self.wizard.create_entries()['domain']).line_ids, [
|
||||
# reverse move lines
|
||||
{'account_id': self.account_expense.id, 'debit': 0.0, 'credit': 4545.45},
|
||||
{'account_id': self.alt_exp_account.id, 'debit': 0.0, 'credit': 909.09},
|
||||
{'account_id': self.account_revenue.id, 'debit': 5454.54, 'credit': 0.0},
|
||||
# move lines
|
||||
{'account_id': self.account_expense.id, 'debit': 4545.45, 'credit': 0.0},
|
||||
{'account_id': self.alt_exp_account.id, 'debit': 909.09, 'credit': 0.0},
|
||||
{'account_id': self.account_revenue.id, 'debit': 0.0, 'credit': 5454.54},
|
||||
])
|
||||
|
||||
def test_accrued_order_returned(self):
|
||||
self.purchase_order.order_line.qty_received = 10
|
||||
# received products billed, nothing to bill left
|
||||
move = self.env['account.move'].browse(self.purchase_order.action_create_invoice()['res_id'])
|
||||
move.invoice_date = '2020-01-01'
|
||||
move.action_post()
|
||||
|
||||
with self.assertRaises(UserError):
|
||||
self.wizard.create_entries()
|
||||
|
||||
self.purchase_order.order_line.qty_received = 5
|
||||
res = self.env['account.move'].search(self.wizard.create_entries()['domain']).line_ids
|
||||
self.assertRecordValues(res, [
|
||||
# reverse move lines
|
||||
{'account_id': self.account_expense.id, 'debit': 5000.0, 'credit': 0.0},
|
||||
{'account_id': self.alt_exp_account.id, 'debit': 1000.0, 'credit': 0.0},
|
||||
{'account_id': self.account_revenue.id, 'debit': 0.0, 'credit': 6000.0},
|
||||
# move lines
|
||||
{'account_id': self.account_expense.id, 'debit': 0.0, 'credit': 5000.0},
|
||||
{'account_id': self.alt_exp_account.id, 'debit': 0.0, 'credit': 1000.0},
|
||||
{'account_id': self.account_revenue.id, 'debit': 6000.0, 'credit': 0.0},
|
||||
])
|
||||
|
||||
self.purchase_order.order_line.qty_received = 0
|
||||
res = self.env['account.move'].search(self.wizard.create_entries()['domain']).line_ids
|
||||
self.assertRecordValues(res, [
|
||||
# reverse move lines
|
||||
{'account_id': self.account_expense.id, 'debit': 5000.0, 'credit': 0.0},
|
||||
{'account_id': self.alt_exp_account.id, 'debit': 1000.0, 'credit': 0.0},
|
||||
{'account_id': self.account_revenue.id, 'debit': 0.0, 'credit': 6000.0},
|
||||
# move lines
|
||||
{'account_id': self.account_expense.id, 'debit': 0.0, 'credit': 5000.0},
|
||||
{'account_id': self.alt_exp_account.id, 'debit': 0.0, 'credit': 1000.0},
|
||||
{'account_id': self.account_revenue.id, 'debit': 6000.0, 'credit': 0.0},
|
||||
])
|
||||
|
||||
def test_error_when_different_currencies_accrued(self):
|
||||
"""
|
||||
Tests that if two Purchase Orders with different currencies are selected for Accrued Expense Entry,
|
||||
a UserError is raised.
|
||||
"""
|
||||
purchase_orders = self.env['purchase.order'].create([
|
||||
{
|
||||
'partner_id': self.partner_a.id,
|
||||
'currency_id': self.company_data['currency'].id,
|
||||
},
|
||||
{
|
||||
'partner_id': self.partner_a.id,
|
||||
'currency_id': self.currency_data['currency'].id,
|
||||
}
|
||||
])
|
||||
purchase_orders.button_confirm()
|
||||
accrued_wizard = self.env['account.accrued.orders.wizard'].with_context(
|
||||
active_model='purchase.order',
|
||||
active_ids=purchase_orders.ids,
|
||||
).new()
|
||||
with self.assertRaises(UserError, msg="An error should be raised if two different currencies are used for Accrued Expense Entry."):
|
||||
accrued_wizard._compute_move_vals()
|
||||
591
odoo-bringout-oca-ocb-purchase/purchase/tests/test_purchase.py
Normal file
591
odoo-bringout-oca-ocb-purchase/purchase/tests/test_purchase.py
Normal file
|
|
@ -0,0 +1,591 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
from odoo.addons.account.tests.common import AccountTestInvoicingCommon
|
||||
from odoo.tests import tagged, Form
|
||||
from odoo import Command, fields
|
||||
|
||||
|
||||
from datetime import timedelta
|
||||
import pytz
|
||||
|
||||
|
||||
@tagged('-at_install', 'post_install')
|
||||
class TestPurchase(AccountTestInvoicingCommon):
|
||||
|
||||
def test_date_planned(self):
|
||||
"""Set a date planned on 2 PO lines. Check that the PO date_planned is the earliest PO line date
|
||||
planned. Change one of the dates so it is even earlier and check that the date_planned is set to
|
||||
this earlier date.
|
||||
"""
|
||||
po = Form(self.env['purchase.order'])
|
||||
po.partner_id = self.partner_a
|
||||
with po.order_line.new() as po_line:
|
||||
po_line.product_id = self.product_a
|
||||
po_line.product_qty = 1
|
||||
po_line.price_unit = 100
|
||||
with po.order_line.new() as po_line:
|
||||
po_line.product_id = self.product_b
|
||||
po_line.product_qty = 10
|
||||
po_line.price_unit = 200
|
||||
po = po.save()
|
||||
|
||||
# Check that the same date is planned on both PO lines.
|
||||
self.assertNotEqual(po.order_line[0].date_planned, False)
|
||||
self.assertAlmostEqual(po.order_line[0].date_planned, po.order_line[1].date_planned, delta=timedelta(seconds=10))
|
||||
self.assertAlmostEqual(po.order_line[0].date_planned, po.date_planned, delta=timedelta(seconds=10))
|
||||
|
||||
orig_date_planned = po.order_line[0].date_planned
|
||||
|
||||
# Set an earlier date planned on a PO line and check that the PO expected date matches it.
|
||||
new_date_planned = orig_date_planned - timedelta(hours=1)
|
||||
po.order_line[0].date_planned = new_date_planned
|
||||
self.assertAlmostEqual(po.order_line[0].date_planned, po.date_planned, delta=timedelta(seconds=10))
|
||||
|
||||
# Set an even earlier date planned on the other PO line and check that the PO expected date matches it.
|
||||
new_date_planned = orig_date_planned - timedelta(hours=72)
|
||||
po.order_line[1].date_planned = new_date_planned
|
||||
self.assertAlmostEqual(po.order_line[1].date_planned, po.date_planned, delta=timedelta(seconds=10))
|
||||
|
||||
def test_purchase_order_sequence(self):
|
||||
PurchaseOrder = self.env['purchase.order'].with_context(tracking_disable=True)
|
||||
company = self.env.user.company_id
|
||||
self.env['ir.sequence'].search([
|
||||
('code', '=', 'purchase.order'),
|
||||
]).write({
|
||||
'use_date_range': True, 'prefix': 'PO/%(range_year)s/',
|
||||
})
|
||||
vals = {
|
||||
'partner_id': self.partner_a.id,
|
||||
'company_id': company.id,
|
||||
'currency_id': company.currency_id.id,
|
||||
'date_order': '2019-01-01',
|
||||
}
|
||||
purchase_order = PurchaseOrder.create(vals.copy())
|
||||
self.assertTrue(purchase_order.name.startswith('PO/2019/'))
|
||||
vals['date_order'] = '2020-01-01'
|
||||
purchase_order = PurchaseOrder.create(vals.copy())
|
||||
self.assertTrue(purchase_order.name.startswith('PO/2020/'))
|
||||
# In EU/BXL tz, this is actually already 01/01/2020
|
||||
vals['date_order'] = '2019-12-31 23:30:00'
|
||||
purchase_order = PurchaseOrder.with_context(tz='Europe/Brussels').create(vals.copy())
|
||||
self.assertTrue(purchase_order.name.startswith('PO/2020/'))
|
||||
|
||||
def test_reminder_1(self):
|
||||
"""Set to send reminder tomorrow, check if a reminder can be send to the
|
||||
partner.
|
||||
"""
|
||||
# set partner to send reminder in Company 2
|
||||
self.partner_a.with_company(self.env.companies[1]).receipt_reminder_email = True
|
||||
self.partner_a.with_company(self.env.companies[1]).reminder_date_before_receipt = 1
|
||||
# Create the PO in Company 1
|
||||
self.env.user.tz = 'Europe/Brussels'
|
||||
po = Form(self.env['purchase.order'])
|
||||
po.partner_id = self.partner_a
|
||||
with po.order_line.new() as po_line:
|
||||
po_line.product_id = self.product_a
|
||||
po_line.product_qty = 1
|
||||
po_line.price_unit = 100
|
||||
with po.order_line.new() as po_line:
|
||||
po_line.product_id = self.product_b
|
||||
po_line.product_qty = 10
|
||||
po_line.price_unit = 200
|
||||
# set to send reminder today
|
||||
date_planned = fields.Datetime.now().replace(hour=23, minute=0) + timedelta(days=2)
|
||||
po.date_planned = date_planned
|
||||
po = po.save()
|
||||
po.button_confirm()
|
||||
# Check that reminder is not set in Company 1 and the mail will not be sent
|
||||
self.assertEqual(po.company_id, self.env.companies[0])
|
||||
self.assertFalse(po.receipt_reminder_email)
|
||||
self.assertEqual(po.reminder_date_before_receipt, 1, "The default value should be taken from the company")
|
||||
old_messages = po.message_ids
|
||||
po._send_reminder_mail()
|
||||
messages_send = po.message_ids - old_messages
|
||||
self.assertFalse(messages_send)
|
||||
# Set to send reminder in Company 1
|
||||
self.partner_a.receipt_reminder_email = True
|
||||
self.partner_a.reminder_date_before_receipt = 2
|
||||
# Invalidate the cache to ensure that the computed fields are recomputed
|
||||
self.env.invalidate_all()
|
||||
self.assertTrue(po.receipt_reminder_email)
|
||||
self.assertEqual(po.reminder_date_before_receipt, 2)
|
||||
|
||||
# check date_planned is correctly set
|
||||
self.assertEqual(po.date_planned, date_planned)
|
||||
po_tz = pytz.timezone(po.user_id.tz)
|
||||
localized_date_planned = po.date_planned.astimezone(po_tz)
|
||||
self.assertEqual(localized_date_planned, po.get_localized_date_planned())
|
||||
# Ensure that the function get_localized_date_planned can accept a date in string format
|
||||
self.assertEqual(localized_date_planned, po.get_localized_date_planned(po.date_planned.strftime('%Y-%m-%d %H:%M:%S')))
|
||||
|
||||
# check vendor is a message recipient
|
||||
self.assertTrue(po.partner_id in po.message_partner_ids)
|
||||
|
||||
# check reminder send
|
||||
old_messages = po.message_ids
|
||||
po._send_reminder_mail()
|
||||
messages_send = po.message_ids - old_messages
|
||||
self.assertTrue(messages_send)
|
||||
self.assertTrue(po.partner_id in messages_send.mapped('partner_ids'))
|
||||
|
||||
# check confirm button + date planned localized in message
|
||||
old_messages = po.message_ids
|
||||
po.confirm_reminder_mail()
|
||||
messages_send = po.message_ids - old_messages
|
||||
self.assertTrue(po.mail_reminder_confirmed)
|
||||
self.assertEqual(len(messages_send), 1)
|
||||
self.assertIn(str(localized_date_planned.date()), messages_send.body)
|
||||
|
||||
def test_reminder_2(self):
|
||||
"""Set to send reminder tomorrow, check if no reminder can be send.
|
||||
"""
|
||||
po = Form(self.env['purchase.order'])
|
||||
po.partner_id = self.partner_a
|
||||
with po.order_line.new() as po_line:
|
||||
po_line.product_id = self.product_a
|
||||
po_line.product_qty = 1
|
||||
po_line.price_unit = 100
|
||||
with po.order_line.new() as po_line:
|
||||
po_line.product_id = self.product_b
|
||||
po_line.product_qty = 10
|
||||
po_line.price_unit = 200
|
||||
# set to send reminder tomorrow
|
||||
po.date_planned = fields.Datetime.now() + timedelta(days=2)
|
||||
po = po.save()
|
||||
self.partner_a.receipt_reminder_email = True
|
||||
self.partner_a.reminder_date_before_receipt = 1
|
||||
po.button_confirm()
|
||||
|
||||
# check vendor is a message recipient
|
||||
self.assertTrue(po.partner_id in po.message_partner_ids)
|
||||
|
||||
old_messages = po.message_ids
|
||||
po._send_reminder_mail()
|
||||
messages_send = po.message_ids - old_messages
|
||||
# check no reminder send
|
||||
self.assertFalse(messages_send)
|
||||
|
||||
def test_update_date_planned(self):
|
||||
po = Form(self.env['purchase.order'])
|
||||
po.partner_id = self.partner_a
|
||||
with po.order_line.new() as po_line:
|
||||
po_line.product_id = self.product_a
|
||||
po_line.product_qty = 1
|
||||
po_line.price_unit = 100
|
||||
po_line.date_planned = '2020-06-06 00:00:00'
|
||||
with po.order_line.new() as po_line:
|
||||
po_line.product_id = self.product_b
|
||||
po_line.product_qty = 10
|
||||
po_line.price_unit = 200
|
||||
po_line.date_planned = '2020-06-06 00:00:00'
|
||||
po = po.save()
|
||||
po.button_confirm()
|
||||
|
||||
# update first line
|
||||
po._update_date_planned_for_lines([(po.order_line[0], fields.Datetime.today())])
|
||||
self.assertEqual(po.order_line[0].date_planned, fields.Datetime.today())
|
||||
activity = self.env['mail.activity'].search([
|
||||
('summary', '=', 'Date Updated'),
|
||||
('res_model_id', '=', 'purchase.order'),
|
||||
('res_id', '=', po.id),
|
||||
])
|
||||
self.assertTrue(activity)
|
||||
self.assertIn(
|
||||
'<p>partner_a modified receipt dates for the following products:</p>\n'
|
||||
'<p> - product_a from 2020-06-06 to %s</p>' % fields.Date.today(),
|
||||
activity.note,
|
||||
)
|
||||
|
||||
# update second line
|
||||
po._update_date_planned_for_lines([(po.order_line[1], fields.Datetime.today())])
|
||||
self.assertEqual(po.order_line[1].date_planned, fields.Datetime.today())
|
||||
self.assertIn(
|
||||
'<p>partner_a modified receipt dates for the following products:</p>\n'
|
||||
'<p> - product_a from 2020-06-06 to %(today)s</p>\n'
|
||||
'<p> - product_b from 2020-06-06 to %(today)s</p>' % {'today': fields.Date.today()},
|
||||
activity.note,
|
||||
)
|
||||
|
||||
def test_compute_packaging_00(self):
|
||||
"""Create a PO and use packaging. Check we suggested suitable packaging
|
||||
according to the product_qty. Also check product_qty or product_packaging
|
||||
are correctly calculated when one of them changed.
|
||||
"""
|
||||
# Required for `product_packaging_qty` to be visible in the view
|
||||
self.env.user.groups_id += self.env.ref('product.group_stock_packaging')
|
||||
packaging_single = self.env['product.packaging'].create({
|
||||
'name': "I'm a packaging",
|
||||
'product_id': self.product_a.id,
|
||||
'qty': 1.0,
|
||||
})
|
||||
packaging_dozen = self.env['product.packaging'].create({
|
||||
'name': "I'm also a packaging",
|
||||
'product_id': self.product_a.id,
|
||||
'qty': 12.0,
|
||||
})
|
||||
|
||||
po = self.env['purchase.order'].create({
|
||||
'partner_id': self.partner_a.id,
|
||||
})
|
||||
po_form = Form(po)
|
||||
with po_form.order_line.new() as line:
|
||||
line.product_id = self.product_a
|
||||
line.product_qty = 1.0
|
||||
po_form.save()
|
||||
self.assertEqual(po.order_line.product_packaging_id, packaging_single)
|
||||
self.assertEqual(po.order_line.product_packaging_qty, 1.0)
|
||||
with po_form.order_line.edit(0) as line:
|
||||
line.product_packaging_qty = 2.0
|
||||
po_form.save()
|
||||
self.assertEqual(po.order_line.product_qty, 2.0)
|
||||
|
||||
|
||||
with po_form.order_line.edit(0) as line:
|
||||
line.product_qty = 24.0
|
||||
po_form.save()
|
||||
self.assertEqual(po.order_line.product_packaging_id, packaging_dozen)
|
||||
self.assertEqual(po.order_line.product_packaging_qty, 2.0)
|
||||
with po_form.order_line.edit(0) as line:
|
||||
line.product_packaging_qty = 1.0
|
||||
po_form.save()
|
||||
self.assertEqual(po.order_line.product_qty, 12)
|
||||
|
||||
# Do the same test but without form, to check the `product_packaging_id` and `product_packaging_qty` are set
|
||||
# without manual call to compute
|
||||
po = self.env['purchase.order'].create({
|
||||
'partner_id': self.partner_a.id,
|
||||
'order_line': [
|
||||
Command.create({'product_id': self.product_a.id, 'product_qty': 1.0}),
|
||||
]
|
||||
})
|
||||
self.assertEqual(po.order_line.product_packaging_id, packaging_single)
|
||||
self.assertEqual(po.order_line.product_packaging_qty, 1.0)
|
||||
po.order_line.product_packaging_qty = 2.0
|
||||
self.assertEqual(po.order_line.product_qty, 2.0)
|
||||
|
||||
po.order_line.product_qty = 24.0
|
||||
self.assertEqual(po.order_line.product_packaging_id, packaging_dozen)
|
||||
self.assertEqual(po.order_line.product_packaging_qty, 2.0)
|
||||
po.order_line.product_packaging_qty = 1.0
|
||||
self.assertEqual(po.order_line.product_qty, 12)
|
||||
|
||||
def test_compute_packaging_01(self):
|
||||
"""Create a PO and use packaging in a multicompany environment.
|
||||
Ensure any suggested packaging matches the PO's.
|
||||
"""
|
||||
company1 = self.company_data['company']
|
||||
company2 = self.company_data_2['company']
|
||||
generic_single_pack = self.env['product.packaging'].create({
|
||||
'name': "single pack",
|
||||
'product_id': self.product_a.id,
|
||||
'qty': 1.0,
|
||||
'company_id': False,
|
||||
})
|
||||
company2_pack_of_10 = self.env['product.packaging'].create({
|
||||
'name': "pack of 10 by Company 2",
|
||||
'product_id': self.product_a.id,
|
||||
'qty': 10.0,
|
||||
'company_id': company2.id,
|
||||
})
|
||||
|
||||
po1 = self.env['purchase.order'].with_company(company1).create({
|
||||
'partner_id': self.partner_a.id,
|
||||
'order_line': [
|
||||
Command.create({'product_id': self.product_a.id, 'product_qty': 10.0}),
|
||||
]
|
||||
})
|
||||
self.assertEqual(po1.order_line.product_packaging_id, generic_single_pack)
|
||||
self.assertEqual(po1.order_line.product_packaging_qty, 10.0)
|
||||
|
||||
# verify that with the right company, we can get the other packaging
|
||||
po2 = self.env['purchase.order'].with_company(company2).create({
|
||||
'partner_id': self.partner_a.id,
|
||||
'order_line': [
|
||||
Command.create({'product_id': self.product_a.id, 'product_qty': 10.0}),
|
||||
]
|
||||
})
|
||||
self.assertEqual(po2.order_line.product_packaging_id, company2_pack_of_10)
|
||||
self.assertEqual(po2.order_line.product_packaging_qty, 1.0)
|
||||
|
||||
def test_with_different_uom(self):
|
||||
""" This test ensures that the unit price is correctly computed"""
|
||||
# Required for `product_uom` to be visibile in the view
|
||||
self.env.user.groups_id += self.env.ref('uom.group_uom')
|
||||
uom_units = self.env.ref('uom.product_uom_unit')
|
||||
uom_dozens = self.env.ref('uom.product_uom_dozen')
|
||||
uom_pairs = self.env['uom.uom'].create({
|
||||
'name': 'Pairs',
|
||||
'category_id': uom_units.category_id.id,
|
||||
'uom_type': 'bigger',
|
||||
'factor_inv': 2,
|
||||
'rounding': 1,
|
||||
})
|
||||
product_data = {
|
||||
'name': 'SuperProduct',
|
||||
'type': 'consu',
|
||||
'uom_id': uom_units.id,
|
||||
'uom_po_id': uom_pairs.id,
|
||||
'standard_price': 100
|
||||
}
|
||||
product_01 = self.env['product.product'].create(product_data)
|
||||
product_02 = self.env['product.product'].create(product_data)
|
||||
|
||||
po_form = Form(self.env['purchase.order'])
|
||||
po_form.partner_id = self.partner_a
|
||||
with po_form.order_line.new() as po_line:
|
||||
po_line.product_id = product_01
|
||||
with po_form.order_line.new() as po_line:
|
||||
po_line.product_id = product_02
|
||||
po_line.product_uom = uom_dozens
|
||||
po = po_form.save()
|
||||
|
||||
self.assertEqual(po.order_line[0].price_unit, 200)
|
||||
self.assertEqual(po.order_line[1].price_unit, 1200)
|
||||
|
||||
def test_on_change_quantity_description(self):
|
||||
"""
|
||||
When a user changes the quantity of a product in a purchase order it
|
||||
should not change the description if the descritpion was changed by
|
||||
the user before
|
||||
"""
|
||||
self.env.user.write({'company_id': self.company_data['company'].id})
|
||||
|
||||
po = Form(self.env['purchase.order'])
|
||||
po.partner_id = self.partner_a
|
||||
with po.order_line.new() as pol:
|
||||
pol.product_id = self.product_a
|
||||
pol.product_qty = 1
|
||||
|
||||
pol.name = "New custom description"
|
||||
pol.product_qty += 1
|
||||
self.assertEqual(pol.name, "New custom description")
|
||||
|
||||
def test_purchase_multicurrency(self):
|
||||
"""
|
||||
Purchase order lines should keep unit price precision of products
|
||||
Also the products having prices in different currencies should be
|
||||
correctly handled when creating a purchase order i-e product having a price of 100 usd
|
||||
and when purchasing in EUR company the correct conversion should be applied
|
||||
"""
|
||||
self.env['decimal.precision'].search([
|
||||
('name', '=', 'Product Price'),
|
||||
]).digits = 5
|
||||
product = self.env['product.product'].create({
|
||||
'name': 'product_test',
|
||||
'uom_id': self.env.ref('uom.product_uom_unit').id,
|
||||
'lst_price': 10.0,
|
||||
'standard_price': 0.12345,
|
||||
})
|
||||
currency = self.env['res.currency'].create({
|
||||
'name': 'Dark Chocolate Coin',
|
||||
'symbol': '🍫',
|
||||
'rounding': 0.001,
|
||||
'position': 'after',
|
||||
'currency_unit_label': 'Dark Choco',
|
||||
'currency_subunit_label': 'Dark Cacao Powder',
|
||||
})
|
||||
currency_rate = self.env['res.currency.rate'].create({
|
||||
'name': '2016-01-01',
|
||||
'rate': 2,
|
||||
'currency_id': currency.id,
|
||||
'company_id': self.env.company.id,
|
||||
})
|
||||
|
||||
po_form = Form(self.env['purchase.order'])
|
||||
po_form.partner_id = self.partner_a
|
||||
with po_form.order_line.new() as po_line:
|
||||
po_line.product_id = product
|
||||
purchase_order_usd = po_form.save()
|
||||
self.assertEqual(purchase_order_usd.order_line.price_unit, product.standard_price, "Value shouldn't be rounded $")
|
||||
|
||||
po_form = Form(self.env['purchase.order'])
|
||||
po_form.partner_id = self.partner_a
|
||||
po_form.currency_id = currency
|
||||
with po_form.order_line.new() as po_line:
|
||||
po_line.product_id = product
|
||||
purchase_order_coco = po_form.save()
|
||||
self.assertEqual(purchase_order_coco.order_line.price_unit, currency_rate.rate * product.standard_price, "Value shouldn't be rounded 🍫")
|
||||
|
||||
#check if the correct currency is set on the purchase order by comparing the expected price and actual price
|
||||
|
||||
company_a = self.company_data['company']
|
||||
company_b = self.company_data_2['company']
|
||||
|
||||
company_b.currency_id = currency
|
||||
|
||||
self.env['res.currency.rate'].create({
|
||||
'name': '2023-01-01',
|
||||
'rate': 2,
|
||||
'currency_id': currency.id,
|
||||
'company_id': company_b.id,
|
||||
})
|
||||
|
||||
product_b = self.env['product.product'].with_company(company_a).create({
|
||||
'name': 'product_2',
|
||||
'uom_id': self.env.ref('uom.product_uom_unit').id,
|
||||
'standard_price': 0.0,
|
||||
})
|
||||
|
||||
self.assertEqual(product_b.cost_currency_id, company_a.currency_id, 'The cost currency should be the one set on'
|
||||
' the company')
|
||||
|
||||
product_b = product_b.with_company(company_b)
|
||||
|
||||
self.assertEqual(product_b.cost_currency_id, currency, 'The cost currency should be the one set on the company,'
|
||||
' as the product is now opened in another company')
|
||||
|
||||
product_b.supplier_taxes_id = False
|
||||
product_b.update({'standard_price': 10.0})
|
||||
|
||||
#create a purchase order with the product from company B
|
||||
order_b = self.env['purchase.order'].with_company(company_b).create({
|
||||
'partner_id': self.partner_a.id,
|
||||
'order_line': [(0, 0, {
|
||||
'product_id': product_b.id,
|
||||
'product_qty': 1,
|
||||
'product_uom': self.env.ref('uom.product_uom_unit').id,
|
||||
})],
|
||||
})
|
||||
|
||||
self.assertEqual(order_b.order_line.price_unit, 10.0, 'The price unit should be 10.0')
|
||||
|
||||
def test_purchase_not_creating_useless_product_vendor(self):
|
||||
""" This test ensures that the product vendor is not created when the
|
||||
product is not set on the purchase order line.
|
||||
"""
|
||||
|
||||
#create a contact of type contact
|
||||
contact = self.env['res.partner'].create({
|
||||
'name': 'Contact',
|
||||
'type': 'contact',
|
||||
})
|
||||
|
||||
#create a contact of type Delivery Address lnked to the contact
|
||||
delivery_address = self.env['res.partner'].create({
|
||||
'name': 'Delivery Address',
|
||||
'type': 'delivery',
|
||||
'parent_id': contact.id,
|
||||
})
|
||||
|
||||
#create a product that use the delivery address as vendor
|
||||
product = self.env['product.product'].create({
|
||||
'name': 'Product A',
|
||||
'seller_ids': [(0, 0, {
|
||||
'partner_id': delivery_address.id,
|
||||
'min_qty': 1.0,
|
||||
'price': 1.0,
|
||||
})]
|
||||
})
|
||||
|
||||
#create a purchase order with the delivery address as partner
|
||||
po_form = Form(self.env['purchase.order'])
|
||||
po_form.partner_id = delivery_address
|
||||
with po_form.order_line.new() as po_line:
|
||||
po_line.product_id = product
|
||||
po_line.product_qty = 1.0
|
||||
po = po_form.save()
|
||||
po.button_confirm()
|
||||
|
||||
self.assertEqual(po.order_line.product_id.seller_ids.mapped('partner_id'), delivery_address)
|
||||
|
||||
def test_supplier_list_in_product_with_multicompany(self):
|
||||
"""
|
||||
Check that a different supplier list can be added to a product for each company.
|
||||
"""
|
||||
company_a = self.company_data['company']
|
||||
company_b = self.company_data_2['company']
|
||||
product = self.env['product.product'].create({
|
||||
'name': 'product_test',
|
||||
})
|
||||
# create a purchase order in the company A
|
||||
self.env['purchase.order'].with_company(company_a).create({
|
||||
'partner_id': self.partner_a.id,
|
||||
'order_line': [(0, 0, {
|
||||
'product_id': product.id,
|
||||
'product_qty': 1,
|
||||
'product_uom': self.env.ref('uom.product_uom_unit').id,
|
||||
'price_unit': 1,
|
||||
})],
|
||||
}).button_confirm()
|
||||
|
||||
self.assertEqual(product.seller_ids[0].partner_id, self.partner_a)
|
||||
self.assertEqual(product.seller_ids[0].company_id, company_a)
|
||||
|
||||
# switch to the company B
|
||||
self.env['purchase.order'].with_company(company_b).create({
|
||||
'partner_id': self.partner_b.id,
|
||||
'order_line': [(0, 0, {
|
||||
'product_id': product.id,
|
||||
'product_qty': 1,
|
||||
'product_uom': self.env.ref('uom.product_uom_unit').id,
|
||||
'price_unit': 2,
|
||||
})],
|
||||
}).button_confirm()
|
||||
product = product.with_company(company_b)
|
||||
self.assertEqual(product.seller_ids[0].partner_id, self.partner_b)
|
||||
self.assertEqual(product.seller_ids[0].company_id, company_b)
|
||||
|
||||
# Switch to the company A and check that the vendor list is still the same
|
||||
product = product.with_company(company_a)
|
||||
self.assertEqual(product.seller_ids[0].partner_id, self.partner_a)
|
||||
self.assertEqual(product.seller_ids[0].company_id, company_a)
|
||||
|
||||
product._invalidate_cache()
|
||||
self.assertEqual(product.seller_ids[0].partner_id, self.partner_a)
|
||||
self.assertEqual(product.seller_ids[0].company_id, company_a)
|
||||
|
||||
def test_purchase_order_multi_company(self):
|
||||
"""
|
||||
Check that the unit price is correct in a multi company environment
|
||||
when editing a PO with another company selected.
|
||||
"""
|
||||
company_a = self.env.user.company_ids[0]
|
||||
company_b = self.env.user.company_ids[1]
|
||||
product = self.env['product.product'].with_company(company_a).create({
|
||||
'name': 'product_test',
|
||||
'standard_price': 3.0
|
||||
})
|
||||
product.with_company(company_b).standard_price = 5.0
|
||||
|
||||
po_form = Form(self.env['purchase.order'].with_company(company_a))
|
||||
po_form.partner_id = self.partner_a
|
||||
with po_form.order_line.new() as po_line:
|
||||
po_line.product_id = product
|
||||
po = po_form.save()
|
||||
|
||||
po_form = Form(po.with_company(company_b))
|
||||
with po_form.order_line.edit(0) as po_line:
|
||||
po_line.product_id = product
|
||||
po = po_form.save()
|
||||
|
||||
self.assertEqual(po.order_line[0].price_unit, 3.0)
|
||||
|
||||
def test_action_view_po_when_product_template_archived(self):
|
||||
"""
|
||||
Test to ensure that the purchased_product_qty value remains the same
|
||||
after archiving the product template. Also check that the purchased smart
|
||||
button returns the correct purchase order lines.
|
||||
"""
|
||||
po = self.env['purchase.order'].create({
|
||||
'partner_id': self.partner_a.id,
|
||||
'order_line': [
|
||||
Command.create({
|
||||
'product_id': self.product_a.id,
|
||||
'product_qty': 10,
|
||||
'price_unit': 1,
|
||||
}),
|
||||
],
|
||||
})
|
||||
po.button_confirm()
|
||||
product_tmpl = self.product_a.product_tmpl_id
|
||||
self.assertEqual(product_tmpl.purchased_product_qty, 10)
|
||||
|
||||
product_tmpl.action_archive()
|
||||
# Need to flush the recordsets to recalculate the purchased_product_qty after archiving
|
||||
product_tmpl.invalidate_recordset()
|
||||
|
||||
self.assertEqual(product_tmpl.purchased_product_qty, 10)
|
||||
|
||||
action = product_tmpl.action_view_po()
|
||||
action_record = self.env[action['res_model']].search(action['domain'])
|
||||
self.assertEqual(action_record, po.order_line)
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
from datetime import timedelta
|
||||
from odoo.addons.account.tests.common import AccountTestInvoicingCommon
|
||||
from odoo.addons.mail.tests.common import MailCase
|
||||
from odoo.tests import tagged, Form, new_test_user
|
||||
from odoo.tools import mute_logger, format_amount
|
||||
from odoo import fields
|
||||
|
||||
@tagged('-at_install', 'post_install')
|
||||
class TestPurchaseDashboard(AccountTestInvoicingCommon, MailCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls, chart_template_ref=None):
|
||||
super().setUpClass(chart_template_ref=chart_template_ref)
|
||||
|
||||
# Create two new users
|
||||
cls.user_a = new_test_user(cls.env, login='purchaseusera', groups='purchase.group_purchase_user')
|
||||
cls.user_b = new_test_user(cls.env, login='purchaseuserb', groups='purchase.group_purchase_user')
|
||||
|
||||
# Create two products.
|
||||
product_data = {
|
||||
'name': 'SuperProduct',
|
||||
'type': 'consu',
|
||||
}
|
||||
cls.product_100 = cls.env['product.product'].create({**product_data, 'standard_price': 100})
|
||||
cls.product_250 = cls.env['product.product'].create({**product_data, 'standard_price': 250})
|
||||
|
||||
@mute_logger('odoo.addons.mail.models.mail_mail')
|
||||
def test_purchase_dashboard(self):
|
||||
'''
|
||||
Test purchase dashboard values with multiple users.
|
||||
'''
|
||||
|
||||
# Create 3 Request for Quotations with lines.
|
||||
rfqs = self.env['purchase.order'].create([{
|
||||
'partner_id': self.partner_a.id,
|
||||
'company_id': self.user_a.company_id.id,
|
||||
'currency_id': self.user_a.company_id.currency_id.id,
|
||||
'date_order': fields.Date.today(),
|
||||
} for i in range(3)])
|
||||
for rfq, qty in zip(rfqs, [1, 2, 3]):
|
||||
rfq_form = Form(rfq)
|
||||
with rfq_form.order_line.new() as line_1:
|
||||
line_1.product_id = self.product_100
|
||||
line_1.product_qty = qty
|
||||
with rfq_form.order_line.new() as line_2:
|
||||
line_2.product_id = self.product_250
|
||||
line_2.product_qty = qty
|
||||
rfq_form.save()
|
||||
|
||||
# Create 1 late RFQ without line.
|
||||
self.env['purchase.order'].create([{
|
||||
'partner_id': self.partner_a.id,
|
||||
'company_id': self.user_a.company_id.id,
|
||||
'currency_id': self.user_a.company_id.currency_id.id,
|
||||
'date_order': fields.Date.today() - timedelta(days=7)
|
||||
}])
|
||||
|
||||
# Create 1 draft RFQ for user A.
|
||||
self.env['purchase.order'].with_user(self.user_a).create([{
|
||||
'partner_id': self.partner_a.id,
|
||||
'company_id': self.user_a.company_id.id,
|
||||
'currency_id': self.user_a.company_id.currency_id.id,
|
||||
'date_order': fields.Date().today() + timedelta(days=7)
|
||||
}])
|
||||
|
||||
self.flush_tracking()
|
||||
with self.mock_mail_gateway():
|
||||
rfqs[0].with_user(self.user_a).write({'state': 'sent'})
|
||||
self.flush_tracking()
|
||||
# Sanity checks for rfq state.
|
||||
self.assertEqual(rfqs[0].state, 'sent')
|
||||
with self.mock_mail_gateway():
|
||||
rfqs[1].with_user(self.user_b).write({'state': 'sent'})
|
||||
self.flush_tracking()
|
||||
self.assertEqual(rfqs[1].state, 'sent')
|
||||
|
||||
# Confirm Orders with lines.
|
||||
rfqs.button_confirm()
|
||||
# Retrieve dashboard as User A to check 'my_{to_send, waiting, late}' values.
|
||||
dashboard_result = rfqs.with_user(self.user_a).retrieve_dashboard()
|
||||
|
||||
# Check dashboard values
|
||||
currency_id = self.env.company.currency_id
|
||||
zero_value_keys = ['all_waiting', 'my_waiting', 'my_late']
|
||||
self.assertListEqual([dashboard_result[key] for key in zero_value_keys], [0]*len(zero_value_keys))
|
||||
self.assertEqual(dashboard_result['all_to_send'], 2)
|
||||
self.assertEqual(dashboard_result['my_to_send'], 1)
|
||||
self.assertEqual(dashboard_result['all_late'], 1)
|
||||
self.assertEqual(dashboard_result['all_avg_order_value'], format_amount(self.env, self.tax_purchase_a.compute_all(700.0)['total_included'], currency_id))
|
||||
self.assertEqual(dashboard_result['all_avg_days_to_purchase'], 0)
|
||||
self.assertEqual(dashboard_result['all_total_last_7_days'], format_amount(self.env, self.tax_purchase_a.compute_all(2100.0)['total_included'], currency_id))
|
||||
self.assertEqual(dashboard_result['all_sent_rfqs'], 2)
|
||||
|
|
@ -0,0 +1,886 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
from datetime import timedelta
|
||||
|
||||
from odoo.addons.account.tests.common import AccountTestInvoicingCommon
|
||||
from odoo.tests import tagged
|
||||
from odoo.tests.common import Form
|
||||
from odoo import Command, fields
|
||||
|
||||
|
||||
class TestPurchaseToInvoiceCommon(AccountTestInvoicingCommon):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestPurchaseToInvoiceCommon, cls).setUpClass()
|
||||
uom_unit = cls.env.ref('uom.product_uom_unit')
|
||||
uom_hour = cls.env.ref('uom.product_uom_hour')
|
||||
cls.product_order = cls.env['product.product'].create({
|
||||
'name': "Zed+ Antivirus",
|
||||
'standard_price': 235.0,
|
||||
'list_price': 280.0,
|
||||
'type': 'consu',
|
||||
'uom_id': uom_unit.id,
|
||||
'uom_po_id': uom_unit.id,
|
||||
'purchase_method': 'purchase',
|
||||
'default_code': 'PROD_ORDER',
|
||||
'taxes_id': False,
|
||||
})
|
||||
cls.service_deliver = cls.env['product.product'].create({
|
||||
'name': "Cost-plus Contract",
|
||||
'standard_price': 200.0,
|
||||
'list_price': 180.0,
|
||||
'type': 'service',
|
||||
'uom_id': uom_unit.id,
|
||||
'uom_po_id': uom_unit.id,
|
||||
'purchase_method': 'receive',
|
||||
'default_code': 'SERV_DEL',
|
||||
'taxes_id': False,
|
||||
})
|
||||
cls.service_order = cls.env['product.product'].create({
|
||||
'name': "Prepaid Consulting",
|
||||
'standard_price': 40.0,
|
||||
'list_price': 90.0,
|
||||
'type': 'service',
|
||||
'uom_id': uom_hour.id,
|
||||
'uom_po_id': uom_hour.id,
|
||||
'purchase_method': 'purchase',
|
||||
'default_code': 'PRE-PAID',
|
||||
'taxes_id': False,
|
||||
})
|
||||
cls.product_deliver = cls.env['product.product'].create({
|
||||
'name': "Switch, 24 ports",
|
||||
'standard_price': 55.0,
|
||||
'list_price': 70.0,
|
||||
'type': 'consu',
|
||||
'uom_id': uom_unit.id,
|
||||
'uom_po_id': uom_unit.id,
|
||||
'purchase_method': 'receive',
|
||||
'default_code': 'PROD_DEL',
|
||||
'taxes_id': False,
|
||||
})
|
||||
|
||||
@classmethod
|
||||
def init_purchase(cls, partner=None, confirm=False, products=None, taxes=None, company=False):
|
||||
date_planned = fields.Datetime.now() - timedelta(days=1)
|
||||
po_form = Form(cls.env['purchase.order'] \
|
||||
.with_company(company or cls.env.company) \
|
||||
.with_context(tracking_disable=True))
|
||||
po_form.partner_id = partner or cls.partner_a
|
||||
po_form.partner_ref = 'my_match_reference'
|
||||
|
||||
for product in (products or []):
|
||||
with po_form.order_line.new() as line_form:
|
||||
line_form.product_id = product
|
||||
line_form.price_unit = product.list_price
|
||||
line_form.product_qty = 1
|
||||
line_form.product_uom = product.uom_id
|
||||
line_form.date_planned = date_planned
|
||||
if taxes:
|
||||
line_form.tax_ids.clear()
|
||||
for tax in taxes:
|
||||
line_form.tax_ids.add(tax)
|
||||
|
||||
rslt = po_form.save()
|
||||
|
||||
if confirm:
|
||||
rslt.button_confirm()
|
||||
|
||||
return rslt
|
||||
|
||||
|
||||
@tagged('post_install', '-at_install')
|
||||
class TestPurchaseToInvoice(TestPurchaseToInvoiceCommon):
|
||||
|
||||
def test_vendor_bill_delivered(self):
|
||||
"""Test if a order of product invoiced by delivered quantity can be
|
||||
correctly invoiced."""
|
||||
purchase_order = self.env['purchase.order'].with_context(tracking_disable=True).create({
|
||||
'partner_id': self.partner_a.id,
|
||||
})
|
||||
PurchaseOrderLine = self.env['purchase.order.line'].with_context(tracking_disable=True)
|
||||
pol_prod_deliver = PurchaseOrderLine.create({
|
||||
'name': self.product_deliver.name,
|
||||
'product_id': self.product_deliver.id,
|
||||
'product_qty': 10.0,
|
||||
'product_uom': self.product_deliver.uom_id.id,
|
||||
'price_unit': self.product_deliver.list_price,
|
||||
'order_id': purchase_order.id,
|
||||
'taxes_id': False,
|
||||
})
|
||||
pol_serv_deliver = PurchaseOrderLine.create({
|
||||
'name': self.service_deliver.name,
|
||||
'product_id': self.service_deliver.id,
|
||||
'product_qty': 10.0,
|
||||
'product_uom': self.service_deliver.uom_id.id,
|
||||
'price_unit': self.service_deliver.list_price,
|
||||
'order_id': purchase_order.id,
|
||||
'taxes_id': False,
|
||||
})
|
||||
purchase_order.button_confirm()
|
||||
|
||||
self.assertEqual(purchase_order.invoice_status, "no")
|
||||
for line in purchase_order.order_line:
|
||||
self.assertEqual(line.qty_to_invoice, 0.0)
|
||||
self.assertEqual(line.qty_invoiced, 0.0)
|
||||
|
||||
purchase_order.order_line.qty_received = 5
|
||||
self.assertEqual(purchase_order.invoice_status, "to invoice")
|
||||
for line in purchase_order.order_line:
|
||||
self.assertEqual(line.qty_to_invoice, 5)
|
||||
self.assertEqual(line.qty_invoiced, 0.0)
|
||||
|
||||
purchase_order.action_create_invoice()
|
||||
self.assertEqual(purchase_order.invoice_status, "invoiced")
|
||||
for line in purchase_order.order_line:
|
||||
self.assertEqual(line.qty_to_invoice, 0.0)
|
||||
self.assertEqual(line.qty_invoiced, 5)
|
||||
|
||||
def test_vendor_bill_ordered(self):
|
||||
"""Test if a order of product invoiced by ordered quantity can be
|
||||
correctly invoiced."""
|
||||
purchase_order = self.env['purchase.order'].with_context(tracking_disable=True).create({
|
||||
'partner_id': self.partner_a.id,
|
||||
})
|
||||
PurchaseOrderLine = self.env['purchase.order.line'].with_context(tracking_disable=True)
|
||||
pol_prod_order = PurchaseOrderLine.create({
|
||||
'name': self.product_order.name,
|
||||
'product_id': self.product_order.id,
|
||||
'product_qty': 10.0,
|
||||
'product_uom': self.product_order.uom_id.id,
|
||||
'price_unit': self.product_order.list_price,
|
||||
'order_id': purchase_order.id,
|
||||
'taxes_id': False,
|
||||
})
|
||||
pol_serv_order = PurchaseOrderLine.create({
|
||||
'name': self.service_order.name,
|
||||
'product_id': self.service_order.id,
|
||||
'product_qty': 10.0,
|
||||
'product_uom': self.service_order.uom_id.id,
|
||||
'price_unit': self.service_order.list_price,
|
||||
'order_id': purchase_order.id,
|
||||
'taxes_id': False,
|
||||
})
|
||||
purchase_order.button_confirm()
|
||||
|
||||
self.assertEqual(purchase_order.invoice_status, "to invoice")
|
||||
for line in purchase_order.order_line:
|
||||
self.assertEqual(line.qty_to_invoice, 10)
|
||||
self.assertEqual(line.qty_invoiced, 0.0)
|
||||
|
||||
purchase_order.order_line.qty_received = 5
|
||||
self.assertEqual(purchase_order.invoice_status, "to invoice")
|
||||
for line in purchase_order.order_line:
|
||||
self.assertEqual(line.qty_to_invoice, 10)
|
||||
self.assertEqual(line.qty_invoiced, 0.0)
|
||||
|
||||
purchase_order.action_create_invoice()
|
||||
self.assertEqual(purchase_order.invoice_status, "invoiced")
|
||||
for line in purchase_order.order_line:
|
||||
self.assertEqual(line.qty_to_invoice, 0.0)
|
||||
self.assertEqual(line.qty_invoiced, 10)
|
||||
|
||||
def test_vendor_bill_delivered_return(self):
|
||||
"""Test when return product, a order of product invoiced by delivered
|
||||
quantity can be correctly invoiced."""
|
||||
purchase_order = self.env['purchase.order'].with_context(tracking_disable=True).create({
|
||||
'partner_id': self.partner_a.id,
|
||||
})
|
||||
PurchaseOrderLine = self.env['purchase.order.line'].with_context(tracking_disable=True)
|
||||
pol_prod_deliver = PurchaseOrderLine.create({
|
||||
'name': self.product_deliver.name,
|
||||
'product_id': self.product_deliver.id,
|
||||
'product_qty': 10.0,
|
||||
'product_uom': self.product_deliver.uom_id.id,
|
||||
'price_unit': self.product_deliver.list_price,
|
||||
'order_id': purchase_order.id,
|
||||
'taxes_id': False,
|
||||
})
|
||||
pol_serv_deliver = PurchaseOrderLine.create({
|
||||
'name': self.service_deliver.name,
|
||||
'product_id': self.service_deliver.id,
|
||||
'product_qty': 10.0,
|
||||
'product_uom': self.service_deliver.uom_id.id,
|
||||
'price_unit': self.service_deliver.list_price,
|
||||
'order_id': purchase_order.id,
|
||||
'taxes_id': False,
|
||||
})
|
||||
purchase_order.button_confirm()
|
||||
|
||||
purchase_order.order_line.qty_received = 10
|
||||
purchase_order.action_create_invoice()
|
||||
self.assertEqual(purchase_order.invoice_status, "invoiced")
|
||||
for line in purchase_order.order_line:
|
||||
self.assertEqual(line.qty_to_invoice, 0.0)
|
||||
self.assertEqual(line.qty_invoiced, 10)
|
||||
|
||||
purchase_order.order_line.qty_received = 5
|
||||
self.assertEqual(purchase_order.invoice_status, "to invoice")
|
||||
for line in purchase_order.order_line:
|
||||
self.assertEqual(line.qty_to_invoice, -5)
|
||||
self.assertEqual(line.qty_invoiced, 10)
|
||||
purchase_order.action_create_invoice()
|
||||
self.assertEqual(purchase_order.invoice_status, "invoiced")
|
||||
for line in purchase_order.order_line:
|
||||
self.assertEqual(line.qty_to_invoice, 0.0)
|
||||
self.assertEqual(line.qty_invoiced, 5)
|
||||
|
||||
def test_vendor_bill_ordered_return(self):
|
||||
"""Test when return product, a order of product invoiced by ordered
|
||||
quantity can be correctly invoiced."""
|
||||
purchase_order = self.env['purchase.order'].with_context(tracking_disable=True).create({
|
||||
'partner_id': self.partner_a.id,
|
||||
})
|
||||
PurchaseOrderLine = self.env['purchase.order.line'].with_context(tracking_disable=True)
|
||||
pol_prod_order = PurchaseOrderLine.create({
|
||||
'name': self.product_order.name,
|
||||
'product_id': self.product_order.id,
|
||||
'product_qty': 10.0,
|
||||
'product_uom': self.product_order.uom_id.id,
|
||||
'price_unit': self.product_order.list_price,
|
||||
'order_id': purchase_order.id,
|
||||
'taxes_id': False,
|
||||
})
|
||||
pol_serv_order = PurchaseOrderLine.create({
|
||||
'name': self.service_order.name,
|
||||
'product_id': self.service_order.id,
|
||||
'product_qty': 10.0,
|
||||
'product_uom': self.service_order.uom_id.id,
|
||||
'price_unit': self.service_order.list_price,
|
||||
'order_id': purchase_order.id,
|
||||
'taxes_id': False,
|
||||
})
|
||||
purchase_order.button_confirm()
|
||||
|
||||
purchase_order.order_line.qty_received = 10
|
||||
purchase_order.action_create_invoice()
|
||||
self.assertEqual(purchase_order.invoice_status, "invoiced")
|
||||
for line in purchase_order.order_line:
|
||||
self.assertEqual(line.qty_to_invoice, 0.0)
|
||||
self.assertEqual(line.qty_invoiced, 10)
|
||||
|
||||
purchase_order.order_line.qty_received = 5
|
||||
self.assertEqual(purchase_order.invoice_status, "invoiced")
|
||||
for line in purchase_order.order_line:
|
||||
self.assertEqual(line.qty_to_invoice, 0.0)
|
||||
self.assertEqual(line.qty_invoiced, 10)
|
||||
|
||||
def test_vendor_severals_bills_and_multicurrency(self):
|
||||
"""
|
||||
This test ensures that, when adding several PO to a bill, if they are expressed with different
|
||||
currency, the amount of each AML is converted to the bill's currency
|
||||
"""
|
||||
PurchaseOrderLine = self.env['purchase.order.line']
|
||||
PurchaseBillUnion = self.env['purchase.bill.union']
|
||||
ResCurrencyRate = self.env['res.currency.rate']
|
||||
usd = self.env.ref('base.USD')
|
||||
eur = self.env.ref('base.EUR')
|
||||
purchase_orders = []
|
||||
|
||||
ResCurrencyRate.create({'currency_id': usd.id, 'rate': 1})
|
||||
ResCurrencyRate.create({'currency_id': eur.id, 'rate': 2})
|
||||
|
||||
for currency in [usd, eur]:
|
||||
po = self.env['purchase.order'].with_context(tracking_disable=True).create({
|
||||
'partner_id': self.partner_a.id,
|
||||
'currency_id': currency.id,
|
||||
})
|
||||
pol_prod_order = PurchaseOrderLine.create({
|
||||
'name': self.product_order.name,
|
||||
'product_id': self.product_order.id,
|
||||
'product_qty': 1,
|
||||
'product_uom': self.product_order.uom_id.id,
|
||||
'price_unit': 1000,
|
||||
'order_id': po.id,
|
||||
'taxes_id': False,
|
||||
})
|
||||
po.button_confirm()
|
||||
pol_prod_order.write({'qty_received': 1})
|
||||
purchase_orders.append(po)
|
||||
|
||||
move_form = Form(self.env['account.move'].with_context(default_move_type='in_invoice'))
|
||||
move_form.purchase_vendor_bill_id = PurchaseBillUnion.browse(-purchase_orders[0].id)
|
||||
move_form.purchase_vendor_bill_id = PurchaseBillUnion.browse(-purchase_orders[1].id)
|
||||
move = move_form.save()
|
||||
|
||||
self.assertInvoiceValues(move, [
|
||||
{
|
||||
'display_type': 'product',
|
||||
'amount_currency': 1000,
|
||||
'balance': 1000,
|
||||
}, {
|
||||
'display_type': 'product',
|
||||
'amount_currency': 500,
|
||||
'balance': 500,
|
||||
}, {
|
||||
'display_type': 'payment_term',
|
||||
'amount_currency': -1500,
|
||||
'balance': -1500,
|
||||
},
|
||||
], {
|
||||
'amount_total': 1500,
|
||||
'currency_id': usd.id,
|
||||
})
|
||||
|
||||
def test_product_price_decimal_accuracy(self):
|
||||
self.env.ref('product.decimal_price').digits = 3
|
||||
self.env.company.currency_id.rounding = 0.01
|
||||
|
||||
po = self.env['purchase.order'].with_context(tracking_disable=True).create({
|
||||
'partner_id': self.partner_a.id,
|
||||
'order_line': [(0, 0, {
|
||||
'name': self.product_a.name,
|
||||
'product_id': self.product_a.id,
|
||||
'product_qty': 12,
|
||||
'product_uom': self.product_a.uom_id.id,
|
||||
'price_unit': 0.001,
|
||||
'taxes_id': False,
|
||||
})]
|
||||
})
|
||||
po.button_confirm()
|
||||
po.order_line.qty_received = 12
|
||||
|
||||
move_form = Form(self.env['account.move'].with_context(default_move_type='in_invoice'))
|
||||
move_form.purchase_vendor_bill_id = self.env['purchase.bill.union'].browse(-po.id)
|
||||
move = move_form.save()
|
||||
|
||||
self.assertEqual(move.amount_total, 0.01)
|
||||
|
||||
def test_vendor_bill_analytic_account_model_change(self):
|
||||
""" Tests whether, when an analytic account rule is set, and user changes manually the analytic account on
|
||||
the po, it is the same that is mentioned in the bill.
|
||||
"""
|
||||
# Required for `analytic.group_analytic_accounting` to be visible in the view
|
||||
self.env.user.groups_id += self.env.ref('analytic.group_analytic_accounting')
|
||||
analytic_plan = self.env['account.analytic.plan'].create({'name': 'Plan Test', 'company_id': False})
|
||||
analytic_account_default = self.env['account.analytic.account'].create({'name': 'default', 'plan_id': analytic_plan.id})
|
||||
analytic_account_manual = self.env['account.analytic.account'].create({'name': 'manual', 'plan_id': analytic_plan.id})
|
||||
|
||||
self.env['account.analytic.distribution.model'].create({
|
||||
'analytic_distribution': {analytic_account_default.id: 100},
|
||||
'product_id': self.product_order.id,
|
||||
})
|
||||
analytic_distribution_manual = {str(analytic_account_manual.id): 100}
|
||||
|
||||
po_form = Form(self.env['purchase.order'].with_context(tracking_disable=True))
|
||||
po_form.partner_id = self.partner_a
|
||||
with po_form.order_line.new() as po_line_form:
|
||||
po_line_form.name = self.product_order.name
|
||||
po_line_form.product_id = self.product_order
|
||||
po_line_form.product_qty = 1.0
|
||||
po_line_form.price_unit = 10
|
||||
po_line_form.analytic_distribution = analytic_distribution_manual
|
||||
|
||||
purchase_order = po_form.save()
|
||||
purchase_order.button_confirm()
|
||||
purchase_order.action_create_invoice()
|
||||
|
||||
aml = self.env['account.move.line'].search([('purchase_line_id', '=', purchase_order.order_line.id)])
|
||||
self.assertRecordValues(aml, [{'analytic_distribution': analytic_distribution_manual}])
|
||||
|
||||
def test_purchase_order_analytic_account_product_change(self):
|
||||
self.env.user.groups_id += self.env.ref('account.group_account_readonly')
|
||||
self.env.user.groups_id += self.env.ref('analytic.group_analytic_accounting')
|
||||
|
||||
analytic_plan = self.env['account.analytic.plan'].create({'name': 'Plan Test', 'company_id': False})
|
||||
analytic_account_super = self.env['account.analytic.account'].create({'name': 'Super Account', 'plan_id': analytic_plan.id})
|
||||
analytic_account_great = self.env['account.analytic.account'].create({'name': 'Great Account', 'plan_id': analytic_plan.id})
|
||||
|
||||
super_product = self.env['product.product'].create({'name': 'Super Product'})
|
||||
great_product = self.env['product.product'].create({'name': 'Great Product'})
|
||||
self.env['account.analytic.distribution.model'].create([
|
||||
{
|
||||
'analytic_distribution': {analytic_account_super.id: 100},
|
||||
'product_id': super_product.id,
|
||||
},
|
||||
{
|
||||
'analytic_distribution': {analytic_account_great.id: 100},
|
||||
'product_id': great_product.id,
|
||||
},
|
||||
])
|
||||
po_form = Form(self.env['purchase.order'].with_context(tracking_disable=True))
|
||||
partner = self.env['res.partner'].create({'name': 'Test Partner'})
|
||||
po_form.partner_id = partner
|
||||
with po_form.order_line.new() as po_line_form:
|
||||
po_line_form.name = super_product.name
|
||||
po_line_form.product_id = super_product
|
||||
purchase_order = po_form.save()
|
||||
purchase_order_line = purchase_order.order_line
|
||||
|
||||
self.assertEqual(purchase_order_line.analytic_distribution, {str(analytic_account_super.id): 100}, "The analytic account should be set to 'Super Account'")
|
||||
purchase_order_line.write({'product_id': great_product.id})
|
||||
self.assertEqual(purchase_order_line.analytic_distribution, {str(analytic_account_great.id): 100}, "The analytic account should be set to 'Great Account'")
|
||||
|
||||
po_no_analytic_distribution = self.env['purchase.order'].create({
|
||||
'partner_id': partner.id,
|
||||
})
|
||||
pol_no_analytic_distribution = self.env['purchase.order.line'].create({
|
||||
'name': super_product.name,
|
||||
'product_id': super_product.id,
|
||||
'order_id': po_no_analytic_distribution.id,
|
||||
'analytic_distribution': False,
|
||||
})
|
||||
po_no_analytic_distribution.button_confirm()
|
||||
self.assertFalse(pol_no_analytic_distribution.analytic_distribution, "The compute should not overwrite what the user has set.")
|
||||
|
||||
def test_purchase_order_to_invoice_analytic_rule_with_account_prefix(self):
|
||||
"""
|
||||
Test whether, when an analytic plan is set within the scope (applicability) of purchase
|
||||
and with an account prefix set in the distribution model,
|
||||
the default analytic account is correctly set during the conversion from po to invoice
|
||||
"""
|
||||
self.env.user.groups_id += self.env.ref('analytic.group_analytic_accounting')
|
||||
analytic_plan_default = self.env['account.analytic.plan'].create({
|
||||
'name': 'default',
|
||||
'applicability_ids': [Command.create({
|
||||
'business_domain': 'bill',
|
||||
'applicability': 'optional',
|
||||
})]
|
||||
})
|
||||
analytic_account_default = self.env['account.analytic.account'].create({'name': 'default', 'plan_id': analytic_plan_default.id})
|
||||
|
||||
analytic_distribution_model = self.env['account.analytic.distribution.model'].create({
|
||||
'account_prefix': '600',
|
||||
'analytic_distribution': {analytic_account_default.id: 100},
|
||||
'product_id': self.product_a.id,
|
||||
})
|
||||
|
||||
po = self.env['purchase.order'].create({'partner_id': self.partner_a.id})
|
||||
self.env['purchase.order.line'].create({
|
||||
'order_id': po.id,
|
||||
'name': 'test',
|
||||
'product_id': self.product_a.id
|
||||
})
|
||||
self.assertFalse(po.order_line.analytic_distribution, "There should be no analytic set.")
|
||||
po.button_confirm()
|
||||
po.order_line.qty_received = 1
|
||||
po.action_create_invoice()
|
||||
self.assertRecordValues(po.invoice_ids.invoice_line_ids,
|
||||
[{'analytic_distribution': analytic_distribution_model.analytic_distribution}])
|
||||
|
||||
def test_sequence_invoice_lines_from_multiple_purchases(self):
|
||||
"""Test if the invoice lines are sequenced by purchase order when creating an invoice
|
||||
from multiple selected po's"""
|
||||
purchase_orders = self.env['purchase.order']
|
||||
|
||||
for _ in range(3):
|
||||
pol_vals = [
|
||||
(0, 0, {
|
||||
'name': self.product_order.name,
|
||||
'product_id': self.product_order.id,
|
||||
'product_qty': 10.0,
|
||||
'product_uom': self.product_order.uom_id.id,
|
||||
'price_unit': self.product_order.list_price,
|
||||
'taxes_id': False,
|
||||
'sequence': sequence_number,
|
||||
}) for sequence_number in range(10, 13)]
|
||||
purchase_order = self.env['purchase.order'].with_context(tracking_disable=True).create({
|
||||
'partner_id': self.partner_a.id,
|
||||
'order_line': pol_vals,
|
||||
})
|
||||
purchase_order.button_confirm()
|
||||
purchase_orders |= purchase_order
|
||||
|
||||
action = purchase_orders.action_create_invoice()
|
||||
invoice = self.env['account.move'].browse(action['res_id'])
|
||||
|
||||
expected_purchase = [
|
||||
purchase_orders[0], purchase_orders[0], purchase_orders[0],
|
||||
purchase_orders[1], purchase_orders[1], purchase_orders[1],
|
||||
purchase_orders[2], purchase_orders[2], purchase_orders[2],
|
||||
]
|
||||
for line in invoice.invoice_line_ids.sorted('sequence'):
|
||||
self.assertEqual(line.purchase_order_id, expected_purchase.pop(0))
|
||||
|
||||
def test_sequence_autocomplete_invoice(self):
|
||||
"""Test if the invoice lines are sequenced by purchase order when using the autocomplete
|
||||
feature on a bill to add lines from po's"""
|
||||
purchase_orders = self.env['purchase.order']
|
||||
|
||||
for _ in range(3):
|
||||
pol_vals = [
|
||||
(0, 0, {
|
||||
'name': self.product_order.name,
|
||||
'product_id': self.product_order.id,
|
||||
'product_qty': 10.0,
|
||||
'product_uom': self.product_order.uom_id.id,
|
||||
'price_unit': self.product_order.list_price,
|
||||
'taxes_id': False,
|
||||
'sequence': sequence_number,
|
||||
}) for sequence_number in range(10, 13)]
|
||||
purchase_order = self.env['purchase.order'].with_context(tracking_disable=True).create({
|
||||
'partner_id': self.partner_a.id,
|
||||
'order_line': pol_vals,
|
||||
})
|
||||
purchase_order.button_confirm()
|
||||
purchase_orders |= purchase_order
|
||||
|
||||
move_form = Form(self.env['account.move'].with_context(default_move_type='in_invoice'))
|
||||
PurchaseBillUnion = self.env['purchase.bill.union']
|
||||
move_form.purchase_vendor_bill_id = PurchaseBillUnion.browse(-purchase_orders[0].id)
|
||||
move_form.purchase_vendor_bill_id = PurchaseBillUnion.browse(-purchase_orders[1].id)
|
||||
move_form.purchase_vendor_bill_id = PurchaseBillUnion.browse(-purchase_orders[2].id)
|
||||
invoice = move_form.save()
|
||||
|
||||
expected_purchase = [
|
||||
purchase_orders[0], purchase_orders[0], purchase_orders[0],
|
||||
purchase_orders[1], purchase_orders[1], purchase_orders[1],
|
||||
purchase_orders[2], purchase_orders[2], purchase_orders[2],
|
||||
]
|
||||
for line in invoice.invoice_line_ids.sorted('sequence'):
|
||||
self.assertEqual(line.purchase_order_id, expected_purchase.pop(0))
|
||||
|
||||
def test_partial_billing_interaction_with_invoicing_switch_threshold(self):
|
||||
""" Let's say you create a partial bill 'B' for a given PO. Now if you change the
|
||||
'Invoicing Switch Threshold' such that the bill date of 'B' is before the new threshold,
|
||||
the PO should still take bill 'B' into account.
|
||||
"""
|
||||
if not self.env['ir.module.module'].search([('name', '=', 'account_accountant'), ('state', '=', 'installed')]):
|
||||
self.skipTest("This test requires the installation of the account_account module")
|
||||
|
||||
purchase_order = self.env['purchase.order'].with_context(tracking_disable=True).create({
|
||||
'partner_id': self.partner_a.id,
|
||||
'order_line': [
|
||||
Command.create({
|
||||
'name': self.product_deliver.name,
|
||||
'product_id': self.product_deliver.id,
|
||||
'product_qty': 20.0,
|
||||
'product_uom': self.product_deliver.uom_id.id,
|
||||
'price_unit': self.product_deliver.list_price,
|
||||
'taxes_id': False,
|
||||
}),
|
||||
],
|
||||
})
|
||||
line = purchase_order.order_line[0]
|
||||
|
||||
purchase_order.button_confirm()
|
||||
line.qty_received = 10
|
||||
purchase_order.action_create_invoice()
|
||||
|
||||
invoice = purchase_order.invoice_ids
|
||||
invoice.invoice_date = invoice.date
|
||||
invoice.action_post()
|
||||
|
||||
self.assertEqual(line.qty_invoiced, 10)
|
||||
|
||||
self.env['res.config.settings'].create({
|
||||
'invoicing_switch_threshold': fields.Date.add(invoice.invoice_date, days=30),
|
||||
}).execute()
|
||||
|
||||
invoice.invalidate_model(fnames=['payment_state'])
|
||||
|
||||
self.assertEqual(line.qty_invoiced, 10)
|
||||
line.qty_received = 15
|
||||
self.assertEqual(line.qty_invoiced, 10)
|
||||
|
||||
def test_on_change_quantity_price_unit(self):
|
||||
""" When a user changes the quantity of a product in a purchase order it
|
||||
should only update the unit price if PO line has no invoice line. """
|
||||
|
||||
supplierinfo_vals = {
|
||||
'partner_id': self.partner_a.id,
|
||||
'price': 10.0,
|
||||
'min_qty': 1,
|
||||
"product_id": self.product_order.id,
|
||||
"product_tmpl_id": self.product_order.product_tmpl_id.id,
|
||||
}
|
||||
|
||||
supplierinfo = self.env["product.supplierinfo"].create(supplierinfo_vals)
|
||||
po_form = Form(self.env['purchase.order'])
|
||||
po_form.partner_id = self.partner_a
|
||||
with po_form.order_line.new() as po_line_form:
|
||||
po_line_form.product_id = self.product_order
|
||||
po_line_form.product_qty = 1
|
||||
po = po_form.save()
|
||||
po_line = po.order_line[0]
|
||||
|
||||
self.assertEqual(10.0, po_line.price_unit, "Unit price should be set to 10.0 for 1 quantity")
|
||||
|
||||
# Ensure price unit is updated when changing quantity on a un-confirmed PO
|
||||
supplierinfo.write({'min_qty': 2, 'price': 20.0})
|
||||
po_line.write({'product_qty': 2})
|
||||
self.assertEqual(20.0, po_line.price_unit, "Unit price should be set to 20.0 for 2 quantity")
|
||||
|
||||
po.button_confirm()
|
||||
|
||||
# Ensure price unit is updated when changing quantity on a confirmed PO
|
||||
supplierinfo.write({'min_qty': 3, 'price': 30.0})
|
||||
po_line.write({'product_qty': 3})
|
||||
self.assertEqual(30.0, po_line.price_unit, "Unit price should be set to 30.0 for 3 quantity")
|
||||
|
||||
po.action_create_invoice()
|
||||
|
||||
# Ensure price unit is NOT updated when changing quantity on PO confirmed and line linked to an invoice line
|
||||
supplierinfo.write({'min_qty': 4, 'price': 40.0})
|
||||
po_line.write({'product_qty': 4})
|
||||
self.assertEqual(30.0, po_line.price_unit, "Unit price should be set to 30.0 for 3 quantity")
|
||||
|
||||
with po_form.order_line.new() as po_line_form:
|
||||
po_line_form.product_id = self.product_order
|
||||
po_line_form.product_qty = 1
|
||||
po = po_form.save()
|
||||
po_line = po.order_line[1]
|
||||
|
||||
self.assertEqual(235.0, po_line.price_unit, "Unit price should be reset to 235.0 since the supplier supplies minimum of 4 quantities")
|
||||
|
||||
# Ensure price unit is updated when changing quantity on PO confirmed and line NOT linked to an invoice line
|
||||
po_line.write({'product_qty': 4})
|
||||
self.assertEqual(40.0, po_line.price_unit, "Unit price should be set to 40.0 for 4 quantity")
|
||||
|
||||
|
||||
@tagged('post_install', '-at_install')
|
||||
class TestInvoicePurchaseMatch(TestPurchaseToInvoiceCommon):
|
||||
|
||||
def test_total_match_via_partner(self):
|
||||
po = self.init_purchase(confirm=True, partner=self.partner_a, products=[self.product_order])
|
||||
invoice = self.init_invoice('in_invoice', partner=self.partner_a, products=[self.product_order])
|
||||
|
||||
invoice._find_and_set_purchase_orders(
|
||||
[], invoice.partner_id.id, invoice.amount_total)
|
||||
|
||||
self.assertTrue(invoice.id in po.invoice_ids.ids)
|
||||
self.assertEqual(invoice.amount_total, po.amount_total)
|
||||
|
||||
def test_total_match_via_po_reference(self):
|
||||
po = self.init_purchase(confirm=True, products=[self.product_order])
|
||||
invoice = self.init_invoice('in_invoice', partner=self.partner_a, products=[self.product_order])
|
||||
|
||||
invoice._find_and_set_purchase_orders(
|
||||
['my_match_reference'], invoice.partner_id.id, invoice.amount_total)
|
||||
|
||||
self.assertTrue(invoice.id in po.invoice_ids.ids)
|
||||
self.assertEqual(invoice.amount_total, po.amount_total)
|
||||
|
||||
def test_subset_total_match_prefer_purchase(self):
|
||||
po = self.init_purchase(confirm=True, products=[self.product_order, self.service_order])
|
||||
invoice = self.init_invoice('in_invoice', partner=self.partner_a, products=[self.product_order])
|
||||
|
||||
invoice._find_and_set_purchase_orders(
|
||||
['my_match_reference'], invoice.partner_id.id, invoice.amount_total, prefer_purchase_line=True)
|
||||
additional_unmatch_po_line = po.order_line.filtered(lambda l: l.product_id == self.service_order)
|
||||
|
||||
self.assertTrue(invoice.id in po.invoice_ids.ids)
|
||||
self.assertTrue(additional_unmatch_po_line.id in invoice.line_ids.purchase_line_id.ids)
|
||||
self.assertTrue(invoice.line_ids.filtered(lambda l: l.purchase_line_id == additional_unmatch_po_line).quantity == 0)
|
||||
|
||||
def test_subset_total_match_reject_purchase(self):
|
||||
po = self.init_purchase(confirm=True, products=[self.product_order, self.service_order])
|
||||
invoice = self.init_invoice('in_invoice', partner=self.partner_a, products=[self.product_order])
|
||||
|
||||
invoice._find_and_set_purchase_orders(
|
||||
['my_match_reference'], invoice.partner_id.id, invoice.amount_total, prefer_purchase_line=False)
|
||||
additional_unmatch_po_line = po.order_line.filtered(lambda l: l.product_id == self.service_order)
|
||||
|
||||
self.assertTrue(invoice.id in po.invoice_ids.ids)
|
||||
self.assertTrue(additional_unmatch_po_line.id not in invoice.line_ids.purchase_line_id.ids)
|
||||
|
||||
def test_po_match_prefer_purchase(self):
|
||||
po = self.init_purchase(confirm=True, products=[self.product_order, self.service_order])
|
||||
invoice = self.init_invoice('in_invoice', products=[self.product_a])
|
||||
|
||||
invoice._find_and_set_purchase_orders(
|
||||
['my_match_reference'], invoice.partner_id.id, invoice.amount_total, prefer_purchase_line=True)
|
||||
|
||||
self.assertTrue(invoice.id in po.invoice_ids.ids)
|
||||
|
||||
def test_po_match_reject_purchase(self):
|
||||
po = self.init_purchase(confirm=True, products=[self.product_order, self.service_order])
|
||||
invoice = self.init_invoice('in_invoice', products=[self.product_a])
|
||||
|
||||
invoice._find_and_set_purchase_orders(
|
||||
['my_match_reference'], invoice.partner_id.id, invoice.amount_total, prefer_purchase_line=False)
|
||||
|
||||
self.assertTrue(invoice.id not in po.invoice_ids.ids)
|
||||
self.assertNotEqual(invoice.amount_total, po.amount_total)
|
||||
|
||||
def test_no_match(self):
|
||||
po = self.init_purchase(confirm=True, products=[self.product_order, self.service_order])
|
||||
invoice = self.init_invoice('in_invoice', products=[self.product_a])
|
||||
|
||||
invoice._find_and_set_purchase_orders(
|
||||
['other_reference'], invoice.partner_id.id, invoice.amount_total, prefer_purchase_line=False)
|
||||
|
||||
self.assertTrue(invoice.id not in po.invoice_ids.ids)
|
||||
|
||||
def test_onchange_partner_currency(self):
|
||||
"""
|
||||
Test that the currency of the Bill is correctly set when the partner is changed
|
||||
as well as the currency of the Bill lines
|
||||
"""
|
||||
|
||||
vendor_eur = self.env['res.partner'].create({
|
||||
'name': 'Vendor EUR',
|
||||
'property_purchase_currency_id': self.env.ref('base.EUR').id,
|
||||
})
|
||||
vendor_us = self.env['res.partner'].create({
|
||||
'name': 'Vendor USD',
|
||||
'property_purchase_currency_id': self.env.ref('base.USD').id,
|
||||
})
|
||||
vendor_no_currency = self.env['res.partner'].create({
|
||||
'name': 'Vendor No Currency',
|
||||
})
|
||||
|
||||
move_form = Form(self.env['account.move'].with_context(default_move_type='in_invoice'))
|
||||
move_form.partner_id = vendor_eur
|
||||
with move_form.invoice_line_ids.new() as line_form:
|
||||
line_form.product_id = self.product_order
|
||||
line_form.quantity = 1
|
||||
bill = move_form.save()
|
||||
|
||||
self.assertEqual(bill.currency_id, self.env.ref('base.EUR'), "The currency of the Bill should be the same as the currency of the partner")
|
||||
self.assertEqual(bill.invoice_line_ids.currency_id, self.env.ref('base.EUR'), "The currency of the Bill lines should be the same as the currency of the partner")
|
||||
|
||||
move_form.partner_id = vendor_us
|
||||
bill = move_form.save()
|
||||
|
||||
self.assertEqual(bill.currency_id, self.env.ref('base.USD'), "The currency of the Bill should be the same as the currency of the partner")
|
||||
self.assertEqual(bill.invoice_line_ids.currency_id, self.env.ref('base.USD'), "The currency of the Bill lines should be the same as the currency of the partner")
|
||||
|
||||
move_form.partner_id = vendor_no_currency
|
||||
bill = move_form.save()
|
||||
|
||||
self.assertEqual(bill.currency_id, self.env.company.currency_id, "The currency of the Bill should be the same as the currency of the company")
|
||||
self.assertEqual(bill.invoice_line_ids.currency_id, self.env.company.currency_id, "The currency of the Bill lines should be the same as the currency of the company")
|
||||
|
||||
def test_onchange_partner_no_currency(self):
|
||||
"""
|
||||
Test that the currency of the Bill is correctly set when the partner is changed
|
||||
as well as the currency of the Bill lines even if the partner has no property_purchase_currency_id set
|
||||
or when and the `default_currency_id` is defined in the context
|
||||
"""
|
||||
|
||||
vendor_a = self.env['res.partner'].create({
|
||||
'name': 'Vendor A with No Currency',
|
||||
})
|
||||
vendor_b = self.env['res.partner'].create({
|
||||
'name': 'Vendor B with No Currency',
|
||||
})
|
||||
|
||||
ctx = {'default_move_type': 'in_invoice'}
|
||||
move_form = Form(self.env['account.move'].with_context(ctx))
|
||||
move_form.partner_id = vendor_a
|
||||
move_form.currency_id = self.env.ref('base.EUR')
|
||||
with move_form.invoice_line_ids.new() as line_form:
|
||||
line_form.product_id = self.product_order
|
||||
line_form.quantity = 1
|
||||
bill = move_form.save()
|
||||
|
||||
self.assertEqual(bill.currency_id, self.env.ref('base.EUR'), "The currency of the Bill should be the one set on the Bill")
|
||||
self.assertEqual(bill.invoice_line_ids.currency_id, self.env.ref('base.EUR'), "The currency of the Bill lines should be the same as the currency of the Bill")
|
||||
|
||||
move_form.partner_id = vendor_b
|
||||
bill = move_form.save()
|
||||
|
||||
self.assertEqual(bill.currency_id, self.env.ref('base.EUR'), "The currency of the Bill should be the one set on the Bill")
|
||||
self.assertEqual(bill.invoice_line_ids.currency_id, self.env.ref('base.EUR'), "The currency of the Bill lines should be the same as the currency of the Bill")
|
||||
|
||||
ctx['default_currency_id'] = self.currency_data['currency'].id
|
||||
move_form_currency_in_context = Form(self.env['account.move'].with_context(ctx))
|
||||
move_form_currency_in_context.currency_id = self.env.ref('base.EUR')
|
||||
with move_form_currency_in_context.invoice_line_ids.new() as line_form:
|
||||
line_form.product_id = self.product_order
|
||||
line_form.quantity = 1
|
||||
move_form_currency_in_context.save()
|
||||
|
||||
move_form_currency_in_context.partner_id = vendor_a
|
||||
bill = move_form_currency_in_context.save()
|
||||
|
||||
self.assertEqual(bill.currency_id, self.currency_data['currency'], "The currency of the Bill should be the one of the context")
|
||||
self.assertEqual(bill.invoice_line_ids.currency_id, self.currency_data['currency'], "The currency of the Bill lines should be the same as the currency of the Bill")
|
||||
|
||||
def test_payment_reference_autocomplete_invoice(self):
|
||||
"""
|
||||
Test that the payment_reference field is not replaced when selected a purchase order
|
||||
We test the flow for 8 use cases:
|
||||
- Purchase order with partner ref:
|
||||
- Bill with ref:
|
||||
- Bill with payment_reference -> should not be replaced
|
||||
- Bill without payment_reference -> should be the po.partner_ref
|
||||
- Bill without ref:
|
||||
- Bill with payment_reference -> should not be replaced
|
||||
- Bill without payment_reference -> should be the po.partner_ref
|
||||
- Purchase order without partner ref:
|
||||
- Bill with ref
|
||||
- Bill with payment_reference -> should not be replaced
|
||||
- Bill without payment_reference -> should be the bill ref
|
||||
- Bill with ref
|
||||
- Bill with payment_reference -> should not be replaced
|
||||
- Bill without payment_reference -> should be empty
|
||||
"""
|
||||
purchase_order_w_ref, purchase_order_wo_ref = self.env['purchase.order'].with_context(tracking_disable=True).create([
|
||||
{
|
||||
'partner_id': self.partner_a.id,
|
||||
'partner_ref': partner_ref,
|
||||
'order_line': [
|
||||
Command.create({
|
||||
'product_id': self.product_order.id,
|
||||
'product_qty': 1.0,
|
||||
'price_unit': self.product_order.list_price,
|
||||
'taxes_id': False,
|
||||
}),
|
||||
]
|
||||
} for partner_ref in ('PO-001', False)
|
||||
])
|
||||
(purchase_order_w_ref + purchase_order_wo_ref).button_confirm()
|
||||
|
||||
expected_values_dict = {
|
||||
purchase_order_w_ref: {
|
||||
'w_bill_ref': {'w_payment_reference': '222', 'wo_payment_reference': purchase_order_w_ref.partner_ref},
|
||||
'wo_bill_ref': {'w_payment_reference': '222', 'wo_payment_reference': purchase_order_w_ref.partner_ref},
|
||||
},
|
||||
purchase_order_wo_ref: {
|
||||
'w_bill_ref': {'w_payment_reference': '222', 'wo_payment_reference': '111'},
|
||||
'wo_bill_ref': {'w_payment_reference': '222', 'wo_payment_reference': ''},
|
||||
}
|
||||
}
|
||||
|
||||
for purchase_order, purchase_expected_values in expected_values_dict.items():
|
||||
for w_bill_ref, expected_values in purchase_expected_values.items():
|
||||
for w_payment_reference, expected_value in expected_values.items():
|
||||
with self.subTest(po_partner_ref=purchase_order.partner_ref, w_bill_ref=w_bill_ref, w_payment_reference=w_payment_reference, expected_value=expected_value):
|
||||
move_form = Form(self.env['account.move'].with_context(default_move_type='in_invoice'))
|
||||
move_form.ref = '111' if w_bill_ref == 'w_bill_ref' else ''
|
||||
move_form.payment_reference = '222' if w_payment_reference == 'w_payment_reference' else ''
|
||||
move_form.purchase_vendor_bill_id = self.env['purchase.bill.union'].browse(-purchase_order.id).exists()
|
||||
payment_reference = move_form._values['payment_reference']
|
||||
self.assertEqual(payment_reference, expected_value, "The payment reference should be %s" % expected_value)
|
||||
|
||||
def test_invoice_user_id_on_bill(self):
|
||||
"""
|
||||
Test that the invoice_user_id field is False when creating a vendor bill from a PO
|
||||
or when using Auto-Complete feature of a vendor bill.
|
||||
"""
|
||||
group_purchase_user = self.env.ref('purchase.group_purchase_user')
|
||||
group_employee = self.env.ref('base.group_user')
|
||||
group_partner_manager = self.env.ref('base.group_partner_manager')
|
||||
purchase_user = self.env['res.users'].with_context(no_reset_password=True).create({
|
||||
'name': 'Purchase user',
|
||||
'login': 'purchaseUser',
|
||||
'email': 'pu@odoo.com',
|
||||
'groups_id': [Command.set([group_purchase_user.id, group_employee.id, group_partner_manager.id])],
|
||||
})
|
||||
po1 = self.env['purchase.order'].with_context(tracking_disable=True).create({
|
||||
'partner_id': self.partner_a.id,
|
||||
'user_id': purchase_user.id,
|
||||
'order_line': [
|
||||
Command.create({
|
||||
'product_id': self.product_order.id,
|
||||
'product_qty': 1.0,
|
||||
'price_unit': self.product_order.list_price,
|
||||
'taxes_id': False,
|
||||
}),
|
||||
]
|
||||
})
|
||||
po2 = po1.copy()
|
||||
po1.button_confirm()
|
||||
po2.button_confirm()
|
||||
# creating bill from PO
|
||||
po1.order_line.qty_received = 1
|
||||
po1.action_create_invoice()
|
||||
invoice1 = po1.invoice_ids
|
||||
self.assertFalse(invoice1.invoice_user_id)
|
||||
# creating bill with Auto_complete feature
|
||||
move_form = Form(self.env['account.move'].with_context(default_move_type='in_invoice'))
|
||||
move_form.purchase_vendor_bill_id = self.env['purchase.bill.union'].browse(-po2.id)
|
||||
invoice2 = move_form.save()
|
||||
self.assertFalse(invoice2.invoice_user_id)
|
||||
|
|
@ -0,0 +1,218 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
from odoo.addons.account.tests.common import AccountTestInvoicingCommon
|
||||
from odoo.tests import Form, tagged
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
|
||||
@tagged('post_install', '-at_install')
|
||||
class TestPurchaseOrderReport(AccountTestInvoicingCommon):
|
||||
|
||||
def test_00_purchase_order_report(self):
|
||||
uom_dozen = self.env.ref('uom.product_uom_dozen')
|
||||
|
||||
po = self.env['purchase.order'].create({
|
||||
'partner_id': self.partner_a.id,
|
||||
'currency_id': self.currency_data['currency'].id,
|
||||
'order_line': [
|
||||
(0, 0, {
|
||||
'name': self.product_a.name,
|
||||
'product_id': self.product_a.id,
|
||||
'product_qty': 1.0,
|
||||
'product_uom': uom_dozen.id,
|
||||
'price_unit': 100.0,
|
||||
'date_planned': datetime.today(),
|
||||
'taxes_id': False,
|
||||
}),
|
||||
(0, 0, {
|
||||
'name': self.product_b.name,
|
||||
'product_id': self.product_b.id,
|
||||
'product_qty': 1.0,
|
||||
'product_uom': uom_dozen.id,
|
||||
'price_unit': 200.0,
|
||||
'date_planned': datetime.today(),
|
||||
'taxes_id': False,
|
||||
}),
|
||||
],
|
||||
})
|
||||
po.button_confirm()
|
||||
|
||||
f = Form(self.env['account.move'].with_context(default_move_type='in_invoice'))
|
||||
f.invoice_date = f.date
|
||||
f.partner_id = po.partner_id
|
||||
# <field name="invoice_vendor_bill_id" position="after">
|
||||
# <field name="purchase_id" invisible="1"/>
|
||||
# <label for="purchase_vendor_bill_id" string="Auto-Complete" class="oe_edit_only"
|
||||
# attrs="{'invisible': ['|', ('state','!=','draft'), ('move_type', '!=', 'in_invoice')]}" />
|
||||
# <field name="purchase_vendor_bill_id" nolabel="1"
|
||||
# attrs="{'invisible': ['|', ('state','!=','draft'), ('move_type', '!=', 'in_invoice')]}"
|
||||
# class="oe_edit_only"
|
||||
# domain="partner_id and [('company_id', '=', company_id), ('partner_id.commercial_partner_id', '=', commercial_partner_id)] or [('company_id', '=', company_id)]"
|
||||
# placeholder="Select a purchase order or an old bill"
|
||||
# context="{'show_total_amount': True}"
|
||||
# options="{'no_create': True, 'no_open': True}"/>
|
||||
# </field>
|
||||
# @api.onchange('purchase_vendor_bill_id', 'purchase_id')
|
||||
# def _onchange_purchase_auto_complete(self):
|
||||
# ...
|
||||
# elif self.purchase_vendor_bill_id.purchase_order_id:
|
||||
# self.purchase_id = self.purchase_vendor_bill_id.purchase_order_id
|
||||
# self.purchase_vendor_bill_id = False
|
||||
# purchase_vendor_bill_id = fields.Many2one('purchase.bill.union'
|
||||
# class PurchaseBillUnion(models.Model):
|
||||
# _name = 'purchase.bill.union'
|
||||
# ...
|
||||
# def init(self):
|
||||
# self.env.cr.execute("""
|
||||
# ...
|
||||
# SELECT
|
||||
# -id, name, ...
|
||||
# id as purchase_order_id
|
||||
# FROM purchase_order
|
||||
# ...
|
||||
# )""")
|
||||
# ...
|
||||
f.purchase_vendor_bill_id = self.env['purchase.bill.union'].browse(-po.id)
|
||||
invoice = f.save()
|
||||
invoice.action_post()
|
||||
po.flush_model()
|
||||
|
||||
res_product1 = self.env['purchase.report'].search([
|
||||
('order_id', '=', po.id),
|
||||
('product_id', '=', self.product_a.id),
|
||||
('company_id', '=', self.company_data['company'].id),
|
||||
])
|
||||
|
||||
# check that report will convert dozen to unit or not
|
||||
self.assertEqual(res_product1.qty_ordered, 12.0, 'UoM conversion is not working')
|
||||
# report should show in company currency (amount/rate) = (100/2)
|
||||
self.assertEqual(res_product1.price_total, 50.0, 'Currency conversion is not working')
|
||||
|
||||
res_product2 = self.env['purchase.report'].search([
|
||||
('order_id', '=', po.id),
|
||||
('product_id', '=', self.product_b.id),
|
||||
('company_id', '=', self.company_data['company'].id),
|
||||
])
|
||||
|
||||
self.assertEqual(res_product2.qty_ordered, 1.0, 'No conversion needed since product_b is already a dozen')
|
||||
# report should show in company currency (amount/rate) = (200/2)
|
||||
self.assertEqual(res_product2.price_total, 100.0, 'Currency conversion is not working')
|
||||
|
||||
def test_01_delay_and_delay_pass(self):
|
||||
po_form = Form(self.env['purchase.order'])
|
||||
po_form.partner_id = self.partner_a
|
||||
po_form.date_order = datetime.now() + timedelta(days=10)
|
||||
with po_form.order_line.new() as line:
|
||||
line.product_id = self.product_a
|
||||
with po_form.order_line.new() as line:
|
||||
line.product_id = self.product_b
|
||||
po_form.date_planned = datetime.now() + timedelta(days=15)
|
||||
po = po_form.save()
|
||||
|
||||
po.button_confirm()
|
||||
|
||||
po.flush_model()
|
||||
report = self.env['purchase.report'].read_group(
|
||||
[('order_id', '=', po.id)],
|
||||
['order_id', 'delay', 'delay_pass'],
|
||||
['order_id'],
|
||||
)
|
||||
self.assertEqual(round(report[0]['delay']), -10, msg="The PO has been confirmed 10 days in advance")
|
||||
self.assertEqual(round(report[0]['delay_pass']), 5, msg="There are 5 days between the order date and the planned date")
|
||||
|
||||
def test_02_po_report_note_section_filter(self):
|
||||
po = self.env['purchase.order'].create({
|
||||
'partner_id': self.partner_a.id,
|
||||
'currency_id': self.currency_data['currency'].id,
|
||||
'order_line': [
|
||||
(0, 0, {
|
||||
'name': 'This is a note',
|
||||
'display_type': 'line_note',
|
||||
'product_id': False,
|
||||
'product_qty': 0.0,
|
||||
'product_uom': False,
|
||||
'price_unit': 0.0,
|
||||
'taxes_id': False,
|
||||
}),
|
||||
(0, 0, {
|
||||
'name': 'This is a section',
|
||||
'display_type': 'line_section',
|
||||
'product_id': False,
|
||||
'product_qty': 0.0,
|
||||
'product_uom': False,
|
||||
'price_unit': 0.0,
|
||||
'taxes_id': False,
|
||||
}),
|
||||
],
|
||||
})
|
||||
po.button_confirm()
|
||||
|
||||
result_po = self.env['purchase.report'].search([('order_id', '=', po.id)])
|
||||
self.assertFalse(result_po, "The report should ignore the notes and sections")
|
||||
|
||||
def test_po_report_currency(self):
|
||||
"""
|
||||
Check that the currency of the report is the one of the current company
|
||||
"""
|
||||
po = self.env['purchase.order'].create({
|
||||
'partner_id': self.partner_a.id,
|
||||
'currency_id': self.currency_data['currency'].id,
|
||||
'order_line': [
|
||||
(0, 0, {
|
||||
'product_id': self.product_a.id,
|
||||
'product_qty': 10.0,
|
||||
'price_unit': 50.0,
|
||||
}),
|
||||
],
|
||||
})
|
||||
po_2 = self.env['purchase.order'].create({
|
||||
'partner_id': self.partner_a.id,
|
||||
'currency_id': self.env['res.currency'].search([('name', '=', 'EUR')], limit=1).id,
|
||||
'order_line': [
|
||||
(0, 0, {
|
||||
'product_id': self.product_a.id,
|
||||
'product_qty': 10.0,
|
||||
'price_unit': 50.0,
|
||||
}),
|
||||
],
|
||||
})
|
||||
# flush the POs to make sure the report is up to date
|
||||
po.flush_model()
|
||||
po_2.flush_model()
|
||||
report = self.env['purchase.report'].search([('product_id', "=", self.product_a.id)])
|
||||
self.assertEqual(report.currency_id, self.env.company.currency_id)
|
||||
|
||||
def test_avg_price_calculation(self):
|
||||
"""
|
||||
Check that the average price is calculated based on the quantity ordered in each line
|
||||
PO:
|
||||
- 10 unit of product A -> price $50
|
||||
- 1 unit of product A -> price $10
|
||||
Total qty_ordered: 11
|
||||
avergae price: 46.36 = ((10 * 50) + (10 * 1)) / 11
|
||||
"""
|
||||
po = self.env['purchase.order'].create({
|
||||
'partner_id': self.partner_a.id,
|
||||
'order_line': [
|
||||
(0, 0, {
|
||||
'product_id': self.product_a.id,
|
||||
'product_qty': 10.0,
|
||||
'price_unit': 50.0,
|
||||
}),
|
||||
(0, 0, {
|
||||
'product_id': self.product_a.id,
|
||||
'product_qty': 1.0,
|
||||
'price_unit': 10.0,
|
||||
}),
|
||||
],
|
||||
})
|
||||
po.button_confirm()
|
||||
po.flush_model()
|
||||
report = self.env['purchase.report'].read_group(
|
||||
[('product_id', '=', self.product_a.id)],
|
||||
['qty_ordered', 'price_average:avg'],
|
||||
['product_id'],
|
||||
)
|
||||
self.assertEqual(report[0]['qty_ordered'], 11)
|
||||
self.assertEqual(round(report[0]['price_average'], 2), 46.36)
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from odoo.addons.account.tests.test_invoice_tax_totals import TestTaxTotals
|
||||
from odoo.tests import tagged
|
||||
|
||||
|
||||
@tagged('post_install', '-at_install')
|
||||
class PurchaseTestTaxTotals(TestTaxTotals):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls, chart_template_ref=None):
|
||||
super().setUpClass(chart_template_ref=chart_template_ref)
|
||||
|
||||
cls.po_product = cls.env['product.product'].create({
|
||||
'name': 'Odoo course',
|
||||
'type': 'service',
|
||||
})
|
||||
|
||||
def _create_document_for_tax_totals_test(self, lines_data):
|
||||
# Overridden in order to run the inherited tests with purchase.order's
|
||||
# tax_totals field instead of account.move's
|
||||
|
||||
lines_vals = [
|
||||
(0, 0, {
|
||||
'name': 'test',
|
||||
'product_id': self.po_product.id,
|
||||
'product_qty': 1,
|
||||
'product_uom': self.po_product.uom_po_id.id,
|
||||
'price_unit': amount,
|
||||
'taxes_id': [(6, 0, taxes.ids)],
|
||||
})
|
||||
for amount, taxes in lines_data]
|
||||
|
||||
return self.env['purchase.order'].create({
|
||||
'partner_id': self.partner_a.id,
|
||||
'order_line': lines_vals,
|
||||
})
|
||||
|
||||
def test_archived_tax_totals(self):
|
||||
tax_10 = self.env['account.tax'].create({
|
||||
'name': "tax_10",
|
||||
'amount_type': 'percent',
|
||||
'amount': 10.0,
|
||||
'tax_group_id': self.tax_group1.id,
|
||||
})
|
||||
|
||||
po = self._create_document_for_tax_totals_test([
|
||||
(100.0, tax_10),
|
||||
])
|
||||
po.button_confirm()
|
||||
po.order_line.qty_received = 1
|
||||
po.action_create_invoice()
|
||||
|
||||
invoice = po.invoice_ids
|
||||
invoice.invoice_date = '2020-01-01'
|
||||
invoice.action_post()
|
||||
|
||||
old_ammount = po.amount_total
|
||||
tax_10.active = False
|
||||
self.assertEqual(po.amount_total, old_ammount)
|
||||
Loading…
Add table
Add a link
Reference in a new issue