odoo/addons/sale/tests/test_sale_order.py

140 lines
8.1 KiB
Python
Raw Normal View History

# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo.exceptions import UserError, AccessError
from test_sale_common import TestSale
class TestSaleOrder(TestSale):
def test_sale_order(self):
""" Test the sale order flow (invoicing and quantity updates)
- Invoice repeatedly while varrying delivered quantities and check that invoice are always what we expect
"""
# DBO TODO: validate invoice and register payments
inv_obj = self.env['account.invoice']
so = self.env['sale.order'].create({
'partner_id': self.partner.id,
'partner_invoice_id': self.partner.id,
'partner_shipping_id': self.partner.id,
'order_line': [(0, 0, {'name': p.name, 'product_id': p.id, 'product_uom_qty': 2, 'product_uom': p.uom_id.id, 'price_unit': p.list_price}) for (_, p) in self.products.iteritems()],
'pricelist_id': self.env.ref('product.list0').id,
})
self.assertEqual(so.amount_total, sum([2 * p.list_price for (k, p) in self.products.iteritems()]), 'Sale: total amount is wrong')
# send quotation
so.force_quotation_send()
self.assertTrue(so.state == 'sent', 'Sale: state after sending is wrong')
# confirm quotation
so.action_confirm()
self.assertTrue(so.state == 'sale')
self.assertTrue(so.invoice_status == 'to invoice')
# create invoice: only 'invoice on order' products are invoiced
inv_id = so.action_invoice_create()
inv = inv_obj.browse(inv_id)
self.assertEqual(len(inv.invoice_line_ids), 2, 'Sale: invoice is missing lines')
self.assertEqual(inv.amount_total, sum([2 * p.list_price if p.invoice_policy == 'order' else 0 for (k, p) in self.products.iteritems()]), 'Sale: invoice total amount is wrong')
self.assertTrue(so.invoice_status == 'no', 'Sale: SO status after invoicing should be "nothing to invoice"')
self.assertTrue(len(so.invoice_ids) == 1, 'Sale: invoice is missing')
# deliver lines except 'time and material' then invoice again
for line in so.order_line:
line.qty_delivered = 2 if line.product_id.expense_policy=='no' else 0
self.assertTrue(so.invoice_status == 'to invoice', 'Sale: SO status after delivery should be "to invoice"')
inv_id = so.action_invoice_create()
inv = inv_obj.browse(inv_id)
self.assertEqual(len(inv.invoice_line_ids), 2, 'Sale: second invoice is missing lines')
self.assertEqual(inv.amount_total, sum([2 * p.list_price if p.invoice_policy == 'delivery' else 0 for (k, p) in self.products.iteritems()]), 'Sale: second invoice total amount is wrong')
self.assertTrue(so.invoice_status == 'invoiced', 'Sale: SO status after invoicing everything should be "invoiced"')
self.assertTrue(len(so.invoice_ids) == 2, 'Sale: invoice is missing')
# go over the sold quantity
for line in so.order_line:
if line.product_id == self.products['serv_order']:
line.qty_delivered = 10
self.assertTrue(so.invoice_status == 'upselling', 'Sale: SO status after increasing delivered qty higher than ordered qty should be "upselling"')
# upsell and invoice
for line in so.order_line:
if line.product_id == self.products['serv_order']:
line.product_uom_qty = 10
inv_id = so.action_invoice_create()
inv = inv_obj.browse(inv_id)
self.assertEqual(len(inv.invoice_line_ids), 1, 'Sale: third invoice is missing lines')
self.assertEqual(inv.amount_total, 8 * self.products['serv_order'].list_price, 'Sale: second invoice total amount is wrong')
self.assertTrue(so.invoice_status == 'invoiced', 'Sale: SO status after invoicing everything (including the upsel) should be "invoiced"')
def test_unlink_cancel(self):
""" Test deleting and cancelling sale orders depending on their state and on the user's rights """
so = self.env['sale.order'].create({
'partner_id': self.partner.id,
'partner_invoice_id': self.partner.id,
'partner_shipping_id': self.partner.id,
'order_line': [(0, 0, {'name': p.name, 'product_id': p.id, 'product_uom_qty': 2, 'product_uom': p.uom_id.id, 'price_unit': p.list_price}) for (_, p) in self.products.iteritems()],
'pricelist_id': self.env.ref('product.list0').id,
})
# SO in state 'draft' can be deleted
so_copy = so.copy()
with self.assertRaises(AccessError):
so_copy.sudo(self.user).unlink()
self.assertTrue(so_copy.sudo(self.manager).unlink(), 'Sale: deleting a quotation should be possible')
# SO in state 'cancel' can be deleted
so_copy = so.copy()
so_copy.action_confirm()
self.assertTrue(so_copy.state == 'sale', 'Sale: SO should be in state "sale"')
so_copy.action_cancel()
self.assertTrue(so_copy.state == 'cancel', 'Sale: SO should be in state "cancel"')
with self.assertRaises(AccessError):
so_copy.sudo(self.user).unlink()
self.assertTrue(so_copy.sudo(self.manager).unlink(), 'Sale: deleting a cancelled SO should be possible')
# SO in state 'sale' or 'done' cannot be deleted
so.action_confirm()
self.assertTrue(so.state == 'sale', 'Sale: SO should be in state "sale"')
with self.assertRaises(UserError):
so.sudo(self.manager).unlink()
so.action_done()
self.assertTrue(so.state == 'done', 'Sale: SO should be in state "done"')
with self.assertRaises(UserError):
so.sudo(self.manager).unlink()
def test_cost_invoicing(self):
""" Test confirming a vendor invoice to reinvoice cost on the so """
# force the pricelist to have the same currency as the company
self.env.ref('product.list0').currency_id = self.env.ref('base.main_company').currency_id
serv_cost = self.env.ref('product.service_cost_01')
prod_gap = self.env.ref('product.product_product_1')
so = self.env['sale.order'].create({
'partner_id': self.partner.id,
'partner_invoice_id': self.partner.id,
'partner_shipping_id': self.partner.id,
'order_line': [(0, 0, {'name': prod_gap.name, 'product_id': prod_gap.id, 'product_uom_qty': 2, 'product_uom': prod_gap.uom_id.id, 'price_unit': prod_gap.list_price})],
'pricelist_id': self.env.ref('product.list0').id,
})
so.action_confirm()
so._create_analytic_account()
inv_partner = self.env.ref('base.res_partner_2')
company = self.env.ref('base.main_company')
journal = self.env['account.journal'].create({'name': 'Purchase Journal - Test', 'code': 'STPJ', 'type': 'purchase', 'company_id': company.id})
account_payable = self.env['account.account'].create({'code': 'X1111', 'name': 'Sale - Test Payable Account', 'user_type_id': self.env.ref('account.data_account_type_payable').id, 'reconcile': True})
account_income = self.env['account.account'].create({'code': 'X1112', 'name': 'Sale - Test Account', 'user_type_id': self.env.ref('account.data_account_type_direct_costs').id})
invoice_vals = {
'name': '',
'type': 'in_invoice',
'partner_id': inv_partner.id,
'invoice_line_ids': [(0, 0, {'name': serv_cost.name, 'product_id': serv_cost.id, 'quantity': 2, 'uom_id': serv_cost.uom_id.id, 'price_unit': serv_cost.standard_price, 'account_analytic_id': so.project_id.id, 'account_id': account_income.id})],
'account_id': account_payable.id,
'journal_id': journal.id,
'currency_id': company.currency_id.id,
}
inv = self.env['account.invoice'].create(invoice_vals)
inv.action_invoice_open()
sol = so.order_line.filtered(lambda l: l.product_id == serv_cost)
self.assertTrue(sol, 'Sale: cost invoicing does not add lines when confirming vendor invoice')
self.assertEquals((sol.price_unit, sol.qty_delivered, sol.product_uom_qty, sol.qty_invoiced), (160, 2, 0, 0), 'Sale: line is wrong after confirming vendor invoice')