Files
odoo_source/addons/point_of_sale/tests/common.py
T
Jacky (trj) a520745230 [IMP] point_of_sale: visual improvement
Small visual changes that should improve the user experience.
The loading demo data popup has been moved to the backend.
Created an Alert Popup that is less aggressive than an Error Popup

closes odoo/odoo#83780

Task-id: 2485062
Related: odoo/enterprise#26104
Signed-off-by: Masereel Pierre <pim@odoo.com>
2022-08-08 10:50:14 +02:00

726 lines
32 KiB
Python

# -*- coding: utf-8 -*-
from random import randint
from datetime import datetime
from odoo import fields, tools
from odoo.addons.stock_account.tests.test_anglo_saxon_valuation_reconciliation_common import ValuationReconciliationTestCommon
from odoo.tests.common import Form
from odoo.tests import tagged
import logging
_logger = logging.getLogger(__name__)
@tagged('post_install', '-at_install')
class TestPointOfSaleCommon(ValuationReconciliationTestCommon):
@classmethod
def setUpClass(cls, chart_template_ref=None):
super().setUpClass(chart_template_ref=chart_template_ref)
cls.company_data['company'].write({
'point_of_sale_update_stock_quantities': 'real',
})
cls.AccountBankStatement = cls.env['account.bank.statement']
cls.AccountBankStatementLine = cls.env['account.bank.statement.line']
cls.PosMakePayment = cls.env['pos.make.payment']
cls.PosOrder = cls.env['pos.order']
cls.PosSession = cls.env['pos.session']
cls.company = cls.company_data['company']
cls.product3 = cls.env['product.product'].create({
'name': 'Product 3',
'list_price': 450,
})
cls.product4 = cls.env['product.product'].create({
'name': 'Product 4',
'list_price': 750,
})
cls.partner1 = cls.env['res.partner'].create({'name': 'Partner 1'})
cls.partner4 = cls.env['res.partner'].create({'name': 'Partner 4'})
cls.pos_config = cls.env['pos.config'].create({
'name': 'Main',
'journal_id': cls.company_data['default_journal_sale'].id,
'invoice_journal_id': cls.company_data['default_journal_sale'].id,
})
cls.led_lamp = cls.env['product.product'].create({
'name': 'LED Lamp',
'available_in_pos': True,
'list_price': 0.90,
})
cls.whiteboard_pen = cls.env['product.product'].create({
'name': 'Whiteboard Pen',
'available_in_pos': True,
'list_price': 1.20,
})
cls.newspaper_rack = cls.env['product.product'].create({
'name': 'Newspaper Rack',
'available_in_pos': True,
'list_price': 1.28,
})
cls.cash_payment_method = cls.env['pos.payment.method'].create({
'name': 'Cash',
'receivable_account_id': cls.company_data['default_account_receivable'].id,
'journal_id': cls.company_data['default_journal_cash'].id,
'company_id': cls.env.company.id,
})
cls.bank_payment_method = cls.env['pos.payment.method'].create({
'name': 'Bank',
'journal_id': cls.company_data['default_journal_bank'].id,
'receivable_account_id': cls.company_data['default_account_receivable'].id,
'company_id': cls.env.company.id,
})
cls.credit_payment_method = cls.env['pos.payment.method'].create({
'name': 'Credit',
'receivable_account_id': cls.company_data['default_account_receivable'].id,
'split_transactions': True,
'company_id': cls.env.company.id,
})
cls.pos_config.write({'payment_method_ids': [(4, cls.credit_payment_method.id), (4, cls.bank_payment_method.id), (4, cls.cash_payment_method.id)]})
# Create POS journal
cls.pos_config.journal_id = cls.env['account.journal'].create({
'type': 'general',
'name': 'Point of Sale - Test',
'code': 'POSS - Test',
'company_id': cls.env.company.id,
'sequence': 20
})
# create a VAT tax of 10%, included in the public price
Tax = cls.env['account.tax']
account_tax_10_incl = Tax.create({
'name': 'VAT 10 perc Incl',
'amount_type': 'percent',
'amount': 10.0,
'price_include': True,
})
# assign this 10 percent tax on the [PCSC234] PC Assemble SC234 product
# as a sale tax
cls.product3.taxes_id = [(6, 0, [account_tax_10_incl.id])]
# create a VAT tax of 5%, which is added to the public price
account_tax_05_incl = Tax.create({
'name': 'VAT 5 perc Incl',
'amount_type': 'percent',
'amount': 5.0,
'price_include': False,
})
# create a second VAT tax of 5% but this time for a child company, to
# ensure that only product taxes of the current session's company are considered
#(this tax should be ignore when computing order's taxes in following tests)
account_tax_05_incl_chicago = Tax.create({
'name': 'VAT 05 perc Excl (US)',
'amount_type': 'percent',
'amount': 5.0,
'price_include': False,
'company_id': cls.company_data_2['company'].id,
})
cls.product4.company_id = False
# I assign those 5 percent taxes on the PCSC349 product as a sale taxes
cls.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_10_incl).mapped('invoice_repartition_line_ids')
refund_rep_lines = (account_tax_05_incl | account_tax_10_incl).mapped('refund_repartition_line_ids')
# Expense account, should just be something else than receivable/payable
(invoice_rep_lines | refund_rep_lines).write({'account_id': cls.company_data['default_account_tax_sale'].id})
@tagged('post_install', '-at_install')
class TestPoSCommon(ValuationReconciliationTestCommon):
""" 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.
"""
@classmethod
def setUpClass(cls, chart_template_ref=None):
super().setUpClass(chart_template_ref=chart_template_ref)
cls.company_data['company'].write({
'point_of_sale_update_stock_quantities': 'real',
'country_id': cls.env['res.country'].create({
'name': 'PoS Land',
'code': 'WOW',
}),
})
# Set basic defaults
cls.company = cls.company_data['company']
cls.pos_sale_journal = cls.env['account.journal'].create({
'type': 'general',
'name': 'Point of Sale Test',
'code': 'POSS',
'company_id': cls.company.id,
'sequence': 20
})
cls.sales_account = cls.company_data['default_account_revenue']
cls.invoice_journal = cls.company_data['default_journal_sale']
cls.receivable_account = cls.company_data['default_account_receivable']
cls.tax_received_account = cls.company_data['default_account_tax_sale']
cls.company.account_default_pos_receivable_account_id = cls.env['account.account'].create({
'code': 'X1012 - POS',
'name': 'Debtors - (POS)',
'reconcile': True,
'account_type': 'asset_receivable',
})
cls.pos_receivable_account = cls.company.account_default_pos_receivable_account_id
cls.pos_receivable_cash = cls.copy_account(cls.company.account_default_pos_receivable_account_id, {'name': 'POS Receivable Cash'})
cls.pos_receivable_bank = cls.copy_account(cls.company.account_default_pos_receivable_account_id, {'name': 'POS Receivable Bank'})
cls.outstanding_bank = cls.copy_account(cls.company.account_journal_payment_debit_account_id, {'name': 'Outstanding Bank'})
cls.c1_receivable = cls.copy_account(cls.receivable_account, {'name': 'Customer 1 Receivable'})
cls.other_receivable_account = cls.env['account.account'].create({
'name': 'Other Receivable',
'code': 'RCV00' ,
'account_type': 'asset_receivable',
'internal_group': 'asset',
'reconcile': True,
})
# company_currency can be different from `base.USD` depending on the localization installed
cls.company_currency = cls.company.currency_id
# other_currency is a currency different from the company_currency
# sometimes company_currency is different from USD, so handle appropriately.
cls.other_currency = cls.currency_data['currency']
cls.currency_pricelist = cls.env['product.pricelist'].create({
'name': 'Public Pricelist',
'currency_id': cls.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.
cls.basic_config = cls._create_basic_config()
cls.other_currency_config = cls._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
cls.categ_basic = cls.env.ref('product.product_category_all')
cls.env.company.anglo_saxon_accounting = True
cls.categ_anglo = cls._create_categ_anglo()
# other basics
cls.sale_account = cls.categ_basic.property_account_income_categ_id
cls.other_sale_account = cls.env['account.account'].search([
('company_id', '=', cls.company.id),
('account_type', '=', 'income'),
('id', '!=', cls.sale_account.id)
], limit=1)
# Set customers
cls.customer = cls.env['res.partner'].create({'name': 'Customer 1', 'property_account_receivable_id': cls.c1_receivable.id})
cls.other_customer = cls.env['res.partner'].create({'name': 'Other Customer', 'property_account_receivable_id': cls.other_receivable_account.id})
# Set taxes
# cls.taxes => dict
# keys: 'tax7', 'tax10'(price_include=True), 'tax_group_7_10'
cls.taxes = cls._create_taxes()
cls.stock_location_components = cls.env["stock.location"].create({
'name': 'Shelf 1',
'location_id': cls.company_data['default_warehouse'].lot_stock_id.id,
})
#####################
## private methods ##
#####################
@classmethod
def _create_basic_config(cls):
config = cls.env['pos.config'].create({
'name': 'PoS Shop Test',
'invoice_journal_id': cls.invoice_journal.id,
'journal_id': cls.pos_sale_journal.id,
'available_pricelist_ids': cls.currency_pricelist.ids,
'pricelist_id': cls.currency_pricelist.id,
})
cls.cash_pm1 = cls.env['pos.payment.method'].create({
'name': 'Cash',
'journal_id': cls.company_data['default_journal_cash'].id,
'receivable_account_id': cls.pos_receivable_cash.id,
'company_id': cls.env.company.id,
})
cls.bank_pm1 = cls.env['pos.payment.method'].create({
'name': 'Bank',
'journal_id': cls.company_data['default_journal_bank'].id,
'receivable_account_id': cls.pos_receivable_bank.id,
'outstanding_account_id': cls.outstanding_bank.id,
'company_id': cls.env.company.id,
})
cls.cash_split_pm1 = cls.cash_pm1.copy(default={
'name': 'Split (Cash) PM',
'split_transactions': True,
})
cls.bank_split_pm1 = cls.bank_pm1.copy(default={
'name': 'Split (Bank) PM',
'split_transactions': True,
})
cls.pay_later_pm = cls.env['pos.payment.method'].create({'name': 'Pay Later', 'split_transactions': True})
config.write({'payment_method_ids': [(4, cls.cash_split_pm1.id), (4, cls.bank_split_pm1.id), (4, cls.cash_pm1.id), (4, cls.bank_pm1.id), (4, cls.pay_later_pm.id)]})
return config
@classmethod
def _create_other_currency_config(cls):
(cls.other_currency.rate_ids | cls.company_currency.rate_ids).unlink()
cls.env['res.currency.rate'].create({
'rate': 0.5,
'currency_id': cls.other_currency.id,
'name': datetime.today().date(),
})
other_cash_journal = cls.env['account.journal'].create({
'name': 'Cash Other',
'type': 'cash',
'company_id': cls.company.id,
'code': 'CSHO',
'sequence': 10,
'currency_id': cls.other_currency.id
})
other_invoice_journal = cls.env['account.journal'].create({
'name': 'Customer Invoice Other',
'type': 'sale',
'company_id': cls.company.id,
'code': 'INVO',
'sequence': 11,
'currency_id': cls.other_currency.id
})
other_sales_journal = cls.env['account.journal'].create({
'name':'PoS Sale Other',
'type': 'sale',
'code': 'POSO',
'company_id': cls.company.id,
'sequence': 12,
'currency_id': cls.other_currency.id
})
other_bank_journal = cls.env['account.journal'].create({
'name': 'Bank Other',
'type': 'bank',
'company_id': cls.company.id,
'code': 'BNKO',
'sequence': 13,
'currency_id': cls.other_currency.id
})
other_pricelist = cls.env['product.pricelist'].create({
'name': 'Public Pricelist Other',
'currency_id': cls.other_currency.id,
})
cls.cash_pm2 = cls.env['pos.payment.method'].create({
'name': 'Cash Other',
'journal_id': other_cash_journal.id,
'receivable_account_id': cls.pos_receivable_cash.id,
})
cls.bank_pm2 = cls.env['pos.payment.method'].create({
'name': 'Bank Other',
'journal_id': other_bank_journal.id,
'receivable_account_id': cls.pos_receivable_bank.id,
'outstanding_account_id': cls.outstanding_bank.id,
})
config = cls.env['pos.config'].create({
'name': 'Shop Other',
'invoice_journal_id': other_invoice_journal.id,
'journal_id': other_sales_journal.id,
'use_pricelist': True,
'available_pricelist_ids': other_pricelist.ids,
'pricelist_id': other_pricelist.id,
'payment_method_ids': [cls.cash_pm2.id, cls.bank_pm2.id],
})
return config
@classmethod
def _create_categ_anglo(cls):
return cls.env['product.category'].create({
'name': 'Anglo',
'parent_id': False,
'property_cost_method': 'fifo',
'property_valuation': 'real_time',
'property_stock_account_input_categ_id': cls.company_data['default_account_stock_in'].id,
'property_stock_account_output_categ_id': cls.company_data['default_account_stock_out'].id,
})
@classmethod
def _create_taxes(cls):
""" Create taxes
tax7: 7%, excluded in product price
tax10: 10%, included in product price
tax21: 21%, included in product price
"""
def create_tag(name):
return cls.env['account.account.tag'].create({
'name': name,
'applicability': 'taxes',
'country_id': cls.env.company.country_id.id
})
cls.tax_tag_invoice_base = create_tag('Invoice Base tag')
cls.tax_tag_invoice_tax = create_tag('Invoice Tax tag')
cls.tax_tag_refund_base = create_tag('Refund Base tag')
cls.tax_tag_refund_tax = create_tag('Refund Tax tag')
def create_tax(percentage, price_include=False):
return cls.env['account.tax'].create({
'name': f'Tax {percentage}%',
'amount': percentage,
'price_include': price_include,
'amount_type': 'percent',
'include_base_amount': False,
'invoice_repartition_line_ids': [
(0, 0, {
'factor_percent': 100,
'repartition_type': 'base',
'tag_ids': [(6, 0, cls.tax_tag_invoice_base.ids)],
}),
(0, 0, {
'factor_percent': 100,
'repartition_type': 'tax',
'account_id': cls.tax_received_account.id,
'tag_ids': [(6, 0, cls.tax_tag_invoice_tax.ids)],
}),
],
'refund_repartition_line_ids': [
(0, 0, {
'factor_percent': 100,
'repartition_type': 'base',
'tag_ids': [(6, 0, cls.tax_tag_refund_base.ids)],
}),
(0, 0, {
'factor_percent': 100,
'repartition_type': 'tax',
'account_id': cls.tax_received_account.id,
'tag_ids': [(6, 0, cls.tax_tag_refund_tax.ids)],
}),
],
})
def create_tax_fixed(amount, price_include=False):
return cls.env['account.tax'].create({
'name': f'Tax fixed amount {amount}',
'amount': amount,
'price_include': price_include,
'include_base_amount': price_include,
'amount_type': 'fixed',
'invoice_repartition_line_ids': [
(0, 0, {
'factor_percent': 100,
'repartition_type': 'base',
'tag_ids': [(6, 0, cls.tax_tag_invoice_base.ids)],
}),
(0, 0, {
'factor_percent': 100,
'repartition_type': 'tax',
'account_id': cls.tax_received_account.id,
'tag_ids': [(6, 0, cls.tax_tag_invoice_tax.ids)],
}),
],
'refund_repartition_line_ids': [
(0, 0, {
'factor_percent': 100,
'repartition_type': 'base',
'tag_ids': [(6, 0, cls.tax_tag_refund_base.ids)],
}),
(0, 0, {
'factor_percent': 100,
'repartition_type': 'tax',
'account_id': cls.tax_received_account.id,
'tag_ids': [(6, 0, cls.tax_tag_refund_tax.ids)],
}),
],
})
tax_fixed006 = create_tax_fixed(0.06, price_include=True)
tax_fixed012 = create_tax_fixed(0.12, price_include=True)
tax7 = create_tax(7, price_include=False)
tax10 = create_tax(10, price_include=True)
tax21 = create_tax(21, price_include=True)
tax_group_7_10 = tax7.copy()
with Form(tax_group_7_10) as tax:
tax.name = 'Tax 7+10%'
tax.amount_type = 'group'
tax.children_tax_ids.add(tax7)
tax.children_tax_ids.add(tax10)
return {
'tax7': tax7,
'tax10': tax10,
'tax21': tax21,
'tax_fixed006': tax_fixed006,
'tax_fixed012': tax_fixed012,
'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, pos_order_lines_ui_args, 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) pos_order_lines_ui_args: pairs of `ordered product` and `quantity`
or triplet of `ordered product`, `quantity` and discount
: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, discount=0.0):
price_unit = self.pricelist._get_product_price(product, quantity)
tax_ids = fiscal_position.map_tax(product.taxes_id)
price_unit_after_discount = price_unit * (1 - discount / 100.0)
tax_values = (
tax_ids.compute_all(price_unit_after_discount, self.currency, quantity)
if tax_ids
else {
'total_excluded': price_unit * quantity,
'total_included': price_unit * quantity,
}
)
return (0, 0, {
'discount': discount,
'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, discount and discount[0] or 0.0)
for product, quantity, *discount
in pos_order_lines_ui_args
]
# 2. generate the payments
total_amount_incl = sum(line[2]['price_subtotal_incl'] for line in order_lines)
if payments is None:
default_cash_pm = self.config.payment_method_ids.filtered(lambda pm: pm.is_cash_count)[:1]
if not default_cash_pm:
raise Exception('There should be a cash payment method set in the pos.config.')
payments = [create_payment(default_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.env.user.id,
'to_invoice': is_invoiced,
},
'id': uid,
'to_invoice': is_invoiced,
}
@classmethod
def create_product(cls, name, category, lst_price, standard_price=None, tax_ids=None, sale_account=None):
product = cls.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,
})
if sale_account:
product.property_account_income_id = sale_account
return product
@classmethod
def adjust_inventory(cls, products, quantities):
""" Adjust inventory of the given products
"""
for product, qty in zip(products, quantities):
cls.env['stock.quant'].with_context(inventory_mode=True).create({
'product_id': product.id,
'inventory_quantity': qty,
'location_id': cls.stock_location_components.id,
}).action_apply_inventory()
def open_new_session(self, opening_cash=0):
""" 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.
* pos_session : the current_session_id of config
* currency : currency of the current pos.session
* pricelist : the default pricelist of the session
"""
self.config.open_ui()
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.pos_session.set_cashbox_pos(opening_cash, None)
return self.pos_session
def _run_test(self, args):
pos_session = self._start_pos_session(args['payment_methods'], args.get('opening_cash', 0))
_logger.info('DONE: Start session.')
orders_map = self._create_orders(args['orders'])
_logger.info('DONE: Orders created.')
before_closing_cb = args.get('before_closing_cb')
if before_closing_cb:
before_closing_cb()
_logger.info('DONE: Call of before_closing_cb.')
self._check_invoice_journal_entries(pos_session, orders_map, expected_values=args['journal_entries_before_closing'])
_logger.info('DONE: Checks for journal entries before closing the session.')
total_cash_payment = sum(pos_session.mapped('order_ids.payment_ids').filtered(lambda payment: payment.payment_method_id.type == 'cash').mapped('amount'))
pos_session.post_closing_cash_details(total_cash_payment)
pos_session.close_session_from_ui()
after_closing_cb = args.get('after_closing_cb')
if after_closing_cb:
after_closing_cb()
_logger.info('DONE: Call of after_closing_cb.')
self._check_session_journal_entries(pos_session, expected_values=args['journal_entries_after_closing'])
_logger.info('DONE: Checks for journal entries after closing the session.')
def _start_pos_session(self, payment_methods, opening_cash):
self.config.write({'payment_method_ids': [(6, 0, payment_methods.ids)]})
pos_session = self.open_new_session(opening_cash)
self.assertEqual(self.config.payment_method_ids.ids, pos_session.payment_method_ids.ids, msg='Payment methods in the config should be the same as the session.')
return pos_session
def _create_orders(self, order_data_params):
'''Returns a dict mapping uid to its created pos.order record.'''
result = {}
for params in order_data_params:
order_data = self.create_ui_order_data(**params)
result[params['uid']] = self.env['pos.order'].browse([order['id'] for order in self.env['pos.order'].create_from_ui([order_data])])
return result
def _check_invoice_journal_entries(self, pos_session, orders_map, expected_values):
'''Checks the invoice, together with the payments, from each invoiced order.'''
currency_rounding = pos_session.currency_id.rounding
for uid in orders_map:
order = orders_map[uid]
if not order.is_invoiced:
continue
invoice = order.account_move
# allow not checking the invoice since pos is not creating the invoices
if expected_values[uid].get('invoice'):
self._assert_account_move(invoice, expected_values[uid]['invoice'])
_logger.info('DONE: Check of invoice for order %s.', uid)
for pos_payment in order.payment_ids:
if pos_payment.payment_method_id == self.pay_later_pm:
# Skip the pay later payments since there are no journal entries
# for them when invoicing.
continue
# This predicate is used to match the pos_payment's journal entry to the
# list of payments specified in the 'payments' field of the `_run_test`
# args.
def predicate(args):
payment_method, amount = args
first = payment_method == pos_payment.payment_method_id
second = tools.float_is_zero(pos_payment.amount - amount, precision_rounding=currency_rounding)
return first and second
self._find_then_assert_values(pos_payment.account_move_id, expected_values[uid]['payments'], predicate)
_logger.info('DONE: Check of invoice payment (%s, %s) for order %s.', pos_payment.payment_method_id.name, pos_payment.amount, uid)
def _check_session_journal_entries(self, pos_session, expected_values):
'''Checks the journal entries after closing the session excluding entries checked in `_check_invoice_journal_entries`.'''
currency_rounding = pos_session.currency_id.rounding
# check expected session journal entry
self._assert_account_move(pos_session.move_id, expected_values['session_journal_entry'])
_logger.info("DONE: Check of the session's account move.")
# check expected cash journal entries
for statement_line in pos_session.cash_register_id.line_ids:
def statement_line_predicate(args):
return tools.float_is_zero(statement_line.amount - args[0], precision_rounding=currency_rounding)
self._find_then_assert_values(statement_line.move_id, expected_values['cash_statement'], statement_line_predicate)
_logger.info("DONE: Check of cash statement lines.")
# check expected bank payments
for bank_payment in pos_session.bank_payment_ids:
def bank_payment_predicate(args):
return tools.float_is_zero(bank_payment.amount - args[0], precision_rounding=currency_rounding)
self._find_then_assert_values(bank_payment.move_id, expected_values['bank_payments'], bank_payment_predicate)
_logger.info("DONE: Check of bank account payments.")
def _find_then_assert_values(self, account_move, source_of_expected_vals, predicate):
expected_move_vals = next(move_vals for args, move_vals in source_of_expected_vals if predicate(args))
self._assert_account_move(account_move, expected_move_vals)
def _assert_account_move(self, account_move, expected_account_move_vals):
if expected_account_move_vals:
# We allow partial checks of the lines of the account move if `line_ids_predicate` is specified.
# This means that only those that satisfy the predicate are compared to the expected account move line_ids.
line_ids_predicate = expected_account_move_vals.pop('line_ids_predicate', lambda _: True)
self.assertRecordValues(account_move.line_ids.filtered(line_ids_predicate), expected_account_move_vals.pop('line_ids'))
self.assertRecordValues(account_move, [expected_account_move_vals])
else:
# if the expected_account_move_vals is falsy, the account_move should be falsy.
self.assertFalse(account_move)