[TEST] point_of_sale: create one account move by session

We've adapted the tests to work with the new implementation
of payments and session closure. And we've added some new
ones to have a better coverage on point_of_sale module.

TASK-ID: 1862388

closes odoo/odoo#33578

Signed-off-by: pimodoo <pimodoo@users.noreply.github.com>
This commit is contained in:
Joseph Caburnay
2019-08-09 14:45:44 +00:00
committed by Pierre Masereel
parent b234e97f87
commit 8becb17b8a
13 changed files with 2433 additions and 77 deletions
+8
View File
@@ -5,3 +5,11 @@ from . import test_point_of_sale_flow
from . import test_frontend
from . import test_point_of_sale_ui
from . import test_anglo_saxon
from . import test_pos_setup
from . import test_pos_basic_config
from . import test_pos_products_with_tax
from . import test_pos_multiple_sale_accounts
from . import test_pos_multiple_receivable_accounts
from . import test_pos_other_currency_config
from . import test_pos_with_fiscal_position
from . import test_pos_stock_account
+382 -16
View File
@@ -1,8 +1,12 @@
# -*- coding: utf-8 -*-
from odoo.tests import common
from random import randint
from odoo import fields
from odoo.tests.common import TransactionCase, Form
from odoo.tools import float_is_zero
class TestPointOfSaleCommon(common.TransactionCase):
class TestPointOfSaleCommon(TransactionCase):
def setUp(self):
super(TestPointOfSaleCommon, self).setUp()
@@ -11,16 +15,16 @@ class TestPointOfSaleCommon(common.TransactionCase):
self.PosMakePayment = self.env['pos.make.payment']
self.PosOrder = self.env['pos.order']
self.PosSession = self.env['pos.session']
company = self.env.ref('base.main_company')
self.company_id = company.id
self.company = self.env.ref('base.main_company')
self.company_id = self.company.id
coa = self.env['account.chart.template'].search([
('currency_id', '=', company.currency_id.id),
('currency_id', '=', self.company.currency_id.id),
], limit=1)
test_sale_journal = self.env['account.journal'].create({'name': 'Sales Journal - Test',
'code': 'TSJ',
'type': 'sale',
'company_id': self.company_id})
company.write({'anglo_saxon_accounting': coa.use_anglo_saxon,
self.company.write({'anglo_saxon_accounting': coa.use_anglo_saxon,
'bank_account_code_prefix': coa.bank_account_code_prefix,
'cash_account_code_prefix': coa.cash_account_code_prefix,
'transfer_account_code_prefix': coa.transfer_account_code_prefix,
@@ -34,21 +38,19 @@ class TestPointOfSaleCommon(common.TransactionCase):
self.pos_config.write({
'journal_id': test_sale_journal.id,
'invoice_journal_id': test_sale_journal.id,
'journal_ids': [(0, 0, {'name': 'Cash Journal - Test',
'code': 'TSC',
'type': 'cash',
'company_id': self.company_id,
'journal_user': True})],
})
})
self.led_lamp = self.env.ref('point_of_sale.led_lamp')
self.whiteboard_pen = self.env.ref('point_of_sale.whiteboard_pen')
self.newspaper_rack = self.env.ref('point_of_sale.newspaper_rack')
# create a new session
self.pos_order_session0 = self.env['pos.session'].create({
'user_id': 1,
'config_id': self.pos_config.id
self.cash_payment_method = self.pos_config.payment_method_ids.filtered(lambda pm: pm.name == 'Cash')
self.bank_payment_method = self.pos_config.payment_method_ids.filtered(lambda pm: pm.name == 'Bank')
self.credit_payment_method = self.env['pos.payment.method'].create({
'name': 'Credit',
'receivable_account_id': self.company.account_default_pos_receivable_account_id.id,
'split_transactions': True,
})
self.pos_config.write({'payment_method_ids': [(4, self.credit_payment_method.id)]})
# create a VAT tax of 10%, included in the public price
Tax = self.env['account.tax']
@@ -85,3 +87,367 @@ class TestPointOfSaleCommon(common.TransactionCase):
# I assign those 5 percent taxes on the PCSC349 product as a sale taxes
self.product4.write(
{'taxes_id': [(6, 0, [account_tax_05_incl.id, account_tax_05_incl_chicago.id])]})
# Set account_id in the generated repartition lines. Automatically, nothing is set.
invoice_rep_lines = (account_tax_05_incl | account_tax_05_incl_chicago | account_tax_10_incl).mapped('invoice_repartition_line_ids')
refund_rep_lines = (account_tax_05_incl | account_tax_05_incl_chicago | account_tax_10_incl).mapped('refund_repartition_line_ids')
tax_received_account = self.company.account_sale_tax_id.mapped('invoice_repartition_line_ids.account_id')
(invoice_rep_lines | refund_rep_lines).write({'account_id': tax_received_account.id})
class TestPoSCommon(TransactionCase):
""" Set common values for different special test cases.
The idea is to set up common values here for the tests
and implement different special scenarios by inheriting
this class.
"""
def setUp(self):
super(TestPoSCommon, self).setUp()
self.pos_manager = self.env.ref('base.user_admin')
self.env = self.env(user=self.pos_manager)
# Set basic defaults
self.company = self.env.ref('base.main_company')
self.pos_sale_journal = self.env['account.journal'].create({
'type': 'sale',
'name': 'Point of Sale Test',
'code': 'POST',
'company_id': self.company.id,
'sequence': 20
})
self.invoice_journal = self.env['account.journal'].create({
'type': 'sale',
'name': 'Invoice Journal Test',
'code': 'INVT',
'company_id': self.company.id,
'sequence': 21
})
self.receivable_account = self.pos_manager.partner_id.property_account_receivable_id
self.tax_received_account = self.company.account_sale_tax_id.mapped('invoice_repartition_line_ids.account_id')
self.pos_receivable_account = self.company.account_default_pos_receivable_account_id
self.other_receivable_account = self.env['account.account'].create({
'name': 'Other Receivable',
'code': 'RCV00' ,
'user_type_id': self.env['account.account.type'].create({'name': 'RCV type', 'type': 'receivable'}).id,
'reconcile': True,
})
# company_currency can be different from `base.USD` depending on the localization installed
self.company_currency = self.company.currency_id
# other_currency is a currency different from the company_currency
# sometimes company_currency is different from USD, so handle appropriately.
self.other_currency = self.env.ref('base.EUR') if self.company_currency == self.env.ref('base.USD') else self.env.ref('base.USD')
self.currency_pricelist = self.env['product.pricelist'].create({
'name': 'Public Pricelist',
'currency_id': self.company_currency.id,
})
# Set Point of Sale configurations
# basic_config
# - derived from 'point_of_sale.pos_config_main' with added invoice_journal_id and credit payment method.
# other_currency_config
# - pos.config set to have currency different from company currency.
self.basic_config = self._create_basic_config()
self.other_currency_config = self._create_other_currency_config()
# Set product categories
# categ_basic
# - just the plain 'product.product_category_all'
# categ_anglo
# - product category with fifo and real_time valuations
# - used for checking anglo saxon accounting behavior
self.categ_basic = self.env.ref('product.product_category_all')
self.categ_anglo = self._create_categ_anglo()
# other basics
self.sale_account = self.categ_basic.property_account_income_categ_id
self.other_sale_account = self.env['account.account'].search([
('company_id', '=', self.company.id),
('user_type_id', '=', self.env.ref('account.data_account_type_revenue').id),
('id', '!=', self.sale_account.id)
], limit=1)
# Set customers
self.customer = self.env['res.partner'].create({'name': 'Test Customer'})
self.other_customer = self.env['res.partner'].create({'name': 'Other Customer', 'property_account_receivable_id': self.other_receivable_account.id})
# Set taxes
# self.taxes => dict
# keys: 'tax7', 'tax10'(price_include=True), 'tax_group_7_10'
self.taxes = self._create_taxes()
#####################
## private methods ##
#####################
def _create_basic_config(self):
new_config = Form(self.env['pos.config'])
new_config.name = 'PoS Shop Test'
new_config.module_account = True
new_config.invoice_journal_id = self.invoice_journal
new_config.journal_id = self.pos_sale_journal
new_config.available_pricelist_ids.clear()
new_config.available_pricelist_ids.add(self.currency_pricelist)
new_config.pricelist_id = self.currency_pricelist
config = new_config.save()
cash_journal = config.payment_method_ids.filtered(lambda pm: pm.is_cash_count)[:1].cash_journal_id
cash_split_pm = self.env['pos.payment.method'].create({
'name': 'Split (Cash) PM',
'receivable_account_id': self.pos_receivable_account.id,
'split_transactions': True,
'is_cash_count': True,
'cash_journal_id': cash_journal.id,
})
config.write({'payment_method_ids': [(4,cash_split_pm.id,0)]})
return config
def _create_other_currency_config(self):
(self.other_currency.rate_ids | self.company_currency.rate_ids).unlink()
self.env['res.currency.rate'].create({
'rate': 0.5,
'currency_id': self.other_currency.id,
})
other_cash_journal = self.env['account.journal'].create({
'name': 'Cash Other',
'type': 'cash',
'company_id': self.company.id,
'code': 'CSHO',
'sequence': 10,
'currency_id': self.other_currency.id
})
other_invoice_journal = self.env['account.journal'].create({
'name': 'Customer Invoice Other',
'type': 'sale',
'company_id': self.company.id,
'code': 'INVO',
'sequence': 11,
'currency_id': self.other_currency.id
})
other_sales_journal = self.env['account.journal'].create({
'name':'PoS Sale Other',
'type': 'sale',
'code': 'POSO',
'company_id': self.company.id,
'sequence': 12,
'currency_id': self.other_currency.id
})
other_pricelist = self.env['product.pricelist'].create({
'name': 'Public Pricelist Other',
'currency_id': self.other_currency.id,
})
other_cash_payment_method = self.env['pos.payment.method'].create({
'name': 'Cash Other',
'receivable_account_id': self.pos_receivable_account.id,
'is_cash_count': True,
'cash_journal_id': other_cash_journal.id,
})
other_bank_payment_method = self.env['pos.payment.method'].create({
'name': 'Bank Other',
'receivable_account_id': self.pos_receivable_account.id,
})
new_config = Form(self.env['pos.config'])
new_config.name = 'Shop Other'
new_config.invoice_journal_id = other_invoice_journal
new_config.journal_id = other_sales_journal
new_config.use_pricelist = True
new_config.available_pricelist_ids.clear()
new_config.available_pricelist_ids.add(other_pricelist)
new_config.pricelist_id = other_pricelist
new_config.payment_method_ids.clear()
new_config.payment_method_ids.add(other_cash_payment_method)
new_config.payment_method_ids.add(other_bank_payment_method)
config = new_config.save()
return config
def _create_categ_anglo(self):
return self.env['product.category'].create({
'name': 'Anglo',
'parent_id': False,
'property_cost_method': 'fifo',
'property_valuation': 'real_time',
})
def _create_taxes(self):
""" Create taxes
tax7: 7%, excluded in product price
tax10: 10%, included in product price
"""
tax7 = self.env['account.tax'].create({'name': 'Tax 7%', 'amount': 7})
tax10 = self.env['account.tax'].create({'name': 'Tax 10%', 'amount': 10, 'price_include': True, 'include_base_amount': False})
(tax7 | tax10).mapped('invoice_repartition_line_ids').write({'account_id': self.tax_received_account.id})
(tax7 | tax10).mapped('refund_repartition_line_ids').write({'account_id': self.tax_received_account.id})
tax_group_7_10 = tax7.copy()
with Form(tax_group_7_10) as tax:
tax.name = 'Tax 7+10%'
tax.amount_type = 'group'
tax.children_tax_ids.add(tax7)
tax.children_tax_ids.add(tax10)
return {
'tax7': tax7,
'tax10': tax10,
'tax_group_7_10': tax_group_7_10
}
####################
## public methods ##
####################
def create_random_uid(self):
return ('%05d-%03d-%04d' % (randint(1, 99999), randint(1, 999), randint(1, 9999)))
def create_ui_order_data(self, product_quantity_pairs, customer=False, is_invoiced=False, payments=None, uid=None):
""" Mocks the order_data generated by the pos ui.
This is useful in making orders in an open pos session without making tours.
Its functionality is tested in test_pos_create_ui_order_data.py.
Before use, make sure that self is set with:
1. pricelist -> the pricelist of the current session
2. currency -> currency of the current session
3. pos_session -> the current session, equivalent to config.current_session_id
4. cash_pm -> first cash payment method in the current session
5. config -> the active pos.config
The above values should be set when `self.open_new_session` is called.
:param list(tuple) product_quantity_pairs: pair of `ordered product` and `quantity`
:param list(tuple) payments: pair of `payment_method` and `amount`
"""
default_fiscal_position = self.config.default_fiscal_position_id
fiscal_position = customer.property_account_position_id if customer else default_fiscal_position
def create_order_line(product, quantity):
price_unit = self.pricelist.get_product_price(product, quantity, False)
tax_ids = fiscal_position.map_tax(product.taxes_id) if fiscal_position else product.taxes_id
tax_values = (
tax_ids.compute_all(price_unit, self.currency, quantity)
if tax_ids
else {
'total_excluded': price_unit * quantity,
'total_included': price_unit * quantity,
}
)
return (0, 0, {
'discount': 0,
'id': randint(1, 1000000),
'pack_lot_ids': [],
'price_unit': price_unit,
'product_id': product.id,
'price_subtotal': tax_values['total_excluded'],
'price_subtotal_incl': tax_values['total_included'],
'qty': quantity,
'tax_ids': [(6, 0, tax_ids.ids)]
})
def create_payment(payment_method, amount):
return (0, 0, {
'amount': amount,
'name': fields.Datetime.now(),
'payment_method_id': payment_method.id,
})
uid = uid or self.create_random_uid()
# 1. generate the order lines
order_lines = [create_order_line(product, quantity) for product, quantity in product_quantity_pairs]
# 2. generate the payments
total_amount_incl = sum(line[2]['price_subtotal_incl'] for line in order_lines)
if payments is None:
payments = [create_payment(self.cash_pm, total_amount_incl)]
else:
payments = [
create_payment(pm, amount)
for pm, amount in payments
]
# 3. complete the fields of the order_data
total_amount_base = sum(line[2]['price_subtotal'] for line in order_lines)
return {
'data': {
'amount_paid': sum(payment[2]['amount'] for payment in payments),
'amount_return': 0,
'amount_tax': total_amount_incl - total_amount_base,
'amount_total': total_amount_incl,
'creation_date': fields.Datetime.to_string(fields.Datetime.now()),
'fiscal_position_id': fiscal_position.id,
'pricelist_id': self.config.pricelist_id.id,
'lines': order_lines,
'name': 'Order %s' % uid,
'partner_id': customer and customer.id,
'pos_session_id': self.pos_session.id,
'sequence_number': 2,
'statement_ids': payments,
'uid': uid,
'user_id': self.pos_manager.id,
},
'id': uid,
'to_invoice': is_invoiced,
}
def create_product(self, name, category, lst_price, standard_price=None, tax_ids=None, sale_account=None):
product = self.env['product.product'].create({
'type': 'product',
'available_in_pos': True,
'taxes_id': [(5, 0, 0)] if not tax_ids else [(6, 0, tax_ids)],
'name': name,
'categ_id': category.id,
'lst_price': lst_price,
'standard_price': standard_price if standard_price else 0.0,
})
product.invoice_policy = 'delivery'
if sale_account:
product.property_account_income_id = sale_account
return product
def adjust_inventory(self, products, quantities):
""" Adjust inventory of the given products
"""
inventory = self.env['stock.inventory'].create({
'name': 'Inventory adjustment'
})
for product, qty in zip(products, quantities):
self.env['stock.inventory.line'].create({
'product_id': product.id,
'product_uom_id': self.env.ref('uom.product_uom_unit').id,
'inventory_id': inventory.id,
'product_qty': qty,
'location_id': self.env.ref('stock.stock_location_components').id,
})
inventory._action_start()
inventory.action_validate()
def open_new_session(self):
""" Used to open new pos session in each configuration.
- The idea is to properly set values that are constant
and commonly used in an open pos session.
- Calling this method is also a prerequisite for using
`self.create_ui_order_data` function.
Fields:
* config : the pos.config currently being used.
Its value is set at `self.setUp` of the inheriting
test class.
* session : the current_session_id of config
* currency : currency of the current pos.session
* pricelist : the default pricelist of the session
* cash_pm : cash payment method of the session
* bank_pm : bank payment method of the session
* cash_split_pm : credit payment method of the session
"""
self.config.open_session_cb()
self.pos_session = self.config.current_session_id
self.currency = self.pos_session.currency_id
self.pricelist = self.pos_session.config_id.pricelist_id
self.cash_pm = self.pos_session.payment_method_ids.filtered(lambda pm: pm.is_cash_count and not pm.split_transactions)[:1]
self.bank_pm = self.pos_session.payment_method_ids.filtered(lambda pm: not pm.is_cash_count and not pm.split_transactions)[:1]
self.cash_split_pm = self.pos_session.payment_method_ids.filtered(lambda pm: pm.is_cash_count and pm.split_transactions)[:1]
+22 -16
View File
@@ -48,6 +48,13 @@ class TestAngloSaxonCommon(common.TransactionCase):
self.sale_journal = self.env['account.journal'].create({'name': 'SALE journal', 'type': 'sale', 'code': 'INV00'})
self.pos_config.invoice_journal_id = self.sale_journal
self.pos_config.journal_ids = [self.cash_journal.id]
self.cash_payment_method = self.env['pos.payment.method'].create({
'name': 'Cash Test',
'is_cash_count': True,
'cash_journal_id': self.cash_journal.id,
'receivable_account_id': self.account.id,
})
self.pos_config.write({'payment_method_ids': [(6, 0, self.cash_payment_method.ids)]})
class TestAngloSaxonFlow(TestAngloSaxonCommon):
@@ -56,17 +63,8 @@ class TestAngloSaxonFlow(TestAngloSaxonCommon):
# This test will check that the correct journal entries are created when a product in real time valuation
# is sold in a company using anglo-saxon
self.pos_config.open_session_cb()
self.pos_config.current_session_id.write({'journal_ids': [(6, 0, [self.cash_journal.id])]})
current_session = self.pos_config.current_session_id
self.cash_journal.loss_account_id = self.account
self.pos_statement = self.Statement.create({
'balance_start': 0.0,
'balance_end_real': 0.0,
'date': time.strftime('%Y-%m-%d'),
'journal_id': self.cash_journal.id,
'company_id': self.company.id,
'name': 'pos session test',
})
self.pos_config.current_session_id.write({'statement_ids': [(6, 0, [self.pos_statement.id])]})
# I create a PoS order with 1 unit of New product at 450 EUR
self.pos_order_pos0 = self.PosOrder.create({
@@ -93,7 +91,7 @@ class TestAngloSaxonFlow(TestAngloSaxonCommon):
context_make_payment = {"active_ids": [self.pos_order_pos0.id], "active_id": self.pos_order_pos0.id}
self.pos_make_payment_0 = self.PosMakePayment.with_context(context_make_payment).create({
'amount': 450.0,
'journal_id': self.cash_journal.id,
'payment_method_id': self.cash_payment_method.id,
})
# I click on the validate button to register the payment.
@@ -110,10 +108,14 @@ class TestAngloSaxonFlow(TestAngloSaxonCommon):
current_session_id.action_pos_session_close()
self.assertEqual(current_session_id.state, 'closed', 'Check that session is closed')
# Check if there is account_move in the order.
# There shouldn't be because the order is not invoiced.
self.assertFalse(self.pos_order_pos0.account_move, 'There should be no invoice in the order.')
# I test that the generated journal entries are correct.
account_output = self.category.property_stock_account_output_categ_id
expense_account = self.category.property_account_expense_categ_id
aml = self.pos_order_pos0.account_move.line_ids
aml = current_session.move_id.line_ids
aml_output = aml.filtered(lambda l: l.account_id.id == account_output.id)
aml_expense = aml.filtered(lambda l: l.account_id.id == expense_account.id)
self.assertEqual(aml_output.credit, self.product.standard_price, "Cost of Good Sold entry missing or mismatching")
@@ -140,8 +142,8 @@ class TestAngloSaxonFlow(TestAngloSaxonCommon):
self.assertEqual(self.product.value_svl, 30, "Value should be (5*5 + 5*1) = 30")
self.assertEqual(self.product.quantity_svl, 10)
self.pos_config.open_session_cb()
self.pos_config.module_account = True
self.pos_config.open_session_cb()
pos_order_values = {
'company_id': self.company.id,
@@ -172,6 +174,7 @@ class TestAngloSaxonFlow(TestAngloSaxonCommon):
context_make_payment = {"active_ids": [pos_order_pos0.id], "active_id": pos_order_pos0.id}
self.pos_make_payment_0 = self.PosMakePayment.with_context(context_make_payment).create({
'amount': 7 * 450.0,
'payment_method_id': self.cash_payment_method.id,
})
# register the payment
@@ -183,8 +186,11 @@ class TestAngloSaxonFlow(TestAngloSaxonCommon):
current_session_id.action_pos_session_validate()
# check the anglo saxon move lines
line = pos_order_pos0.account_move.line_ids.filtered(lambda l: l.debit and l.account_id == self.category.property_account_expense_categ_id)
self.assertEqual(pos_order_pos0.account_move.journal_id, self.pos_config.journal_id)
# with uninvoiced orders, the account_move field of pos.order is empty.
# the accounting lines are in move_id of pos.session.
session_move = pos_order_pos0.session_id.move_id
line = session_move.line_ids.filtered(lambda l: l.debit and l.account_id == self.category.property_account_expense_categ_id)
self.assertEqual(session_move.journal_id, self.pos_config.journal_id)
self.assertEqual(line.debit, 27, 'As it is a fifo product, the move\'s value should be 5*5 + 2*1')
def test_fifo_valuation_with_invoice(self):
@@ -194,7 +200,7 @@ class TestAngloSaxonFlow(TestAngloSaxonCommon):
context_make_payment = {"active_ids": [pos_order_pos0.id], "active_id": pos_order_pos0.id}
self.pos_make_payment_0 = self.PosMakePayment.with_context(context_make_payment).create({
'amount': 7 * 450.0,
'journal_id': self.cash_journal.id,
'payment_method_id': self.cash_payment_method.id,
})
# register the payment
+13 -5
View File
@@ -27,6 +27,14 @@ class TestUi(odoo.tests.HttpCase):
'fields_id': field.id,
'value': 'account.account,' + str(account_receivable.id)})
cash_journal = journal_obj.create({
'name': 'Cash Test',
'type': 'cash',
'company_id': main_company.id,
'code': 'CSH',
'sequence': 10,
})
# test an extra price on an attribute
pear = env.ref('point_of_sale.whiteboard')
attribute_value = env['product.attribute.value'].create({
@@ -287,11 +295,11 @@ class TestUi(odoo.tests.HttpCase):
})],
'journal_id': test_sale_journal.id,
'invoice_journal_id': test_sale_journal.id,
'journal_ids': [(0, 0, {'name': 'Cash Journal - Test',
'code': 'TSC',
'type': 'cash',
'company_id': main_company.id,
'journal_user': True})],
'payment_method_ids': [(0, 0, { 'name': 'Cash',
'is_cash_count': True,
'cash_journal_id': cash_journal.id,
'receivable_account_id': account_receivable.id,
})],
'use_pricelist': True,
'pricelist_id': public_pricelist.id,
'available_pricelist_ids': [(4, pricelist.id) for pricelist in all_pricelists],
@@ -4,18 +4,13 @@ import time
import odoo
from odoo import fields
from odoo.tools import float_compare, mute_logger, test_reports
from odoo.tests.common import Form
from odoo.addons.point_of_sale.tests.common import TestPointOfSaleCommon
@odoo.tests.tagged('post_install', '-at_install')
class TestPointOfSaleFlow(TestPointOfSaleCommon):
def test_register_open(self):
"""
In order to test the Point of Sale module, I will open all cash registers through the wizard
"""
# open all statements/cash registers
self.env['pos.open.statement'].create({}).open_statement()
def test_order_refund(self):
self.pos_config.open_session_cb()
@@ -47,10 +42,18 @@ class TestPointOfSaleFlow(TestPointOfSaleCommon):
})],
'amount_total': 1710.0,
'amount_tax': 0.0,
'amount_paid': 1710.0,
'amount_paid': 0.0,
'amount_return': 0.0,
})
payment_context = {"active_ids": order.ids, "active_id": order.id}
order_payment = self.PosMakePayment.with_context(**payment_context).create({
'amount': order.amount_total,
'payment_method_id': self.cash_payment_method.id
})
order_payment.with_context(**payment_context).check()
self.assertAlmostEqual(order.amount_total, order.amount_paid, msg='Order should be fully paid.')
# I create a refund
refund_action = order.refund()
refund = self.PosOrder.browse(refund_action['res_id'])
@@ -60,13 +63,18 @@ class TestPointOfSaleFlow(TestPointOfSaleCommon):
payment_context = {"active_ids": refund.ids, "active_id": refund.id}
refund_payment = self.PosMakePayment.with_context(**payment_context).create({
'amount': refund.amount_total
'amount': refund.amount_total,
'payment_method_id': self.cash_payment_method.id,
})
# I click on the validate button to register the payment.
refund_payment.with_context(**payment_context).check()
self.assertEqual(refund.state, 'paid', "The refund is not marked as paid")
self.assertTrue(refund.payment_ids.payment_method_id.is_cash_count, msg='There should only be one payment and paid in cash.')
current_session.action_pos_session_closing_control()
self.assertEqual(current_session.state, 'closed', msg='State of current session should be closed.')
def test_order_to_picking(self):
"""
@@ -287,7 +295,6 @@ class TestPointOfSaleFlow(TestPointOfSaleCommon):
# I close the session to generate the journal entries
self.pos_config.current_session_id.action_pos_session_closing_control()
def test_order_to_invoice(self):
def compute_tax(product, price, qty=1, taxes=None):
@@ -350,13 +357,16 @@ class TestPointOfSaleFlow(TestPointOfSaleCommon):
# I generate an invoice from the order
res = self.pos_order_pos1.action_pos_order_invoice()
self.assertIn('res_id', res, "No invoice created")
self.assertIn('res_id', res, "Invoice should be created")
# I test that the total of the attached invoice is correct
invoice = self.env['account.move'].browse(res['res_id'])
self.assertAlmostEqual(
invoice.amount_total, self.pos_order_pos1.amount_total, places=2, msg="Invoice not correct")
# I close the session to generate the journal entries
current_session.action_pos_session_closing_control()
"""In order to test the reports on Bank Statement defined in point_of_sale module, I create a bank statement line, confirm it and print the reports"""
# I select the period and journal for the bank statement
@@ -450,11 +460,9 @@ class TestPointOfSaleFlow(TestPointOfSaleCommon):
'sequence_number': 2,
'statement_ids': [[0,
0,
{'account_id': self.env.user.partner_id.property_account_receivable_id.id,
'amount': untax + atax,
'journal_id': self.pos_config.journal_ids[0].id,
{'amount': untax + atax,
'name': fields.Datetime.now(),
'statement_id': current_session.statement_ids[0].id}]],
'payment_method_id': self.cash_payment_method.id}]],
'uid': '00042-003-0014',
'user_id': self.env.uid},
'id': '00042-003-0014',
@@ -486,11 +494,9 @@ class TestPointOfSaleFlow(TestPointOfSaleCommon):
'sequence_number': self.pos_config.journal_id.id,
'statement_ids': [[0,
0,
{'account_id': self.env.user.partner_id.property_account_receivable_id.id,
'amount': untax + atax,
'journal_id': self.pos_config.journal_ids[0].id,
{'amount': untax + atax,
'name': fields.Datetime.now(),
'statement_id': current_session.statement_ids[0].id}]],
'payment_method_id': self.credit_payment_method.id}]],
'uid': '00043-003-0014',
'user_id': self.env.uid},
'id': '00043-003-0014',
@@ -522,11 +528,9 @@ class TestPointOfSaleFlow(TestPointOfSaleCommon):
'sequence_number': self.pos_config.journal_id.id,
'statement_ids': [[0,
0,
{'account_id': self.env.user.partner_id.property_account_receivable_id.id,
'amount': untax + atax,
'journal_id': self.pos_config.journal_ids[0].id,
{'amount': untax + atax,
'name': fields.Datetime.now(),
'statement_id': current_session.statement_ids[0].id}]],
'payment_method_id': self.bank_payment_method.id}]],
'uid': '00044-003-0014',
'user_id': self.env.uid},
'id': '00044-003-0014',
@@ -588,19 +592,39 @@ class TestPointOfSaleFlow(TestPointOfSaleCommon):
untax = res['total_excluded']
return untax, sum(tax.get('amount', 0.0) for tax in res['taxes'])
# make a config that has currency different from the company
eur_pricelist = self.partner1.property_product_pricelist.copy(default={'currency_id': self.env.ref('base.EUR').id})
sale_journal = self.env['account.journal'].create({
'name': 'PoS Sale EUR',
'type': 'sale',
'code': 'POSE',
'company_id': self.company.id,
'sequence': 12,
'currency_id': self.env.ref('base.EUR').id
})
eur_config = self.pos_config.create({
'name': 'Shop EUR Test',
'module_account': False,
'journal_id': sale_journal.id,
'use_pricelist': True,
'available_pricelist_ids': [(6, 0, eur_pricelist.ids)],
'pricelist_id': eur_pricelist.id,
'payment_method_ids': [(6, 0, self.bank_payment_method.ids)]
})
# I click on create a new session button
self.pos_config.open_session_cb()
current_session = self.pos_config.current_session_id
eur_config.open_session_cb()
current_session = eur_config.current_session_id
# I create a PoS order with 2 units of PCSC234 at 450 EUR (Tax Incl)
# and 3 units of PCSC349 at 300 EUR. (Tax Excl)
untax1, atax1 = compute_tax(self.product3, 450*0.95, 2)
untax2, atax2 = compute_tax(self.product4, 300*0.95, 3)
untax1, atax1 = compute_tax(self.product3, 450, 2)
untax2, atax2 = compute_tax(self.product4, 300, 3)
self.pos_order_pos0 = self.PosOrder.create({
'company_id': self.company_id,
'session_id': current_session.id,
'pricelist_id': self.partner1.property_product_pricelist.copy(default={'currency_id': self.env.ref('base.EUR').id}).id,
'pricelist_id': eur_pricelist.id,
'partner_id': self.partner1.id,
'lines': [(0, 0, {
'name': "OL/0001",
@@ -630,14 +654,15 @@ class TestPointOfSaleFlow(TestPointOfSaleCommon):
# I check that the total of the order is now equal to (450*2 +
# 300*3*1.05)*0.95
self.assertLess(
abs(self.pos_order_pos0.amount_total - (450 * 2 + 300 * 3 * 1.05) * 0.95),
abs(self.pos_order_pos0.amount_total - (450 * 2 + 300 * 3 * 1.05)),
0.01, 'The order has a wrong total including tax and discounts')
# I click on the "Make Payment" wizard to pay the PoS order with a
# partial amount of 100.0 EUR
context_make_payment = {"active_ids": [self.pos_order_pos0.id], "active_id": self.pos_order_pos0.id}
self.pos_make_payment_0 = self.PosMakePayment.with_context(context_make_payment).create({
'amount': 100.0
'amount': 100.0,
'payment_method_id': self.bank_payment_method.id,
})
# I click on the validate button to register the payment.
@@ -652,14 +677,15 @@ class TestPointOfSaleFlow(TestPointOfSaleCommon):
defs = self.pos_make_payment_0.with_context({'active_id': self.pos_order_pos0.id}).default_get(['amount'])
self.assertLess(
abs(defs['amount'] - ((450 * 2 + 300 * 3 * 1.05) * 0.95 - 100.0)), 0.01, "The remaining balance is incorrect.")
abs(defs['amount'] - ((450 * 2 + 300 * 3 * 1.05) - 100.0)), 0.01, "The remaining balance is incorrect.")
#'I pay the remaining balance.
context_make_payment = {
"active_ids": [self.pos_order_pos0.id], "active_id": self.pos_order_pos0.id}
self.pos_make_payment_1 = self.PosMakePayment.with_context(context_make_payment).create({
'amount': (450 * 2 + 300 * 3 * 1.05) * 0.95 - 100.0
'amount': (450 * 2 + 300 * 3 * 1.05) - 100.0,
'payment_method_id': self.bank_payment_method.id,
})
# I click on the validate button to register the payment.
@@ -669,20 +695,20 @@ class TestPointOfSaleFlow(TestPointOfSaleCommon):
self.assertEqual(self.pos_order_pos0.state, 'paid', 'Order should be in paid state.')
# I generate the journal entries
self.pos_order_pos0._create_account_move_line()
current_session.action_pos_session_validate()
# I test that the generated journal entry is attached to the PoS order
self.assertTrue(self.pos_order_pos0.account_move, "Journal entry has not been attached to Pos order.")
self.assertTrue(current_session.move_id, "Journal entry should have been attached to the session.")
# Check the amounts
debit_lines = self.pos_order_pos0.account_move.mapped('line_ids.debit')
credit_lines = self.pos_order_pos0.account_move.mapped('line_ids.credit')
amount_currency_lines = self.pos_order_pos0.account_move.mapped('line_ids.amount_currency')
for a, b in zip(sorted(debit_lines), [0.0, 0.0, 0.0, 0.0, 879.55]):
debit_lines = current_session.move_id.mapped('line_ids.debit')
credit_lines = current_session.move_id.mapped('line_ids.credit')
amount_currency_lines = current_session.move_id.mapped('line_ids.amount_currency')
for a, b in zip(sorted(debit_lines), [0.0, 0.0, 0.0, 0.0, 922.5]):
self.assertAlmostEqual(a, b)
for a, b in zip(sorted(credit_lines), [0.0, 22.5, 40.91, 388.64, 427.5]):
for a, b in zip(sorted(credit_lines), [0.0, 22.5, 40.91, 409.09, 450]):
self.assertAlmostEqual(a, b)
for a, b in zip(sorted(amount_currency_lines), [-855.0, -777.27, -81.82, -45.0, 1752.75]):
for a, b in zip(sorted(amount_currency_lines), [-900, -818.18, -81.82, -45, 1845]):
self.assertAlmostEqual(a, b)
def test_order_to_invoice_no_tax(self):
@@ -731,7 +757,7 @@ class TestPointOfSaleFlow(TestPointOfSaleCommon):
# I check that the order is marked as paid and there is no invoice
# attached to it
self.assertEqual(self.pos_order_pos1.state, 'paid', "Order should be in paid state.")
self.assertFalse(self.pos_order_pos1.account_move, 'Invoice should not be attached to order.')
self.assertFalse(self.pos_order_pos1.account_move, 'Invoice should not be attached to order yet.')
# I generate an invoice from the order
res = self.pos_order_pos1.action_pos_order_invoice()
@@ -744,3 +770,5 @@ class TestPointOfSaleFlow(TestPointOfSaleCommon):
for iline in invoice.invoice_line_ids:
self.assertFalse(iline.tax_ids)
self.pos_config.current_session_id.action_pos_session_closing_control()
@@ -0,0 +1,423 @@
import odoo
from odoo.addons.point_of_sale.tests.common import TestPoSCommon
@odoo.tests.tagged('post_install', '-at_install')
class TestPoSBasicConfig(TestPoSCommon):
""" Test PoS with basic configuration
The tests contain base scenarios in using pos.
More specialized cases are tested in other tests.
"""
def setUp(self):
super(TestPoSBasicConfig, self).setUp()
self.config = self.basic_config
self.product1 = self.create_product('Product 1', self.categ_basic, 10.0, 5)
self.product2 = self.create_product('Product 2', self.categ_basic, 20.0, 10)
self.product3 = self.create_product('Product 3', self.categ_basic, 30.0, 15)
self.adjust_inventory([self.product1, self.product2, self.product3], [100, 50, 50])
def test_orders_no_invoiced(self):
""" Test for orders without invoice
3 orders
- first 2 orders with cash payment
- last order with bank payment
Orders
======
+---------+----------+-----------+----------+-----+-------+
| order | payments | invoiced? | product | qty | total |
+---------+----------+-----------+----------+-----+-------+
| order 1 | cash | no | product1 | 10 | 100 |
| | | | product2 | 5 | 100 |
+---------+----------+-----------+----------+-----+-------+
| order 2 | cash | no | product2 | 7 | 140 |
| | | | product3 | 1 | 30 |
+---------+----------+-----------+----------+-----+-------+
| order 3 | bank | no | product1 | 1 | 10 |
| | | | product2 | 3 | 60 |
| | | | product3 | 5 | 150 |
+---------+----------+-----------+----------+-----+-------+
Expected Result
===============
+---------------------+---------+
| account | balance |
+---------------------+---------+
| sale | -590 |
| pos receivable cash | 370 |
| pos receivable bank | 220 |
+---------------------+---------+
| Total balance | 0.0 |
+---------------------+---------+
"""
start_qty_available = {
self.product1: self.product1.qty_available,
self.product2: self.product2.qty_available,
self.product3: self.product3.qty_available,
}
self.open_new_session()
# create orders
orders = []
orders.append(self.create_ui_order_data([(self.product1, 10), (self.product2, 5)]))
orders.append(self.create_ui_order_data([(self.product2, 7), (self.product3, 1)]))
orders.append(self.create_ui_order_data(
[(self.product1, 1), (self.product3, 5), (self.product2, 3)],
payments=[(self.bank_pm, 220)]
))
# sync orders
order = self.env['pos.order'].create_from_ui(orders)
# check values before closing the session
self.assertEqual(3, self.pos_session.order_count)
orders_total = sum(order.amount_total for order in self.pos_session.order_ids)
self.assertAlmostEqual(orders_total, self.pos_session.total_payments_amount, msg='Total order amount should be equal to the total payment amount.')
# check product qty_available after syncing the order
self.assertEqual(
self.product1.qty_available + 11,
start_qty_available[self.product1],
)
self.assertEqual(
self.product2.qty_available + 15,
start_qty_available[self.product2],
)
self.assertEqual(
self.product3.qty_available + 6,
start_qty_available[self.product3],
)
# picking and stock moves should be in done state
for order in self.pos_session.order_ids:
self.assertEqual(
order.picking_id.state,
'done',
'Picking should be in done state.'
)
move_lines = order.picking_id.move_lines
self.assertEqual(
move_lines.mapped('state'),
['done'] * len(move_lines),
'Move Lines should be in done state.'
)
# close the session
self.pos_session.action_pos_session_validate()
# check accounting values after the session is closed
session_move = self.pos_session.move_id
sales_line = session_move.line_ids.filtered(lambda line: line.account_id == self.sale_account)
self.assertAlmostEqual(sales_line.balance, -590.0, msg='Sales line balance should be equal to total orders amount.')
receivable_line_bank = session_move.line_ids.filtered(lambda line: self.bank_pm.name in line.name)
self.assertAlmostEqual(receivable_line_bank.balance, 220.0, msg='Bank receivable should be equal to the total bank payments.')
receivable_line_cash = session_move.line_ids.filtered(lambda line: self.cash_pm.name in line.name)
self.assertAlmostEqual(receivable_line_cash.balance, 370.0, msg='Cash receivable should be equal to the total cash payments.')
self.assertTrue(receivable_line_cash.full_reconcile_id, msg='Cash receivable line should be fully-reconciled.')
def test_orders_with_invoiced(self):
""" Test for orders: one with invoice
3 orders
- order 1, paid by cash
- order 2, paid by bank
- order 3, paid by bank, invoiced
Orders
======
+---------+----------+---------------+----------+-----+-------+
| order | payments | invoiced? | product | qty | total |
+---------+----------+---------------+----------+-----+-------+
| order 1 | cash | no | product1 | 6 | 60 |
| | | | product2 | 3 | 60 |
| | | | product3 | 1 | 30 |
+---------+----------+---------------+----------+-----+-------+
| order 2 | bank | no | product1 | 1 | 10 |
| | | | product2 | 20 | 400 |
+---------+----------+---------------+----------+-----+-------+
| order 3 | bank | yes, customer | product1 | 10 | 100 |
| | | | product3 | 1 | 30 |
+---------+----------+---------------+----------+-----+-------+
Expected Result
===============
+---------------------+---------+
| account | balance |
+---------------------+---------+
| sale | -560 |
| pos receivable cash | 150 |
| pos receivable bank | 540 |
| receivable | -130 |
+---------------------+---------+
| Total balance | 0.0 |
+---------------------+---------+
"""
start_qty_available = {
self.product1: self.product1.qty_available,
self.product2: self.product2.qty_available,
self.product3: self.product3.qty_available,
}
self.open_new_session()
# create orders
orders = []
orders.append(self.create_ui_order_data(
[(self.product3, 1), (self.product1, 6), (self.product2, 3)],
payments=[(self.cash_pm, 150)],
))
orders.append(self.create_ui_order_data(
[(self.product2, 20), (self.product1, 1)],
payments=[(self.bank_pm, 410)],
))
orders.append(self.create_ui_order_data(
[(self.product1, 10), (self.product3, 1)],
payments=[(self.bank_pm, 130)],
customer=self.customer,
is_invoiced=True,
))
# sync orders
order = self.env['pos.order'].create_from_ui(orders)
# check values before closing the session
self.assertEqual(3, self.pos_session.order_count)
orders_total = sum(order.amount_total for order in self.pos_session.order_ids)
self.assertAlmostEqual(orders_total, self.pos_session.total_payments_amount, msg='Total order amount should be equal to the total payment amount.')
# check product qty_available after syncing the order
self.assertEqual(
self.product1.qty_available + 17,
start_qty_available[self.product1],
)
self.assertEqual(
self.product2.qty_available + 23,
start_qty_available[self.product2],
)
self.assertEqual(
self.product3.qty_available + 2,
start_qty_available[self.product3],
)
# picking and stock moves should be in done state
# no exception for invoiced orders
for order in self.pos_session.order_ids:
self.assertEqual(
order.picking_id.state,
'done',
'Picking should be in done state.'
)
move_lines = order.picking_id.move_lines
self.assertEqual(
move_lines.mapped('state'),
['done'] * len(move_lines),
'Move Lines should be in done state.'
)
# check account move in the invoiced order
invoiced_order = self.pos_session.order_ids.filtered(lambda order: order.account_move)
self.assertEqual(1, len(invoiced_order), 'Only one order is invoiced in this test.')
invoice = invoiced_order.account_move
self.assertAlmostEqual(invoice.amount_total, 130, msg='Amount total should be 130. Product is untaxed.')
invoice_receivable_line = invoice.line_ids.filtered(lambda line: line.account_id == self.receivable_account)
# close the session
self.pos_session.action_pos_session_validate()
# check values after the session is closed
session_move = self.pos_session.move_id
sales_line = session_move.line_ids.filtered(lambda line: line.account_id == self.sale_account)
self.assertAlmostEqual(sales_line.balance, -(orders_total - invoice.amount_total), msg='Sales line should be total order minus invoiced order.')
pos_receivable_line_bank = session_move.line_ids.filtered(
lambda line: self.bank_pm.name in line.name and line.account_id == self.bank_pm.receivable_account_id
)
self.assertAlmostEqual(pos_receivable_line_bank.balance, 540.0, msg='Bank receivable should be equal to the total bank payments.')
pos_receivable_line_cash = session_move.line_ids.filtered(
lambda line: self.cash_pm.name in line.name and line.account_id == self.bank_pm.receivable_account_id
)
self.assertAlmostEqual(pos_receivable_line_cash.balance, 150.0, msg='Cash receivable should be equal to the total cash payments.')
receivable_line = session_move.line_ids.filtered(lambda line: line.account_id == self.receivable_account)
self.assertAlmostEqual(receivable_line.balance, -invoice.amount_total)
# cash receivable and invoice receivable lines should be fully reconciled
self.assertTrue(pos_receivable_line_cash.full_reconcile_id)
self.assertTrue(receivable_line.full_reconcile_id)
# matching number of the receivable lines should be the same
self.assertEqual(receivable_line.full_reconcile_id, invoice_receivable_line.full_reconcile_id)
def test_return_order(self):
""" Test return order
2 orders
- 2nd order is returned
Orders
======
+------------------+----------+-----------+----------+-----+-------+
| order | payments | invoiced? | product | qty | total |
+------------------+----------+-----------+----------+-----+-------+
| order 1 | bank | no | product1 | 1 | 10 |
| | | | product2 | 5 | 100 |
+------------------+----------+-----------+----------+-----+-------+
| order 2 | cash | no | product1 | 3 | 30 |
| | | | product2 | 2 | 40 |
| | | | product3 | 1 | 30 |
+------------------+----------+-----------+----------+-----+-------+
| order 3 (return) | cash | no | product1 | -3 | -30 |
| | | | product2 | -2 | -40 |
| | | | product3 | -1 | -30 |
+------------------+----------+-----------+----------+-----+-------+
Expected Result
===============
+---------------------+---------+
| account | balance |
+---------------------+---------+
| sale (sales) | -210 |
| sale (refund) | 100 |
| pos receivable bank | 110 |
+---------------------+---------+
| Total balance | 0.0 |
+---------------------+---------+
"""
start_qty_available = {
self.product1: self.product1.qty_available,
self.product2: self.product2.qty_available,
self.product3: self.product3.qty_available,
}
self.open_new_session()
# create orders
orders = []
orders.append(self.create_ui_order_data(
[(self.product1, 1), (self.product2, 5)],
payments=[(self.bank_pm, 110)]
))
orders.append(self.create_ui_order_data(
[(self.product1, 3), (self.product2, 2), (self.product3, 1)],
payments=[(self.cash_pm, 100)],
uid='12345-123-1234'
))
# sync orders
order = self.env['pos.order'].create_from_ui(orders)
# check values before closing the session
self.assertEqual(2, self.pos_session.order_count)
orders_total = sum(order.amount_total for order in self.pos_session.order_ids)
self.assertAlmostEqual(orders_total, self.pos_session.total_payments_amount, msg='Total order amount should be equal to the total payment amount.')
# return order
order_to_return = self.pos_session.order_ids.filtered(lambda order: '12345-123-1234' in order.pos_reference)
order_to_return.refund()
refund_order = self.pos_session.order_ids.filtered(lambda order: order.state == 'draft')
# check if amount to pay
self.assertAlmostEqual(refund_order.amount_total - refund_order.amount_paid, -100)
# pay the refund
context_make_payment = {"active_ids": [refund_order.id], "active_id": refund_order.id}
make_payment = self.env['pos.make.payment'].with_context(context_make_payment).create({
'payment_method_id': self.cash_pm.id,
'amount': -100,
})
make_payment.check()
self.assertEqual(refund_order.state, 'paid', 'Payment is registered, order should be paid.')
self.assertAlmostEqual(refund_order.amount_paid, -100.0, msg='Amount paid for return order should be negative.')
# check product qty_available after syncing the order
self.assertEqual(
self.product1.qty_available + 1,
start_qty_available[self.product1],
)
self.assertEqual(
self.product2.qty_available + 5,
start_qty_available[self.product2],
)
self.assertEqual(
self.product3.qty_available,
start_qty_available[self.product3],
)
# picking and stock moves should be in done state
# no exception of return orders
for order in self.pos_session.order_ids:
self.assertEqual(
order.picking_id.state,
'done',
'Picking should be in done state.'
)
move_lines = order.picking_id.move_lines
self.assertEqual(
move_lines.mapped('state'),
['done'] * len(move_lines),
'Move Lines should be in done state.'
)
# close the session
self.pos_session.action_pos_session_validate()
# check values after the session is closed
session_move = self.pos_session.move_id
sale_lines = session_move.line_ids.filtered(lambda line: line.account_id == self.sale_account)
self.assertAlmostEqual(len(sale_lines), 2, 'There should be lines for both sales and refund.')
self.assertAlmostEqual(sum(sale_lines.mapped('balance')), -110.0)
receivable_line_bank = session_move.line_ids.filtered(lambda line: self.bank_pm.name in line.name)
self.assertAlmostEqual(receivable_line_bank.balance, 110.0)
# net cash in the session is zero, thus, there should be no receivable cash line.
receivable_line_cash = session_move.line_ids.filtered(lambda line: self.cash_pm.name in line.name)
self.assertFalse(receivable_line_cash, 'There should be no receivable cash line because both the order and return order are paid with cash - they cancelled.')
def test_split_cash_payments(self):
self.open_new_session()
# create orders
orders = []
orders.append(self.create_ui_order_data(
[(self.product1, 10), (self.product2, 5)],
payments=[(self.cash_split_pm, 100), (self.bank_pm, 100)]
))
orders.append(self.create_ui_order_data(
[(self.product2, 7), (self.product3, 1)],
payments=[(self.cash_split_pm, 70), (self.bank_pm, 100)]
))
orders.append(self.create_ui_order_data(
[(self.product1, 1), (self.product3, 5), (self.product2, 3)],
payments=[(self.cash_split_pm, 120), (self.bank_pm, 100)]
))
# sync orders
order = self.env['pos.order'].create_from_ui(orders)
# close the session
self.pos_session.action_pos_session_validate()
# check values after the session is closed
account_move = self.pos_session.move_id
bank_receivable_lines = account_move.line_ids.filtered(lambda line: self.bank_pm.name in line.name)
self.assertEqual(len(bank_receivable_lines), 1, msg='Bank receivable lines should only have one line because it\'s supposed to be combined.')
self.assertAlmostEqual(bank_receivable_lines.balance, 300.0, msg='Bank receivable should be equal to the total bank payments.')
cash_receivable_lines = account_move.line_ids.filtered(lambda line: self.cash_split_pm.name in line.name)
self.assertEqual(len(cash_receivable_lines), 3, msg='There should be a number of cash receivable lines because the cash_pm is `split_transactions`.')
self.assertAlmostEqual(sum(cash_receivable_lines.mapped('balance')), 290, msg='Total cash receivable balance should be equal to the total cash payments.')
for line in cash_receivable_lines:
self.assertTrue(line.full_reconcile_id, msg='Each cash receivable line should be fully-reconciled.')
@@ -0,0 +1,199 @@
import odoo
from odoo.addons.point_of_sale.tests.common import TestPoSCommon
@odoo.tests.tagged('post_install', '-at_install')
class TestPoSMultipleReceivableAccounts(TestPoSCommon):
""" Test for invoiced orders with customers having receivable account different from default
Thus, for this test, there are two receivable accounts involved and are set in the
customers.
self.customer -> self.receivable_account
self.other_customer -> self.other_receivable_account
NOTE That both receivable accounts above are different from the pos receivable account.
"""
def setUp(self):
super(TestPoSMultipleReceivableAccounts, self).setUp()
self.config = self.basic_config
self.product1 = self.create_product(
'Product 1',
self.categ_basic,
lst_price=10.99,
standard_price=5.0,
tax_ids=self.taxes['tax7'].ids,
)
self.product2 = self.create_product(
'Product 2',
self.categ_basic,
lst_price=19.99,
standard_price=10.0,
tax_ids=self.taxes['tax10'].ids,
sale_account=self.other_sale_account,
)
self.product3 = self.create_product(
'Product 3',
self.categ_basic,
lst_price=30.99,
standard_price=15.0,
tax_ids=self.taxes['tax_group_7_10'].ids,
)
self.adjust_inventory([self.product1, self.product2, self.product3], [100, 50, 50])
def test_01_invoiced_order_from_other_customer(self):
"""
Orders
======
+---------+----------+-----------+----------+-----+---------+--------------------------+--------+
| order | payments | invoiced? | product | qty | untaxed | tax | total |
+---------+----------+-----------+----------+-----+---------+--------------------------+--------+
| order 1 | cash | no | product1 | 10 | 109.9 | 7.69 [7%] | 117.59 |
| | | | product2 | 10 | 181.73 | 18.17 [10%] | 199.9 |
| | | | product3 | 10 | 281.73 | 19.72 [7%] + 28.17 [10%] | 329.62 |
+---------+----------+-----------+----------+-----+---------+--------------------------+--------+
| order 2 | bank | no | product1 | 5 | 54.95 | 3.85 [7%] | 58.80 |
| | | | product2 | 5 | 90.86 | 9.09 [10%] | 99.95 |
+---------+----------+-----------+----------+-----+---------+--------------------------+--------+
| order 3 | bank | yes | product2 | 5 | 90.86 | 9.09 [10%] | 99.95 |
| | | | product3 | 5 | 140.86 | 9.86 [7%] + 14.09 [10%] | 164.81 |
+---------+----------+-----------+----------+-----+---------+--------------------------+--------+
Expected Result
===============
+---------------------+---------+
| account | balance |
+---------------------+---------+
| sale_account | -164.85 |
| sale_account | -281.73 |
| other_sale_account | -272.59 |
| tax 7% | -31.26 |
| tax 10% | -55.43 |
| pos receivable cash | 647.11 |
| pos receivable bank | 423.51 |
| other receivable | -264.76 |
+---------------------+---------+
| Total balance | 0.00 |
+---------------------+---------+
"""
self.open_new_session()
# create orders
orders = []
orders.append(self.create_ui_order_data([(self.product1, 10), (self.product2, 10), (self.product3, 10)]))
orders.append(self.create_ui_order_data(
[(self.product1, 5), (self.product2, 5)],
payments=[(self.bank_pm, 158.75)],
))
orders.append(self.create_ui_order_data(
[(self.product2, 5), (self.product3, 5)],
payments=[(self.bank_pm, 264.76)],
customer=self.other_customer,
is_invoiced=True,
uid='09876-098-0987',
))
# sync orders
order = self.env['pos.order'].create_from_ui(orders)
# check values before closing the session
self.assertEqual(3, self.pos_session.order_count)
orders_total = sum(order.amount_total for order in self.pos_session.order_ids)
self.assertAlmostEqual(orders_total, self.pos_session.total_payments_amount, msg='Total order amount should be equal to the total payment amount.')
# check if there is one invoiced order
self.assertEqual(len(self.pos_session.order_ids.filtered(lambda order: order.state == 'invoiced')), 1, 'There should only be one invoiced order.')
# close the session
self.pos_session.action_pos_session_validate()
session_move = self.pos_session.move_id
# There should be no line corresponding the original receivable account
# But there should be a line for other_receivable_account because
# that is the property_account_receivable_id of the customer
# of the invoiced order.
receivable_line = session_move.line_ids.filtered(lambda line: line.account_id == self.receivable_account)
self.assertFalse(receivable_line, msg='There should be no move line for the original receivable account.')
other_receivable_line = session_move.line_ids.filtered(lambda line: line.account_id == self.other_receivable_account)
self.assertAlmostEqual(other_receivable_line.balance, -264.76)
def test_02_all_orders_invoiced_mixed_customers(self):
"""
Orders
======
+---------+----------+---------------------+----------+-----+---------+--------------------------+--------+
| order | payments | invoiced? | product | qty | untaxed | tax | total |
+---------+----------+---------------------+----------+-----+---------+--------------------------+--------+
| order 1 | cash | yes, other_customer | product1 | 10 | 109.90 | 7.69 [7%] | 117.59 |
| | | | product2 | 10 | 181.73 | 18.17 [10%] | 199.90 |
| | | | product3 | 10 | 281.73 | 19.72 [7%] + 28.17 [10%] | 329.62 |
+---------+----------+---------------------+----------+-----+---------+--------------------------+--------+
| order 2 | bank | yes, customer | product1 | 5 | 54.95 | 3.85 [7%] | 58.80 |
| | | | product2 | 5 | 90.86 | 9.09 [10%] | 99.95 |
+---------+----------+---------------------+----------+-----+---------+--------------------------+--------+
| order 3 | bank | yes, other customer | product2 | 5 | 90.86 | 9.09 [10%] | 99.95 |
| | | | product3 | 5 | 140.86 | 9.86 [7%] + 14.09 [10%] | 164.81 |
+---------+----------+---------------------+----------+-----+---------+--------------------------+--------+
Expected Result
===============
+------------------+---------+
| account | balance |
+------------------+---------+
| receivable cash | 647.11 |
| receivable bank | 423.51 |
| other receivable | -911.87 |
| receivable | -158.75 |
+------------------+---------+
| Total balance | 0.00 |
+------------------+---------+
"""
self.open_new_session()
# create orders
orders = []
orders.append(self.create_ui_order_data(
[(self.product1, 10), (self.product2, 10), (self.product3, 10)],
customer=self.other_customer,
is_invoiced=True,
uid='09876-098-0987',
))
orders.append(self.create_ui_order_data(
[(self.product1, 5), (self.product2, 5)],
payments=[(self.bank_pm, 158.75)],
customer=self.customer,
is_invoiced=True,
uid='09876-098-0988',
))
orders.append(self.create_ui_order_data(
[(self.product2, 5), (self.product3, 5)],
payments=[(self.bank_pm, 264.76)],
customer=self.other_customer,
is_invoiced=True,
uid='09876-098-0989',
))
# sync orders
order = self.env['pos.order'].create_from_ui(orders)
# check values before closing the session
self.assertEqual(3, self.pos_session.order_count)
orders_total = sum(order.amount_total for order in self.pos_session.order_ids)
self.assertAlmostEqual(orders_total, self.pos_session.total_payments_amount, msg='Total order amount should be equal to the total payment amount.')
# check if there is one invoiced order
self.assertEqual(len(self.pos_session.order_ids.filtered(lambda order: order.state == 'invoiced')), 3, 'All orders should be invoiced.')
# close the session
self.pos_session.action_pos_session_validate()
session_move = self.pos_session.move_id
receivable_line = session_move.line_ids.filtered(lambda line: line.account_id == self.receivable_account)
self.assertAlmostEqual(receivable_line.balance, -158.75)
other_receivable_line = session_move.line_ids.filtered(lambda line: line.account_id == self.other_receivable_account)
self.assertAlmostEqual(other_receivable_line.balance, -911.87)
receivable_line_bank = session_move.line_ids.filtered(lambda line: self.bank_pm.name in line.name)
self.assertAlmostEqual(receivable_line_bank.balance, 423.51)
receivable_line_cash = session_move.line_ids.filtered(lambda line: self.cash_pm.name in line.name)
self.assertAlmostEqual(receivable_line_cash.balance, 647.11)
@@ -0,0 +1,220 @@
import odoo
from odoo.addons.point_of_sale.tests.common import TestPoSCommon
@odoo.tests.tagged('post_install', '-at_install')
class TestPoSMultipleSaleAccounts(TestPoSCommon):
""" Test to orders containing products with different sale accounts
keywords/phrases: Different Income Accounts
In this test, two sale (income) accounts are involved:
self.sale_account -> default for products because it is in the category
self.other_sale_account -> manually set to self.product2
"""
def setUp(self):
super(TestPoSMultipleSaleAccounts, self).setUp()
self.config = self.basic_config
self.product1 = self.create_product(
'Product 1',
self.categ_basic,
lst_price=10.99,
standard_price=5.0,
tax_ids=self.taxes['tax7'].ids,
)
self.product2 = self.create_product(
'Product 2',
self.categ_basic,
lst_price=19.99,
standard_price=10.0,
tax_ids=self.taxes['tax10'].ids,
sale_account=self.other_sale_account,
)
self.product3 = self.create_product(
'Product 3',
self.categ_basic,
lst_price=30.99,
standard_price=15.0,
tax_ids=self.taxes['tax_group_7_10'].ids,
)
self.adjust_inventory([self.product1, self.product2, self.product3], [100, 50, 50])
def test_01_check_product_properties(self):
self.assertEqual(self.product2.property_account_income_id, self.other_sale_account, 'Income account for the product2 should be the other sale account.')
self.assertFalse(self.product1.property_account_income_id, msg='Income account for product1 should not be set.')
self.assertFalse(self.product3.property_account_income_id, msg='Income account for product3 should not be set.')
self.assertEqual(self.product1.categ_id.property_account_income_categ_id, self.sale_account)
self.assertEqual(self.product3.categ_id.property_account_income_categ_id, self.sale_account)
def test_02_orders_without_invoice(self):
""" orders without invoice
Orders
======
+---------+----------+-----------+----------+-----+---------+--------------------------+--------+
| order | payments | invoiced? | product | qty | untaxed | tax | total |
+---------+----------+-----------+----------+-----+---------+--------------------------+--------+
| order 1 | cash | no | product1 | 10 | 109.9 | 7.69 [7%] | 117.59 |
| | | | product2 | 10 | 181.73 | 18.17 [10%] | 199.9 |
| | | | product3 | 10 | 281.73 | 19.72 [7%] + 28.17 [10%] | 329.62 |
+---------+----------+-----------+----------+-----+---------+--------------------------+--------+
| order 2 | cash | no | product1 | 5 | 54.95 | 3.85 [7%] | 58.80 |
| | | | product2 | 5 | 90.86 | 9.09 [10%] | 99.95 |
+---------+----------+-----------+----------+-----+---------+--------------------------+--------+
| order 3 | bank | no | product2 | 5 | 90.86 | 9.09 [10%] | 99.95 |
| | | | product3 | 5 | 140.86 | 9.86 [7%] + 14.09 [10%] | 164.81 |
+---------+----------+-----------+----------+-----+---------+--------------------------+--------+
Expected Result
===============
+---------------------+---------+
| account | balance |
+---------------------+---------+
| sale_account | -164.85 | (for the 7% base amount)
| sale_account | -422.59 | (for the 7+10% base amount)
| other_sale_account | -363.45 |
| tax 7% | -41.12 |
| tax 10% | -78.61 |
| pos receivable bank | 264.76 |
| pos receivable cash | 805.86 |
+---------------------+---------+
| Total balance | 0.00 |
+---------------------+---------+
"""
self.open_new_session()
# create orders
orders = []
orders.append(self.create_ui_order_data([(self.product1, 10), (self.product2, 10), (self.product3, 10)]))
orders.append(self.create_ui_order_data([(self.product1, 5), (self.product2, 5)]))
orders.append(self.create_ui_order_data([(self.product2, 5), (self.product3, 5)], payments=[(self.bank_pm, 264.76)]))
# sync orders
order = self.env['pos.order'].create_from_ui(orders)
# check values before closing the session
self.assertEqual(3, self.pos_session.order_count)
orders_total = sum(order.amount_total for order in self.pos_session.order_ids)
self.assertAlmostEqual(orders_total, self.pos_session.total_payments_amount, msg='Total order amount should be equal to the total payment amount.')
# close the session
self.pos_session.action_pos_session_validate()
# check values after the session is closed
session_move = self.pos_session.move_id
sale_account_lines = session_move.line_ids.filtered(lambda line: line.account_id == self.sale_account)
for balance, amount in zip(sorted(sale_account_lines.mapped('balance')), sorted([-164.85, -422.59])):
self.assertAlmostEqual(balance, amount)
other_sale_account_line = session_move.line_ids.filtered(lambda line: line.account_id == self.other_sale_account)
self.assertAlmostEqual(other_sale_account_line.balance, -363.45)
receivable_line_bank = session_move.line_ids.filtered(lambda line: self.bank_pm.name in line.name)
self.assertAlmostEqual(receivable_line_bank.balance, 264.76)
receivable_line_cash = session_move.line_ids.filtered(lambda line: self.cash_pm.name in line.name)
self.assertAlmostEqual(receivable_line_cash.balance, 805.86)
manually_calculated_taxes = (-41.12, -78.61)
tax_lines = session_move.line_ids.filtered(lambda line: line.account_id == self.tax_received_account)
self.assertAlmostEqual(sum(manually_calculated_taxes), sum(tax_lines.mapped('balance')))
for t1, t2 in zip(sorted(manually_calculated_taxes), sorted(tax_lines.mapped('balance'))):
self.assertAlmostEqual(t1, t2, msg='Taxes should be correctly combined.')
self.assertTrue(receivable_line_cash.full_reconcile_id)
def test_03_orders_with_invoice(self):
""" orders with invoice
Orders
======
+---------+----------+---------------+----------+-----+---------+--------------------------+--------+
| order | payments | invoiced? | product | qty | untaxed | tax | total |
+---------+----------+---------------+----------+-----+---------+--------------------------+--------+
| order 1 | cash | no | product1 | 10 | 109.9 | 7.69 [7%] | 117.59 |
| | | | product2 | 10 | 181.73 | 18.17 [10%] | 199.9 |
| | | | product3 | 10 | 281.73 | 19.72 [7%] + 28.17 [10%] | 329.62 |
+---------+----------+---------------+----------+-----+---------+--------------------------+--------+
| order 2 | bank | no | product1 | 5 | 54.95 | 3.85 [7%] | 58.80 |
| | | | product2 | 5 | 90.86 | 9.09 [10%] | 99.95 |
+---------+----------+---------------+----------+-----+---------+--------------------------+--------+
| order 3 | bank | yes, customer | product2 | 5 | 90.86 | 9.09 [10%] | 99.95 |
| | | | product3 | 5 | 140.86 | 9.86 [7%] + 14.09 [10%] | 164.81 |
+---------+----------+---------------+----------+-----+---------+--------------------------+--------+
Expected Result
===============
+---------------------+---------+
| account | balance |
+---------------------+---------+
| sale_account | -164.85 | (for the 7% base amount)
| sale_account | -281.73 | (for the 7+10% base amount)
| other_sale_account | -272.59 |
| tax 7% | -31.26 |
| tax 10% | -55.43 |
| pos receivable cash | 647.11 |
| pos receivable bank | 423.51 |
| receivable | -264.76 |
+---------------------+---------+
| Total balance | 0.00 |
+---------------------+---------+
"""
self.open_new_session()
# create orders
orders = []
orders.append(self.create_ui_order_data([(self.product1, 10), (self.product2, 10), (self.product3, 10)]))
orders.append(self.create_ui_order_data(
[(self.product1, 5), (self.product2, 5)],
payments=[(self.bank_pm, 158.75)],
))
orders.append(self.create_ui_order_data(
[(self.product2, 5), (self.product3, 5)],
payments=[(self.bank_pm, 264.76)],
customer=self.customer,
is_invoiced=True,
uid='09876-098-0987',
))
# sync orders
order = self.env['pos.order'].create_from_ui(orders)
# check values before closing the session
self.assertEqual(3, self.pos_session.order_count)
orders_total = sum(order.amount_total for order in self.pos_session.order_ids)
self.assertAlmostEqual(orders_total, self.pos_session.total_payments_amount, msg='Total order amount should be equal to the total payment amount.')
# check if there is one invoiced order
self.assertEqual(len(self.pos_session.order_ids.filtered(lambda order: order.state == 'invoiced')), 1, 'There should only be one invoiced order.')
# close the session
self.pos_session.action_pos_session_validate()
# check values after the session is closed
session_move = self.pos_session.move_id
sale_account_lines = session_move.line_ids.filtered(lambda line: line.account_id == self.sale_account)
for balance, amount in zip(sorted(sale_account_lines.mapped('balance')), sorted([-164.85, -281.73])):
self.assertAlmostEqual(balance, amount)
other_sale_account_line = session_move.line_ids.filtered(lambda line: line.account_id == self.other_sale_account)
self.assertAlmostEqual(other_sale_account_line.balance, -272.59)
pos_receivable_line_bank = session_move.line_ids.filtered(lambda line: self.bank_pm.name in line.name)
self.assertAlmostEqual(pos_receivable_line_bank.balance, 423.51)
pos_receivable_line_cash = session_move.line_ids.filtered(lambda line: self.cash_pm.name in line.name)
self.assertAlmostEqual(pos_receivable_line_cash.balance, 647.11)
manually_calculated_taxes = (-31.26, -55.43)
tax_lines = session_move.line_ids.filtered(lambda line: line.account_id == self.tax_received_account)
self.assertAlmostEqual(sum(manually_calculated_taxes), sum(tax_lines.mapped('balance')))
for t1, t2 in zip(sorted(manually_calculated_taxes), sorted(tax_lines.mapped('balance'))):
self.assertAlmostEqual(t1, t2, msg='Taxes should be correctly combined.')
receivable_line = session_move.line_ids.filtered(lambda line: line.account_id == self.receivable_account)
self.assertAlmostEqual(receivable_line.balance, -264.76)
self.assertTrue(pos_receivable_line_cash.full_reconcile_id)
self.assertTrue(receivable_line.full_reconcile_id)
@@ -0,0 +1,170 @@
import odoo
from odoo.tests.common import Form
from odoo.addons.point_of_sale.tests.common import TestPoSCommon
@odoo.tests.tagged('post_install', '-at_install')
class TestPoSOtherCurrencyConfig(TestPoSCommon):
""" Test PoS with basic configuration
"""
def setUp(self):
super(TestPoSOtherCurrencyConfig, self).setUp()
self.config = self.other_currency_config
self.product1 = self.create_product('Product 1', self.categ_basic, 10.0, 5)
self.product2 = self.create_product('Product 2', self.categ_basic, 20.0, 10)
self.product3 = self.create_product('Product 3', self.categ_basic, 30.0, 15)
self.adjust_inventory([self.product1, self.product2, self.product3], [100, 50, 50])
# change the price of product2 to 12.99 fixed. No need to convert.
pricelist_item = self.env['product.pricelist.item'].create({
'product_tmpl_id': self.product2.product_tmpl_id.id,
'fixed_price': 12.99,
})
self.config.pricelist_id.write({'item_ids': [(6, 0, (self.config.pricelist_id.item_ids | pricelist_item).ids)]})
def test_01_check_product_cost(self):
# Product price should be half of the original price because currency rate is 0.5.
# (see `self._create_other_currency_config` method)
# Except for product2 where the price is specified in the pricelist.
self.assertAlmostEqual(self.config.pricelist_id.get_product_price(self.product1, 1, self.customer), 5.00)
self.assertAlmostEqual(self.config.pricelist_id.get_product_price(self.product2, 1, self.customer), 12.99)
self.assertAlmostEqual(self.config.pricelist_id.get_product_price(self.product3, 1, self.customer), 15.00)
def test_02_orders_without_invoice(self):
""" orders without invoice
Orders
======
+---------+----------+-----------+----------+-----+-------+
| order | payments | invoiced? | product | qty | total |
+---------+----------+-----------+----------+-----+-------+
| order 1 | cash | no | product1 | 10 | 50 |
| | | | product2 | 10 | 129.9 |
| | | | product3 | 10 | 150 |
+---------+----------+-----------+----------+-----+-------+
| order 2 | cash | no | product1 | 5 | 25 |
| | | | product2 | 5 | 64.95 |
+---------+----------+-----------+----------+-----+-------+
| order 3 | bank | no | product2 | 5 | 64.95 |
| | | | product3 | 5 | 75 |
+---------+----------+-----------+----------+-----+-------+
Expected Result
===============
+---------------------+---------+-----------------+
| account | balance | amount_currency |
+---------------------+---------+-----------------+
| sale_account | -1119.6 | -559.80 |
| pos receivable bank | 279.9 | 139.95 |
| pos receivable cash | 839.7 | 419.85 |
+---------------------+---------+-----------------+
| Total balance | 0.0 | 0.00 |
+---------------------+---------+-----------------+
"""
self.open_new_session()
# create orders
orders = []
orders.append(self.create_ui_order_data([(self.product1, 10), (self.product2, 10), (self.product3, 10)]))
orders.append(self.create_ui_order_data([(self.product1, 5), (self.product2, 5)]))
orders.append(self.create_ui_order_data([(self.product2, 5), (self.product3, 5)], payments=[(self.bank_pm, 139.95)]))
# sync orders
order = self.env['pos.order'].create_from_ui(orders)
# check values before closing the session
self.assertEqual(3, self.pos_session.order_count)
orders_total = sum(order.amount_total for order in self.pos_session.order_ids)
self.assertAlmostEqual(orders_total, self.pos_session.total_payments_amount, msg='Total order amount should be equal to the total payment amount.')
# close the session
self.pos_session.action_pos_session_validate()
session_move = self.pos_session.move_id
sale_account_line = session_move.line_ids.filtered(lambda line: line.account_id == self.sale_account)
self.assertAlmostEqual(sale_account_line.balance, -1119.6)
self.assertAlmostEqual(sale_account_line.amount_currency, -559.80)
pos_receivable_line_bank = session_move.line_ids.filtered(lambda line: line.account_id == self.pos_receivable_account and self.bank_pm.name in line.name)
self.assertAlmostEqual(pos_receivable_line_bank.balance, 279.9)
self.assertAlmostEqual(pos_receivable_line_bank.amount_currency, 139.95)
pos_receivable_line_cash = session_move.line_ids.filtered(lambda line: line.account_id == self.pos_receivable_account and self.cash_pm.name in line.name)
self.assertAlmostEqual(pos_receivable_line_cash.balance, 839.7)
self.assertAlmostEqual(pos_receivable_line_cash.amount_currency, 419.85)
def test_03_orders_with_invoice(self):
""" orders with invoice
Orders
======
+---------+----------+---------------+----------+-----+-------+
| order | payments | invoiced? | product | qty | total |
+---------+----------+---------------+----------+-----+-------+
| order 1 | cash | no | product1 | 10 | 50 |
| | | | product2 | 10 | 129.9 |
| | | | product3 | 10 | 150 |
+---------+----------+---------------+----------+-----+-------+
| order 2 | cash | yes, customer | product1 | 5 | 25 |
| | | | product2 | 5 | 64.95 |
+---------+----------+---------------+----------+-----+-------+
| order 3 | bank | yes, customer | product2 | 5 | 64.95 |
| | | | product3 | 5 | 75 |
+---------+----------+---------------+----------+-----+-------+
Expected Result
===============
+---------------------+---------+-----------------+
| account | balance | amount_currency |
+---------------------+---------+-----------------+
| sale_account | -659.8 | -329.90 |
| pos receivable bank | 279.9 | 139.95 |
| pos receivable cash | 839.7 | 419.85 |
| invoice receivable | -459.8 | -229.90 |
+---------------------+---------+-----------------+
| Total balance | 0.0 | 0.00 |
+---------------------+---------+-----------------+
"""
self.open_new_session()
# create orders
orders = []
orders.append(self.create_ui_order_data([(self.product1, 10), (self.product2, 10), (self.product3, 10)]))
orders.append(self.create_ui_order_data(
[(self.product1, 5), (self.product2, 5)],
customer=self.customer,
is_invoiced=True,
))
orders.append(self.create_ui_order_data(
[(self.product2, 5), (self.product3, 5)],
payments=[(self.bank_pm, 139.95)],
customer=self.customer,
is_invoiced=True,
))
# sync orders
order = self.env['pos.order'].create_from_ui(orders)
# check values before closing the session
self.assertEqual(3, self.pos_session.order_count)
orders_total = sum(order.amount_total for order in self.pos_session.order_ids)
self.assertAlmostEqual(orders_total, self.pos_session.total_payments_amount, msg='Total order amount should be equal to the total payment amount.')
# close the session
self.pos_session.action_pos_session_validate()
session_move = self.pos_session.move_id
sale_account_line = session_move.line_ids.filtered(lambda line: line.account_id == self.sale_account)
self.assertAlmostEqual(sale_account_line.balance, -659.8)
self.assertAlmostEqual(sale_account_line.amount_currency, -329.9)
pos_receivable_line_bank = session_move.line_ids.filtered(lambda line: line.account_id == self.pos_receivable_account and self.bank_pm.name in line.name)
self.assertAlmostEqual(pos_receivable_line_bank.balance, 279.9)
self.assertAlmostEqual(pos_receivable_line_bank.amount_currency, 139.95)
pos_receivable_line_cash = session_move.line_ids.filtered(lambda line: line.account_id == self.pos_receivable_account and self.cash_pm.name in line.name)
self.assertAlmostEqual(pos_receivable_line_cash.balance, 839.7)
self.assertAlmostEqual(pos_receivable_line_cash.amount_currency, 419.85)
invoice_receivable_line = session_move.line_ids.filtered(lambda line: line.account_id == self.receivable_account)
self.assertAlmostEqual(invoice_receivable_line.balance, -459.8)
self.assertAlmostEqual(invoice_receivable_line.amount_currency, -229.9)
@@ -0,0 +1,286 @@
import odoo
from odoo.addons.point_of_sale.tests.common import TestPoSCommon
@odoo.tests.tagged('post_install', '-at_install')
class TestPoSProductsWithTax(TestPoSCommon):
""" Test normal configuration PoS selling products with tax
"""
def setUp(self):
super(TestPoSProductsWithTax, self).setUp()
self.config = self.basic_config
self.product1 = self.create_product(
'Product 1',
self.categ_basic,
10.0,
5.0,
tax_ids=self.taxes['tax7'].ids,
)
self.product2 = self.create_product(
'Product 2',
self.categ_basic,
20.0,
10.0,
tax_ids=self.taxes['tax10'].ids,
)
self.product3 = self.create_product(
'Product 3',
self.categ_basic,
30.0,
15.0,
tax_ids=self.taxes['tax_group_7_10'].ids,
)
self.adjust_inventory([self.product1, self.product2, self.product3], [100, 50, 50])
def test_orders_no_invoiced(self):
""" Test for orders without invoice
Orders
======
+---------+----------+-----------+----------+-----+---------+-----------------------+--------+
| order | payments | invoiced? | product | qty | untaxed | tax | total |
+---------+----------+-----------+----------+-----+---------+-----------------------+--------+
| order 1 | cash | no | product1 | 10 | 100 | 7 | 107 |
| | | | product2 | 5 | 90.91 | 9.09 | 100 |
+---------+----------+-----------+----------+-----+---------+-----------------------+--------+
| order 2 | cash | no | product2 | 7 | 127.27 | 12.73 | 140 |
| | | | product3 | 4 | 109.09 | 10.91[10%] + 7.64[7%] | 127.64 |
+---------+----------+-----------+----------+-----+---------+-----------------------+--------+
| order 3 | bank | no | product1 | 1 | 10 | 0.7 | 10.7 |
| | | | product2 | 3 | 54.55 | 5.45 | 60 |
| | | | product3 | 5 | 136.36 | 13.64[10%] + 9.55[7%] | 159.55 |
+---------+----------+-----------+----------+-----+---------+-----------------------+--------+
Calculated taxes
================
total tax 7% only + group tax (10+7%)
(7 + 0.7) + (7.64 + 9.55) = 7.7 + 17.19 = 24.89
total tax 10% only + group tax (10+7%)
(9.09 + 12.73 + 5.45) + (10.91 + 13.64) = 27.27 + 24.55 = 51.82
Thus, manually_calculated_taxes = (-24,89, -51.82)
"""
self.open_new_session()
# create orders
orders = []
orders.append(self.create_ui_order_data([(self.product1, 10), (self.product2, 5)]))
orders.append(self.create_ui_order_data([(self.product2, 7), (self.product3, 4)]))
orders.append(self.create_ui_order_data(
[(self.product1, 1), (self.product3, 5), (self.product2, 3)],
payments=[(self.bank_pm, 230.25)]
))
# sync orders
order = self.env['pos.order'].create_from_ui(orders)
# check values before closing the session
self.assertEqual(3, self.pos_session.order_count)
orders_total = sum(order.amount_total for order in self.pos_session.order_ids)
self.assertAlmostEqual(orders_total, self.pos_session.total_payments_amount, 'Total order amount should be equal to the total payment amount.')
# close the session
self.pos_session.action_pos_session_validate()
# check values after the session is closed
session_move = self.pos_session.move_id
sales_lines = session_move.line_ids.filtered(lambda line: line.account_id == self.sale_account)
self.assertAlmostEqual(sum(sales_lines.mapped('balance')), -628.18, msg='Sales line balance should be equal to untaxed orders amount.')
receivable_line_bank = session_move.line_ids.filtered(lambda line: self.bank_pm.name in line.name)
self.assertAlmostEqual(receivable_line_bank.balance, 230.25, msg='Bank receivable should be equal to the total bank payments.')
receivable_line_cash = session_move.line_ids.filtered(lambda line: self.cash_pm.name in line.name)
self.assertAlmostEqual(receivable_line_cash.balance, 474.64, msg='Cash receivable should be equal to the total cash payments.')
tax_lines = session_move.line_ids.filtered(lambda line: line.account_id == self.tax_received_account)
manually_calculated_taxes = (-24.89, -51.82)
self.assertAlmostEqual(sum(manually_calculated_taxes), sum(tax_lines.mapped('balance')))
for t1, t2 in zip(sorted(manually_calculated_taxes), sorted(tax_lines.mapped('balance'))):
self.assertAlmostEqual(t1, t2, msg='Taxes should be correctly combined.')
base_amounts = (355.45, 518.18)
self.assertAlmostEqual(sum(base_amounts), sum(tax_lines.mapped('tax_base_amount')))
self.assertTrue(receivable_line_cash.full_reconcile_id, 'Cash receivable line should be fully-reconciled.')
def test_orders_with_invoiced(self):
""" Test for orders: one with invoice
Orders
======
+---------+----------+---------------+----------+-----+---------+---------------+--------+
| order | payments | invoiced? | product | qty | untaxed | tax | total |
+---------+----------+---------------+----------+-----+---------+---------------+--------+
| order 1 | cash | no | product1 | 6 | 60 | 4.2 | 64.2 |
| | | | product2 | 3 | 54.55 | 5.45 | 60 |
| | | | product3 | 1 | 27.27 | 2.73 + 1.91 | 31.91 |
+---------+----------+---------------+----------+-----+---------+---------------+--------+
| order 2 | bank | no | product1 | 1 | 10 | 0.7 | 10.7 |
| | | | product2 | 20 | 363.64 | 36.36 | 400 |
+---------+----------+---------------+----------+-----+---------+---------------+--------+
| order 3 | bank | yes, customer | product1 | 10 | 100 | 7 | 107 |
| | | | product3 | 10 | 272.73 | 27.27 + 19.09 | 319.09 |
+---------+----------+---------------+----------+-----+---------+---------------+--------+
Calculated taxes
================
total tax 7% only
4.2 + 0.7 => 4.9 + 1.91 = 6.81
total tax 10% only
5.45 + 36.36 => 41.81 + 2.73 = 44.54
Thus, manually_calculated_taxes = (-6.81, -44.54)
"""
self.open_new_session()
# create orders
orders = []
orders.append(self.create_ui_order_data(
[(self.product3, 1), (self.product1, 6), (self.product2, 3)],
payments=[(self.cash_pm, 156.11)],
))
orders.append(self.create_ui_order_data(
[(self.product2, 20), (self.product1, 1)],
payments=[(self.bank_pm, 410.7)],
))
orders.append(self.create_ui_order_data(
[(self.product1, 10), (self.product3, 10)],
payments=[(self.bank_pm, 426.09)],
customer=self.customer,
is_invoiced=True,
uid='09876-098-0987',
))
# sync orders
order = self.env['pos.order'].create_from_ui(orders)
# check values before closing the session
self.assertEqual(3, self.pos_session.order_count)
orders_total = sum(order.amount_total for order in self.pos_session.order_ids)
self.assertAlmostEqual(orders_total, self.pos_session.total_payments_amount, msg='Total order amount should be equal to the total payment amount.')
# check account move in the invoiced order
invoiced_order = self.pos_session.order_ids.filtered(lambda order: '09876-098-0987' in order.pos_reference)
self.assertEqual(1, len(invoiced_order), 'Only one order is invoiced in this test.')
invoice = invoiced_order.account_move
self.assertAlmostEqual(invoice.amount_total, 426.09)
# close the session
self.pos_session.action_pos_session_validate()
# check values after the session is closed
session_move = self.pos_session.move_id
# check sales line
# should not include tax amounts
sales_lines = session_move.line_ids.filtered(lambda line: line.account_id == self.sale_account)
self.assertAlmostEqual(sum(sales_lines.mapped('balance')), -515.46)
# check receivable line
# should be equivalent to receivable in the invoice
# should also be fully-reconciled
receivable_line = session_move.line_ids.filtered(lambda line: line.account_id == self.receivable_account)
self.assertAlmostEqual(receivable_line.balance, -426.09)
self.assertTrue(receivable_line.full_reconcile_id, msg='Receivable line for invoices should be fully reconciled.')
pos_receivable_line_bank = session_move.line_ids.filtered(
lambda line: self.bank_pm.name in line.name and line.account_id == self.bank_pm.receivable_account_id
)
self.assertAlmostEqual(pos_receivable_line_bank.balance, 836.79)
pos_receivable_line_cash = session_move.line_ids.filtered(
lambda line: self.cash_pm.name in line.name and line.account_id == self.bank_pm.receivable_account_id
)
self.assertAlmostEqual(pos_receivable_line_cash.balance, 156.11)
self.assertTrue(pos_receivable_line_cash.full_reconcile_id)
receivable_line = session_move.line_ids.filtered(lambda line: line.account_id == self.receivable_account)
self.assertAlmostEqual(receivable_line.balance, -invoice.amount_total)
tax_lines = session_move.line_ids.filtered(lambda line: line.account_id == self.tax_received_account)
manually_calculated_taxes = (-6.81, -44.54)
self.assertAlmostEqual(sum(manually_calculated_taxes), sum(tax_lines.mapped('balance')))
for t1, t2 in zip(sorted(manually_calculated_taxes), sorted(tax_lines.mapped('balance'))):
self.assertAlmostEqual(t1, t2, msg='Taxes should be correctly combined.')
base_amounts = (97.27, 445.46) # computation does not include invoiced order.
self.assertAlmostEqual(sum(base_amounts), sum(tax_lines.mapped('tax_base_amount')))
def test_return_order(self):
""" Test return order
Order (invoiced)
======
+----------+----------+---------------+----------+-----+---------+-------------+-------+
| order | payments | invoiced? | product | qty | untaxed | tax | total |
+----------+----------+---------------+----------+-----+---------+-------------+-------+
| order 1 | cash | yes, customer | product1 | 3 | 30 | 2.1 | 32.1 |
| | | | product2 | 2 | 36.36 | 3.64 | 40 |
| | | | product3 | 1 | 27.27 | 2.73 + 1.91 | 31.91 |
+----------+----------+---------------+----------+-----+---------+-------------+-------+
The order is invoiced so the tax of the invoiced order is in the account_move of the order.
However, the return order is not invoiced, thus, the journal items are in the session_move,
which will contain the tax lines of the returned products.
manually_calculated_taxes = (4.01, 6.37)
"""
self.open_new_session()
# create orders
orders = []
orders.append(self.create_ui_order_data(
[(self.product1, 3), (self.product2, 2), (self.product3, 1)],
payments=[(self.cash_pm, 104.01)],
customer=self.customer,
is_invoiced=True,
uid='12345-123-1234',
))
# sync orders
order = self.env['pos.order'].create_from_ui(orders)
# check values before closing the session
self.assertEqual(1, self.pos_session.order_count)
orders_total = sum(order.amount_total for order in self.pos_session.order_ids)
self.assertAlmostEqual(orders_total, self.pos_session.total_payments_amount, msg='Total order amount should be equal to the total payment amount.')
# return order
order_to_return = self.pos_session.order_ids.filtered(lambda order: '12345-123-1234' in order.pos_reference)
order_to_return.refund()
refund_order = self.pos_session.order_ids.filtered(lambda order: order.state == 'draft')
context_make_payment = {"active_ids": [refund_order.id], "active_id": refund_order.id}
make_payment = self.env['pos.make.payment'].with_context(context_make_payment).create({
'payment_method_id': self.cash_pm.id,
'amount': -104.01,
})
make_payment.check()
self.assertEqual(refund_order.state, 'paid', 'Payment is registered, order should be paid.')
self.assertAlmostEqual(refund_order.amount_paid, -104.01, msg='Amount paid for return order should be negative.')
# close the session
self.pos_session.action_pos_session_validate()
# check values after the session is closed
session_move = self.pos_session.move_id
# instead of credit, the sales line should be debit
sales_lines = session_move.line_ids.filtered(lambda line: line.account_id == self.sale_account)
self.assertAlmostEqual(sum(sales_lines.mapped('balance')), 93.63)
receivable_line_bank = session_move.line_ids.filtered(lambda line: self.bank_pm.name in line.name)
self.assertFalse(receivable_line_bank, msg='There should be no bank receivable line because no bank payment made.')
receivable_line_cash = session_move.line_ids.filtered(lambda line: self.cash_pm.name in line.name)
self.assertFalse(receivable_line_cash, msg='There should be no cash receivable line because it is combined with the original cash payment.')
manually_calculated_taxes = (4.01, 6.37) # should be positive since it is return order
tax_lines = session_move.line_ids.filtered(lambda line: line.account_id == self.tax_received_account)
self.assertAlmostEqual(sum(manually_calculated_taxes), sum(tax_lines.mapped('balance')))
for t1, t2 in zip(sorted(manually_calculated_taxes), sorted(tax_lines.mapped('balance'))):
self.assertAlmostEqual(t1, t2, msg='Taxes should be correctly combined and should be debit.')
@@ -0,0 +1,69 @@
import odoo
from odoo.addons.point_of_sale.tests.common import TestPoSCommon
@odoo.tests.tagged('post_install', '-at_install')
class TestPoSSetup(TestPoSCommon):
""" This group of tests is for sanity check in setting up global records which will be used
in each testing.
If a test fails here, then it means there are inconsistencies in what we expect in the setup.
"""
def setUp(self):
super(TestPoSSetup, self).setUp()
self.config = self.basic_config
self.products = [
self.create_product('Product 1', self.categ_basic, lst_price=10.0, standard_price=5),
self.create_product('Product 2', self.categ_basic, lst_price=20.0, standard_price=10),
self.create_product('Product 3', self.categ_basic, lst_price=30.0, standard_price=15),
]
def test_basic_config_values(self):
config = self.basic_config
self.assertEqual(config.currency_id, self.company_currency)
self.assertEqual(config.pricelist_id.currency_id, self.company_currency)
def test_other_currency_config_values(self):
config = self.other_currency_config
self.assertEqual(config.currency_id, self.other_currency)
self.assertEqual(config.pricelist_id.currency_id, self.other_currency)
def test_product_categories(self):
# check basic product category
# it is expected to have standard and manual_periodic valuation
self.assertEqual(self.categ_basic.property_cost_method, 'standard')
self.assertEqual(self.categ_basic.property_valuation, 'manual_periodic')
# check anglo saxon product category
# this product categ is expected to have fifo and real_time valuation
self.assertEqual(self.categ_anglo.property_cost_method, 'fifo')
self.assertEqual(self.categ_anglo.property_valuation, 'real_time')
def test_product_price(self):
def get_price(pricelist, product):
return pricelist.get_product_price(product, 1, self.customer)
# check usd pricelist
pricelist = self.basic_config.pricelist_id
for product in self.products:
self.assertAlmostEqual(get_price(pricelist, product), product.lst_price)
# check eur pricelist
# exchange rate to the other currency is set to 0.5, thus, lst_price
# is expected to have half its original value.
pricelist = self.other_currency_config.pricelist_id
for product in self.products:
self.assertAlmostEqual(get_price(pricelist, product), product.lst_price * 0.5)
def test_taxes(self):
tax7 = self.taxes['tax7']
self.assertEqual(tax7.name, 'Tax 7%')
self.assertAlmostEqual(tax7.amount, 7)
self.assertEqual(tax7.invoice_repartition_line_ids.mapped('account_id').id, self.tax_received_account.id)
tax10 = self.taxes['tax10']
self.assertEqual(tax10.name, 'Tax 10%')
self.assertAlmostEqual(tax10.amount, 10)
self.assertEqual(tax10.price_include, True)
self.assertEqual(tax10.invoice_repartition_line_ids.mapped('account_id').id, self.tax_received_account.id)
tax_group_7_10 = self.taxes['tax_group_7_10']
self.assertEqual(tax_group_7_10.name, 'Tax 7+10%')
self.assertEqual(tax_group_7_10.amount_type, 'group')
self.assertEqual(sorted(tax_group_7_10.children_tax_ids.ids), sorted((tax7 | tax10).ids))
@@ -0,0 +1,194 @@
import odoo
from odoo.addons.point_of_sale.tests.common import TestPoSCommon
@odoo.tests.tagged('post_install', '-at_install')
class TestPoSStock(TestPoSCommon):
""" Tests for anglo saxon accounting scenario.
"""
def setUp(self):
super(TestPoSStock, self).setUp()
self.config = self.basic_config
self.product1 = self.create_product('Product 1', self.categ_anglo, 10.0, 5.0)
self.product2 = self.create_product('Product 2', self.categ_anglo, 20.0, 10.0)
self.product3 = self.create_product('Product 3', self.categ_basic, 30.0, 15.0)
# start inventory with 10 items for each product
self.adjust_inventory([self.product1, self.product2, self.product3], [10, 10, 10])
# change cost(standard_price) of anglo products
# then set inventory from 10 -> 15
self.product1.write({'standard_price': 6.0})
self.product2.write({'standard_price': 6.0})
self.adjust_inventory([self.product1, self.product2, self.product3], [15, 15, 15])
# change cost(standard_price) of anglo products
# then set inventory from 15 -> 25
self.product1.write({'standard_price': 13.0})
self.product2.write({'standard_price': 13.0})
self.adjust_inventory([self.product1, self.product2, self.product3], [25, 25, 25])
self.output_account = self.categ_anglo.property_stock_account_output_categ_id
self.expense_account = self.categ_anglo.property_account_expense_categ_id
self.valuation_account = self.categ_anglo.property_stock_valuation_account_id
def test_01_orders_no_invoiced(self):
"""
Orders
======
+---------+----------+-----+-------------+------------+
| order | product | qty | total price | total cost |
+---------+----------+-----+-------------+------------+
| order 1 | product1 | 10 | 100.0 | 50.0 | -> 10 items at cost of 5.0 is consumed, remains 5 items at 6.0 and 10 items at 13.0
| | product2 | 10 | 200.0 | 100.0 | -> 10 items at cost of 10.0 is consumed, remains 5 items at 6.0 and 10 items at 13.0
+---------+----------+-----+-------------+------------+
| order 2 | product2 | 7 | 140.0 | 56.0 | -> 5 items at cost of 6.0 and 2 items at cost of 13.0, remains 8 items at cost of 13.0
| | product3 | 7 | 210.0 | 0.0 |
+---------+----------+-----+-------------+------------+
| order 3 | product1 | 6 | 60.0 | 43.0 | -> 5 items at cost of 6.0 and 1 item at cost of 13.0, remains 9 items at cost of 13.0
| | product2 | 6 | 120.0 | 78.0 | -> 6 items at cost of 13.0, remains 2 items at cost of 13.0
| | product3 | 6 | 180.0 | 0.0 |
+---------+----------+-----+-------------+------------+
Expected Result
===============
+---------------------+---------+
| account | balance |
+---------------------+---------+
| sale_account | -1010.0 |
| pos_receivable-cash | 1010.0 |
| expense_account | 327.0 |
| output_account | -327.0 |
+---------------------+---------+
| Total balance | 0.00 |
+---------------------+---------+
"""
self.open_new_session()
# create orders
orders = []
orders.append(self.create_ui_order_data([(self.product1, 10), (self.product2, 10)]))
orders.append(self.create_ui_order_data([(self.product2, 7), (self.product3, 7)]))
orders.append(self.create_ui_order_data([(self.product1, 6), (self.product2, 6), (self.product3, 6)]))
# sync orders
order = self.env['pos.order'].create_from_ui(orders)
# check values before closing the session
self.assertEqual(3, self.pos_session.order_count)
orders_total = sum(order.amount_total for order in self.pos_session.order_ids)
self.assertAlmostEqual(orders_total, self.pos_session.total_payments_amount, msg='Total order amount should be equal to the total payment amount.')
self.assertAlmostEqual(orders_total, 1010.0, msg='The orders\'s total amount should equal the computed.')
# check product qty_available after syncing the order
self.assertEqual(self.product1.qty_available, 9)
self.assertEqual(self.product2.qty_available, 2)
self.assertEqual(self.product3.qty_available, 12)
# picking and stock moves should be in done state
for order in self.pos_session.order_ids:
self.assertEqual(order.picking_id.state, 'done', 'Picking should be in done state.')
self.assertTrue(all(state == 'done' for state in order.picking_id.move_lines.mapped('state')), 'Move Lines should be in done state.' )
# close the session
self.pos_session.action_pos_session_validate()
# check values after the session is closed
account_move = self.pos_session.move_id
sales_line = account_move.line_ids.filtered(lambda line: line.account_id == self.sale_account)
self.assertAlmostEqual(sales_line.balance, -orders_total, msg='Sales line balance should be equal to total orders amount.')
receivable_line_cash = account_move.line_ids.filtered(lambda line: self.pos_receivable_account == line.account_id and self.cash_pm.name in line.name)
self.assertAlmostEqual(receivable_line_cash.balance, 1010.0, msg='Cash receivable should be equal to the total cash payments.')
expense_line = account_move.line_ids.filtered(lambda line: line.account_id == self.expense_account)
self.assertAlmostEqual(expense_line.balance, 327.0)
output_line = account_move.line_ids.filtered(lambda line: line.account_id == self.output_account)
self.assertAlmostEqual(output_line.balance, -327.0)
self.assertTrue(receivable_line_cash.full_reconcile_id, msg='Cash receivable line should be fully-reconciled.')
self.assertTrue(output_line.full_reconcile_id, msg='The stock output account line should be fully-reconciled.')
def test_02_orders_with_invoice(self):
"""
Orders
======
Same with test_01 but order 3 is invoiced.
Expected Result
===============
+---------------------+---------+
| account | balance |
+---------------------+---------+
| sale_account | -650.0 |
| pos_receivable-cash | 1010.0 |
| receivable | -360.0 |
| expense_account | 206.0 |
| output_account | -206.0 |
+---------------------+---------+
| Total balance | 0.00 |
+---------------------+---------+
"""
self.open_new_session()
# create orders
orders = []
orders.append(self.create_ui_order_data([(self.product1, 10), (self.product2, 10)]))
orders.append(self.create_ui_order_data([(self.product2, 7), (self.product3, 7)]))
invoiced_uid = self.create_random_uid()
orders.append(self.create_ui_order_data(
[(self.product1, 6), (self.product2, 6), (self.product3, 6)],
is_invoiced=True,
customer=self.customer,
uid=invoiced_uid,
))
# sync orders
order = self.env['pos.order'].create_from_ui(orders)
# check values before closing the session
self.assertEqual(3, self.pos_session.order_count)
orders_total = sum(order.amount_total for order in self.pos_session.order_ids)
self.assertAlmostEqual(orders_total, self.pos_session.total_payments_amount, msg='Total order amount should be equal to the total payment amount.')
self.assertAlmostEqual(orders_total, 1010.0, msg='The orders\'s total amount should equal the computed.')
# check product qty_available after syncing the order
self.assertEqual(self.product1.qty_available, 9)
self.assertEqual(self.product2.qty_available, 2)
self.assertEqual(self.product3.qty_available, 12)
# picking and stock moves should be in done state
for order in self.pos_session.order_ids:
self.assertEqual(order.picking_id.state, 'done', 'Picking should be in done state.')
self.assertTrue(all(state == 'done' for state in order.picking_id.move_lines.mapped('state')), 'Move Lines should be in done state.' )
# close the session
self.pos_session.action_pos_session_validate()
# check values after the session is closed
account_move = self.pos_session.move_id
sales_line = account_move.line_ids.filtered(lambda line: line.account_id == self.sale_account)
self.assertAlmostEqual(sales_line.balance, -650.0)
receivable_line = account_move.line_ids.filtered(lambda line: line.account_id == self.receivable_account)
self.assertAlmostEqual(receivable_line.balance, -360.0, msg='Receivable line balance should equal the negative of total amount of invoiced orders.')
receivable_line_cash = account_move.line_ids.filtered(lambda line: self.pos_receivable_account == line.account_id and self.cash_pm.name in line.name)
self.assertAlmostEqual(receivable_line_cash.balance, 1010.0, msg='Cash receivable should be equal to the total cash payments.')
expense_line = account_move.line_ids.filtered(lambda line: line.account_id == self.expense_account)
self.assertAlmostEqual(expense_line.balance, 206.0)
output_line = account_move.line_ids.filtered(lambda line: line.account_id == self.output_account)
self.assertAlmostEqual(output_line.balance, -206.0)
# check order journal entry
invoiced_order = self.pos_session.order_ids.filtered(lambda order: invoiced_uid in order.pos_reference)
invoiced_output_account_lines = invoiced_order.account_move.line_ids.filtered(lambda line: line.account_id == self.output_account)
self.assertAlmostEqual(sum(invoiced_output_account_lines.mapped('balance')), -121.0)
self.assertTrue(receivable_line_cash.full_reconcile_id, msg='Cash receivable line should be fully-reconciled.')
self.assertTrue(output_line.full_reconcile_id, msg='The stock output account line should be fully-reconciled.')
@@ -0,0 +1,379 @@
import odoo
from odoo.addons.point_of_sale.tests.common import TestPoSCommon
@odoo.tests.tagged('post_install', '-at_install')
class TestPoSWithFiscalPosition(TestPoSCommon):
""" Tests to pos orders with fiscal position.
keywords/phrases: fiscal position
"""
def setUp(self):
super(TestPoSWithFiscalPosition, self).setUp()
self.config = self.basic_config
self.new_tax_17 = self.env['account.tax'].create({'name': 'New Tax 17%', 'amount': 17})
self.new_tax_17.invoice_repartition_line_ids.write({'account_id': self.tax_received_account.id})
self.fpos = self._create_fiscal_position()
self.fpos_no_tax_dest = self._create_fiscal_position_no_tax_dest()
self.product1 = self.create_product(
'Product 1',
self.categ_basic,
lst_price=10.99,
standard_price=5.0,
tax_ids=self.taxes['tax7'].ids,
)
self.product2 = self.create_product(
'Product 2',
self.categ_basic,
lst_price=19.99,
standard_price=10.0,
tax_ids=self.taxes['tax10'].ids,
)
self.product3 = self.create_product(
'Product 3',
self.categ_basic,
lst_price=30.99,
standard_price=15.0,
tax_ids=self.taxes['tax7'].ids,
)
self.adjust_inventory([self.product1, self.product2, self.product3], [100, 50, 50])
def _create_fiscal_position(self):
fpos = self.env['account.fiscal.position'].create({'name': 'Test Fiscal Position'})
account_fpos = self.env['account.fiscal.position.account'].create({
'position_id': fpos.id,
'account_src_id': self.sale_account.id,
'account_dest_id': self.other_sale_account.id,
})
tax_fpos = self.env['account.fiscal.position.tax'].create({
'position_id': fpos.id,
'tax_src_id': self.taxes['tax7'].id,
'tax_dest_id': self.new_tax_17.id,
})
fpos.write({
'account_ids': [(6, 0, account_fpos.ids)],
'tax_ids': [(6, 0, tax_fpos.ids)],
})
return fpos
def _create_fiscal_position_no_tax_dest(self):
fpos_no_tax_dest = self.env['account.fiscal.position'].create({'name': 'Test Fiscal Position'})
account_fpos = self.env['account.fiscal.position.account'].create({
'position_id': fpos_no_tax_dest.id,
'account_src_id': self.sale_account.id,
'account_dest_id': self.other_sale_account.id,
})
tax_fpos = self.env['account.fiscal.position.tax'].create({
'position_id': fpos_no_tax_dest.id,
'tax_src_id': self.taxes['tax7'].id,
})
fpos_no_tax_dest.write({
'account_ids': [(6, 0, account_fpos.ids)],
'tax_ids': [(6, 0, tax_fpos.ids)],
})
return fpos_no_tax_dest
def test_01_no_invoice_fpos(self):
""" orders without invoice
Orders
======
+---------+----------+---------------+----------+-----+---------+-----------------+--------+
| order | payments | invoiced? | product | qty | untaxed | tax | total |
+---------+----------+---------------+----------+-----+---------+-----------------+--------+
| order 1 | cash | yes, customer | product1 | 10 | 109.90 | 18.68 [7%->17%] | 128.58 |
| | | | product2 | 10 | 181.73 | 18.17 [10%] | 199.90 |
| | | | product3 | 10 | 309.90 | 52.68 [7%->17%] | 362.58 |
+---------+----------+---------------+----------+-----+---------+-----------------+--------+
| order 2 | cash | yes, customer | product1 | 5 | 54.95 | 9.34 [7%->17%] | 64.29 |
| | | | product2 | 5 | 90.86 | 9.09 [10%] | 99.95 |
+---------+----------+---------------+----------+-----+---------+-----------------+--------+
| order 3 | bank | no | product2 | 5 | 90.86 | 9.09 [10%] | 99.95 |
| | | | product3 | 5 | 154.95 | 10.85 [7%] | 165.8 |
+---------+----------+---------------+----------+-----+---------+-----------------+--------+
Expected Result
===============
+---------------------+---------+
| account | balance |
+---------------------+---------+
| sale_account | -154.95 | (for the 7% base amount)
| sale_account | -90.86 | (for the 10% base amount)
| other_sale_account | -474.75 | (for the 17% base amount)
| other_sale_account | -272.59 | (for the 10% base amount)
| tax 17% | -80.70 |
| tax 10% | -36.35 |
| tax 7% | -10.85 |
| pos receivable bank | 265.75 |
| pos receivable cash | 855.30 |
+---------------------+---------+
| Total balance | 0.0 |
+---------------------+---------+
"""
self.customer.write({'property_account_position_id': self.fpos.id})
self.open_new_session()
# create orders
orders = []
orders.append(self.create_ui_order_data(
[(self.product1, 10), (self.product2, 10), (self.product3, 10)],
customer=self.customer
))
orders.append(self.create_ui_order_data(
[(self.product1, 5), (self.product2, 5)],
customer=self.customer,
))
orders.append(self.create_ui_order_data(
[(self.product2, 5), (self.product3, 5)],
payments=[(self.bank_pm, 265.75)],
))
# sync orders
order = self.env['pos.order'].create_from_ui(orders)
# check values before closing the session
self.assertEqual(3, self.pos_session.order_count)
orders_total = sum(order.amount_total for order in self.pos_session.order_ids)
self.assertAlmostEqual(orders_total, self.pos_session.total_payments_amount, msg='Total order amount should be equal to the total payment amount.')
# close the session
self.pos_session.action_pos_session_validate()
# check values after the session is closed
session_move = self.pos_session.move_id
sale_account_lines = session_move.line_ids.filtered(lambda line: line.account_id == self.sale_account)
lines_balance = [-154.95, -90.86]
self.assertEqual(len(sale_account_lines), len(lines_balance))
for balance, amount in zip(sorted(sale_account_lines.mapped('balance')), sorted(lines_balance)):
self.assertAlmostEqual(balance, amount)
other_sale_account_lines = session_move.line_ids.filtered(lambda line: line.account_id == self.other_sale_account)
lines_balance = [-474.75, -272.59]
self.assertEqual(len(other_sale_account_lines), len(lines_balance))
for balance, amount in zip(sorted(other_sale_account_lines.mapped('balance')), sorted(lines_balance)):
self.assertAlmostEqual(balance, amount)
receivable_line_bank = session_move.line_ids.filtered(lambda line: self.bank_pm.name in line.name)
self.assertAlmostEqual(receivable_line_bank.balance, 265.75)
receivable_line_cash = session_move.line_ids.filtered(lambda line: self.cash_pm.name in line.name)
self.assertAlmostEqual(receivable_line_cash.balance, 855.3)
manually_calculated_taxes = (-80.7, -36.35, -10.85)
tax_lines = session_move.line_ids.filtered(lambda line: line.account_id == self.tax_received_account)
self.assertAlmostEqual(len(manually_calculated_taxes), len(tax_lines.mapped('balance')))
for t1, t2 in zip(sorted(manually_calculated_taxes), sorted(tax_lines.mapped('balance'))):
self.assertAlmostEqual(t1, t2, msg='Taxes should be correctly combined.')
self.assertTrue(receivable_line_cash.full_reconcile_id)
def test_02_no_invoice_fpos_no_tax_dest(self):
""" Customer with fiscal position that maps a tax to no tax.
Orders
======
+---------+----------+---------------+----------+-----+---------+-------------+--------+
| order | payments | invoiced? | product | qty | untaxed | tax | total |
+---------+----------+---------------+----------+-----+---------+-------------+--------+
| order 1 | bank | yes, customer | product1 | 10 | 109.90 | 0 | 109.90 |
| | | | product2 | 10 | 181.73 | 18.17 [10%] | 199.90 |
| | | | product3 | 10 | 309.90 | 0 | 309.90 |
+---------+----------+---------------+----------+-----+---------+-------------+--------+
| order 2 | cash | yes, customer | product1 | 5 | 54.95 | 0 | 54.95 |
| | | | product2 | 5 | 90.86 | 9.09 [10%] | 99.95 |
+---------+----------+---------------+----------+-----+---------+-------------+--------+
| order 3 | bank | no | product2 | 5 | 90.86 | 9.09 [10%] | 99.95 |
| | | | product3 | 5 | 154.95 | 10.85 [7%] | 165.80 |
+---------+----------+---------------+----------+-----+---------+-------------+--------+
Expected Result
===============
+---------------------+---------+
| account | balance |
+---------------------+---------+
| sale_account | -154.95 | (for the 7% base amount)
| sale_account | -90.86 | (for the 10% base amount)
| other_sale_account | -272.59 | (for the 10% base amount)
| other_sale_account | -474.75 | (no tax)
| tax 10% | -36.35 |
| tax 7% | -10.85 |
| pos receivable bank | 885.45 |
| pos receivable cash | 154.9 |
+---------------------+---------+
| Total balance | 0.0 |
+---------------------+---------+
"""
self.customer.write({'property_account_position_id': self.fpos_no_tax_dest.id})
self.open_new_session()
# create orders
orders = []
orders.append(self.create_ui_order_data(
[(self.product1, 10), (self.product2, 10), (self.product3, 10)],
customer=self.customer,
payments=[(self.bank_pm, 619.7)],
))
orders.append(self.create_ui_order_data(
[(self.product1, 5), (self.product2, 5)],
customer=self.customer,
))
orders.append(self.create_ui_order_data(
[(self.product2, 5), (self.product3, 5)],
payments=[(self.bank_pm, 265.75)],
))
# sync orders
order = self.env['pos.order'].create_from_ui(orders)
# check values before closing the session
self.assertEqual(3, self.pos_session.order_count)
orders_total = sum(order.amount_total for order in self.pos_session.order_ids)
self.assertAlmostEqual(orders_total, self.pos_session.total_payments_amount, msg='Total order amount should be equal to the total payment amount.')
# close the session
self.pos_session.action_pos_session_validate()
# check values after the session is closed
session_move = self.pos_session.move_id
sale_account_lines = session_move.line_ids.filtered(lambda line: line.account_id == self.sale_account)
lines_balance = [-154.95, -90.86]
self.assertEqual(len(sale_account_lines), len(lines_balance))
for balance, amount in zip(sorted(sale_account_lines.mapped('balance')), sorted(lines_balance)):
self.assertAlmostEqual(balance, amount)
other_sale_account_lines = session_move.line_ids.filtered(lambda line: line.account_id == self.other_sale_account)
lines_balance = [-474.75, -272.59]
self.assertEqual(len(other_sale_account_lines), len(lines_balance))
for balance, amount in zip(sorted(other_sale_account_lines.mapped('balance')), sorted(lines_balance)):
self.assertAlmostEqual(balance, amount)
receivable_line_bank = session_move.line_ids.filtered(lambda line: self.bank_pm.name in line.name)
self.assertAlmostEqual(receivable_line_bank.balance, 885.45)
receivable_line_cash = session_move.line_ids.filtered(lambda line: self.cash_pm.name in line.name)
self.assertAlmostEqual(receivable_line_cash.balance, 154.9)
manually_calculated_taxes = [-36.35, -10.85]
tax_lines = session_move.line_ids.filtered(lambda line: line.account_id == self.tax_received_account)
self.assertAlmostEqual(len(manually_calculated_taxes), len(tax_lines.mapped('balance')))
for t1, t2 in zip(sorted(manually_calculated_taxes), sorted(tax_lines.mapped('balance'))):
self.assertAlmostEqual(t1, t2, msg='Taxes should be correctly combined.')
self.assertTrue(receivable_line_cash.full_reconcile_id)
def test_03_invoiced_fpos(self):
""" Invoice 2 orders.
Orders
======
+---------+----------+---------------------+----------+-----+---------+-----------------+--------+
| order | payments | invoiced? | product | qty | untaxed | tax | total |
+---------+----------+---------------------+----------+-----+---------+-----------------+--------+
| order 1 | bank | yes, customer | product1 | 10 | 109.90 | 18.68 [7%->17%] | 128.58 |
| | | | product2 | 10 | 181.73 | 18.17 [10%] | 199.90 |
| | | | product3 | 10 | 309.90 | 52.68 [7%->17%] | 362.58 |
+---------+----------+---------------------+----------+-----+---------+-----------------+--------+
| order 2 | cash | no, customer | product1 | 5 | 54.95 | 9.34 [7%->17%] | 64.29 |
| | | | product2 | 5 | 90.86 | 9.09 [10%] | 99.95 |
+---------+----------+---------------------+----------+-----+---------+-----------------+--------+
| order 3 | cash | yes, other_customer | product2 | 5 | 90.86 | 9.09 [10%] | 99.95 |
| | | | product3 | 5 | 154.95 | 10.85 [7%] | 165.80 |
+---------+----------+---------------------+----------+-----+---------+-----------------+--------+
Expected Result
===============
+---------------------+---------+
| account | balance |
+---------------------+---------+
| other_sale_account | -54.95 | (for the 17% base amount)
| other_sale_account | -90.86 | (for the 10% base amount)
| tax 10% | -9.09 |
| tax 17% | -9.34 |
| pos receivable cash | 429.99 |
| pos receivable bank | 691.06 |
| receivable | -691.06 |
| other receivable | -265.75 |
+---------------------+---------+
| Total balance | 0.0 |
+---------------------+---------+
"""
self.customer.write({'property_account_position_id': self.fpos.id})
self.open_new_session()
# create orders
orders = []
uid1 = self.create_random_uid()
orders.append(self.create_ui_order_data(
[(self.product1, 10), (self.product2, 10), (self.product3, 10)],
customer=self.customer,
payments=[(self.bank_pm, 691.06)],
is_invoiced=True,
uid=uid1
))
orders.append(self.create_ui_order_data(
[(self.product1, 5), (self.product2, 5)],
customer=self.customer,
))
uid2 = self.create_random_uid()
orders.append(self.create_ui_order_data(
[(self.product2, 5), (self.product3, 5)],
customer=self.other_customer,
is_invoiced=True,
uid=uid2,
))
# sync orders
order = self.env['pos.order'].create_from_ui(orders)
# check values before closing the session
self.assertEqual(3, self.pos_session.order_count)
orders_total = sum(order.amount_total for order in self.pos_session.order_ids)
self.assertAlmostEqual(orders_total, self.pos_session.total_payments_amount, msg='Total order amount should be equal to the total payment amount.')
invoiced_order_1 = self.pos_session.order_ids.filtered(lambda order: uid1 in order.pos_reference)
invoiced_order_2 = self.pos_session.order_ids.filtered(lambda order: uid2 in order.pos_reference)
self.assertTrue(invoiced_order_1, msg='Invoiced order 1 should exist.')
self.assertTrue(invoiced_order_2, msg='Invoiced order 2 should exist.')
self.assertTrue(invoiced_order_1.account_move, msg='Invoiced order 1 should have invoice (account_move).')
self.assertTrue(invoiced_order_2.account_move, msg='Invoiced order 2 should have invoice (account_move).')
# NOTE Tests of values in the invoice accounting lines is not done here.
# close the session
self.pos_session.action_pos_session_validate()
# check values after the session is closed
session_move = self.pos_session.move_id
sale_account_lines = session_move.line_ids.filtered(lambda line: line.account_id == self.sale_account)
self.assertFalse(sale_account_lines, msg='There should be no self.sale_account lines.')
other_sale_account_lines = session_move.line_ids.filtered(lambda line: line.account_id == self.other_sale_account)
lines_balance = [-54.95, -90.86]
self.assertEqual(len(other_sale_account_lines), len(lines_balance))
for balance, amount in zip(sorted(other_sale_account_lines.mapped('balance')), sorted(lines_balance)):
self.assertAlmostEqual(balance, amount)
receivable_line_bank = session_move.line_ids.filtered(lambda line: self.bank_pm.name in line.name)
self.assertAlmostEqual(receivable_line_bank.balance, 691.06)
receivable_line_cash = session_move.line_ids.filtered(lambda line: self.cash_pm.name in line.name)
self.assertAlmostEqual(receivable_line_cash.balance, 429.99)
manually_calculated_taxes = [-9.09, -9.34]
tax_lines = session_move.line_ids.filtered(lambda line: line.account_id == self.tax_received_account)
self.assertAlmostEqual(len(manually_calculated_taxes), len(tax_lines.mapped('balance')))
for t1, t2 in zip(sorted(manually_calculated_taxes), sorted(tax_lines.mapped('balance'))):
self.assertAlmostEqual(t1, t2, msg='Taxes should be correctly combined.')
receivable_line = session_move.line_ids.filtered(lambda line: line.account_id == self.receivable_account)
self.assertAlmostEqual(receivable_line.balance, -691.06, msg='That is not the correct receivable line balance.')
other_receivable_line = session_move.line_ids.filtered(lambda line: line.account_id == self.other_receivable_account)
self.assertAlmostEqual(other_receivable_line.balance, -265.75, msg='That is not the correct other receivable line balance.')
self.assertTrue(receivable_line_cash.full_reconcile_id)
self.assertTrue(receivable_line.full_reconcile_id)
self.assertTrue(other_receivable_line.full_reconcile_id)