summaryrefslogtreecommitdiff
path: root/addons
diff options
context:
space:
mode:
authorMartin Trigaux <mat@odoo.com>2017-03-29 11:58:48 +0200
committerMartin Trigaux <mat@odoo.com>2018-08-13 11:08:03 +0200
commit0222fdfdc749b35b0e59f4e10e7177197c126eba (patch)
treeff8257eef120f574d4cd3488c5de4d70cadb6a39 /addons
parent68616ea74687112e2c92934630752fd74e55a4f9 (diff)
[IMP] point_of_sale: trust client totals
The amounts sent by the point of sale should be trusted instead of recomputing it server side. The previous compution could introduce differences in amounts. The pos.orders should only be created from the pos interface. Turn the compute methods into onchange to still be possible to do refund but disable edition from the backend. Change amount_paid computation with the one server side The way amount_paid is computed was not coherant Remove pos.discount model as never used from the server interface Adapt tests to use a computed amount_total and amount_paid Adapt tests to be closer to real use flows (no more manual creation of account move lines or pickings, it should be automatic) Remove tests that no longer make sense (create + payment from backend is only relevant for refund, rest should be with create_from_ui) Add force_create in the view to make sure the onchange values are used
Diffstat (limited to 'addons')
-rw-r--r--addons/point_of_sale/__manifest__.py1
-rw-r--r--addons/point_of_sale/models/pos_order.py56
-rw-r--r--addons/point_of_sale/static/src/js/models.js4
-rw-r--r--addons/point_of_sale/tests/test_anglo_saxon.py11
-rw-r--r--addons/point_of_sale/tests/test_point_of_sale_flow.py247
-rw-r--r--addons/point_of_sale/views/pos_order_view.xml18
-rw-r--r--addons/point_of_sale/wizard/__init__.py1
-rw-r--r--addons/point_of_sale/wizard/pos_discount.py17
-rw-r--r--addons/point_of_sale/wizard/pos_discount.xml27
9 files changed, 158 insertions, 224 deletions
diff --git a/addons/point_of_sale/__manifest__.py b/addons/point_of_sale/__manifest__.py
index bdc5f4983ec..1cb4f10517e 100644
--- a/addons/point_of_sale/__manifest__.py
+++ b/addons/point_of_sale/__manifest__.py
@@ -16,7 +16,6 @@
'data/digest_data.xml',
'wizard/pos_box.xml',
'wizard/pos_details.xml',
- 'wizard/pos_discount.xml',
'wizard/pos_open_statement.xml',
'wizard/pos_payment.xml',
'views/pos_templates.xml',
diff --git a/addons/point_of_sale/models/pos_order.py b/addons/point_of_sale/models/pos_order.py
index 547fd76565e..4fa895bd939 100644
--- a/addons/point_of_sale/models/pos_order.py
+++ b/addons/point_of_sale/models/pos_order.py
@@ -43,6 +43,10 @@ class PosOrder(models.Model):
'date_order': ui_order['creation_date'],
'fiscal_position_id': ui_order['fiscal_position_id'],
'pricelist_id': ui_order['pricelist_id'],
+ 'amount_paid': ui_order['amount_paid'],
+ 'amount_total': ui_order['amount_total'],
+ 'amount_tax': ui_order['amount_tax'],
+ 'amount_return': ui_order['amount_return'],
}
def _payment_fields(self, ui_paymentline):
@@ -463,10 +467,11 @@ class PosOrder(models.Model):
default=lambda self: self.env.uid,
states={'done': [('readonly', True)], 'invoiced': [('readonly', True)]},
)
- amount_tax = fields.Float(compute='_compute_amount_all', string='Taxes', digits=0)
- amount_total = fields.Float(compute='_compute_amount_all', string='Total', digits=0)
- amount_paid = fields.Float(compute='_compute_amount_all', string='Paid', states={'draft': [('readonly', False)]}, readonly=True, digits=0)
- amount_return = fields.Float(compute='_compute_amount_all', string='Returned', digits=0)
+ amount_tax = fields.Float(string='Taxes', digits=0, readonly=True, required=True)
+ amount_total = fields.Float(string='Total', digits=0, readonly=True, required=True)
+ amount_paid = fields.Float(string='Paid', states={'draft': [('readonly', False)]},
+ readonly=True, digits=0, required=True)
+ amount_return = fields.Float(string='Returned', digits=0, required=True, readonly=True)
lines = fields.One2many('pos.order.line', 'order_id', string='Order Lines', states={'draft': [('readonly', False)]}, readonly=True, copy=True)
statement_ids = fields.One2many('account.bank.statement.line', 'pos_statement_id', string='Payments', states={'draft': [('readonly', False)]}, readonly=True)
pricelist_id = fields.Many2one('product.pricelist', string='Pricelist', required=True, states={
@@ -505,10 +510,9 @@ class PosOrder(models.Model):
states={'draft': [('readonly', False)]},
)
- @api.depends('statement_ids', 'lines.price_subtotal_incl', 'lines.discount')
- def _compute_amount_all(self):
+ @api.onchange('statement_ids', 'lines', 'lines')
+ def _onchange_amount_all(self):
for order in self:
- order.amount_paid = order.amount_return = order.amount_tax = 0.0
currency = order.pricelist_id.currency_id
order.amount_paid = sum(payment.amount for payment in order.statement_ids)
order.amount_return = sum(payment.amount < 0 and payment.amount or 0 for payment in order.statement_ids)
@@ -861,10 +865,12 @@ class PosOrder(models.Model):
def add_payment(self, data):
"""Create a new payment for the order"""
+ self.ensure_one()
args = self._prepare_bank_statement_line_payment_values(data)
context = dict(self.env.context)
context.pop('pos_session_id', False)
self.env['account.bank.statement.line'].with_context(context).create(args)
+ self.amount_paid = sum(payment.amount for payment in self.statement_ids)
return args.get('statement_id', False)
@api.multi
@@ -882,6 +888,9 @@ class PosOrder(models.Model):
'date_order': fields.Datetime.now(),
'pos_reference': order.pos_reference,
'lines': False,
+ 'amount_tax': -order.amount_tax,
+ 'amount_total': -order.amount_total,
+ 'amount_paid': 0,
})
for line in order.lines:
clone_line = line.copy({
@@ -889,6 +898,8 @@ class PosOrder(models.Model):
'name': line.name + _(' REFUND'),
'order_id': clone.id,
'qty': -line.qty,
+ 'price_subtotal': -line.price_subtotal,
+ 'price_subtotal_incl': -line.price_subtotal_incl,
})
PosOrder += clone
@@ -931,8 +942,10 @@ class PosOrderLine(models.Model):
product_id = fields.Many2one('product.product', string='Product', domain=[('sale_ok', '=', True)], required=True, change_default=True)
price_unit = fields.Float(string='Unit Price', digits=0)
qty = fields.Float('Quantity', digits=dp.get_precision('Product Unit of Measure'), default=1)
- price_subtotal = fields.Float(compute='_compute_amount_line_all', digits=0, string='Subtotal w/o Tax')
- price_subtotal_incl = fields.Float(compute='_compute_amount_line_all', digits=0, string='Subtotal')
+ price_subtotal = fields.Float(string='Subtotal w/o Tax', digits=0,
+ readonly=True, required=True)
+ price_subtotal_incl = fields.Float(string='Subtotal', digits=0,
+ readonly=True, required=True)
discount = fields.Float(string='Discount (%)', digits=0, default=0.0)
order_id = fields.Many2one('pos.order', string='Order Ref', ondelete='cascade')
create_date = fields.Datetime(string='Creation Date', readonly=True)
@@ -965,17 +978,22 @@ class PosOrderLine(models.Model):
values['name'] = self.env['ir.sequence'].next_by_code('pos.order.line')
return super(PosOrderLine, self).create(values)
- @api.depends('price_unit', 'tax_ids', 'qty', 'discount', 'product_id')
- def _compute_amount_line_all(self):
+ @api.onchange('price_unit', 'tax_ids', 'qty', 'discount', 'product_id')
+ def _onchange_amount_line_all(self):
for line in self:
- fpos = line.order_id.fiscal_position_id
- tax_ids_after_fiscal_position = fpos.map_tax(line.tax_ids, line.product_id, line.order_id.partner_id) if fpos else line.tax_ids
- price = line.price_unit * (1 - (line.discount or 0.0) / 100.0)
- taxes = tax_ids_after_fiscal_position.compute_all(price, line.order_id.pricelist_id.currency_id, line.qty, product=line.product_id, partner=line.order_id.partner_id)
- line.update({
- 'price_subtotal_incl': taxes['total_included'],
- 'price_subtotal': taxes['total_excluded'],
- })
+ res = line._compute_amount_line_all()
+ line.update(res)
+
+ def _compute_amount_line_all(self):
+ self.ensure_one()
+ fpos = self.order_id.fiscal_position_id
+ tax_ids_after_fiscal_position = fpos.map_tax(self.tax_ids, self.product_id, self.order_id.partner_id) if fpos else self.tax_ids
+ price = self.price_unit * (1 - (self.discount or 0.0) / 100.0)
+ taxes = tax_ids_after_fiscal_position.compute_all(price, self.order_id.pricelist_id.currency_id, self.qty, product=self.product_id, partner=self.order_id.partner_id)
+ return {
+ 'price_subtotal_incl': taxes['total_included'],
+ 'price_subtotal': taxes['total_excluded'],
+ }
@api.onchange('product_id')
def _onchange_product_id(self):
diff --git a/addons/point_of_sale/static/src/js/models.js b/addons/point_of_sale/static/src/js/models.js
index 91e234dafc5..b4a1e0ff6ae 100644
--- a/addons/point_of_sale/static/src/js/models.js
+++ b/addons/point_of_sale/static/src/js/models.js
@@ -1530,6 +1530,8 @@ exports.Orderline = Backbone.Model.extend({
return {
qty: this.get_quantity(),
price_unit: this.get_unit_price(),
+ price_subtotal: this.get_price_without_tax(),
+ price_subtotal_incl: this.get_price_with_tax(),
discount: this.get_discount(),
product_id: this.get_product().id,
tax_ids: [[6, false, _.map(this.get_applicable_taxes(), function(tax){ return tax.id; })]],
@@ -2049,7 +2051,7 @@ exports.Order = Backbone.Model.extend({
}, this));
return {
name: this.get_name(),
- amount_paid: this.get_total_paid(),
+ amount_paid: this.get_total_paid() - this.get_change(),
amount_total: this.get_total_with_tax(),
amount_tax: this.get_total_tax(),
amount_return: this.get_change(),
diff --git a/addons/point_of_sale/tests/test_anglo_saxon.py b/addons/point_of_sale/tests/test_anglo_saxon.py
index b6d27af9505..7fae0de8b83 100644
--- a/addons/point_of_sale/tests/test_anglo_saxon.py
+++ b/addons/point_of_sale/tests/test_anglo_saxon.py
@@ -70,8 +70,14 @@ class TestAngloSaxonFlow(TestAngloSaxonCommon):
'product_id': self.product.id,
'price_unit': 450,
'discount': 0.0,
- 'qty': 1.0
- })]
+ 'qty': 1.0,
+ 'price_subtotal': 450,
+ 'price_subtotal_incl': 450,
+ })],
+ 'amount_total': 450,
+ 'amount_tax': 0,
+ 'amount_paid': 0,
+ 'amount_return': 0,
})
# I make a payment to fully pay the order
@@ -87,6 +93,7 @@ class TestAngloSaxonFlow(TestAngloSaxonCommon):
# I check that the order is marked as paid
self.assertEqual(self.pos_order_pos0.state, 'paid', 'Order should be in paid state.')
+ self.assertEqual(self.pos_order_pos0.amount_paid, 450, 'Amount paid for the order should be updated.')
# I close the current session to generate the journal entries
self.pos_config.current_session_id.action_pos_session_close()
diff --git a/addons/point_of_sale/tests/test_point_of_sale_flow.py b/addons/point_of_sale/tests/test_point_of_sale_flow.py
index 94db310f3e6..4f982486bf0 100644
--- a/addons/point_of_sale/tests/test_point_of_sale_flow.py
+++ b/addons/point_of_sale/tests/test_point_of_sale_flow.py
@@ -17,104 +17,6 @@ class TestPointOfSaleFlow(TestPointOfSaleCommon):
# open all statements/cash registers
self.env['pos.open.statement'].create({}).open_statement()
- def test_order_to_payment(self):
- """
- In order to test the Point of Sale in module, I will do a full flow from the sale to the payment and invoicing.
- I will use two products, one with price including a 10% tax, the other one with 5% tax excluded from the price.
- """
- # I click on create a new session button
- self.pos_config.open_session_cb()
-
- # 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)
- self.pos_order_pos0 = self.PosOrder.create({
- 'company_id': self.company_id,
- 'pricelist_id': self.partner1.property_product_pricelist.id,
- 'partner_id': self.partner1.id,
- 'lines': [(0, 0, {
- 'name': "OL/0001",
- 'product_id': self.product3.id,
- 'price_unit': 450,
- 'discount': 0.0,
- 'qty': 2.0,
- 'tax_ids': [(6, 0, self.product3.taxes_id.ids)],
- }), (0, 0, {
- 'name': "OL/0002",
- 'product_id': self.product4.id,
- 'price_unit': 300,
- 'discount': 0.0,
- 'qty': 3.0,
- 'tax_ids': [(6, 0, self.product4.taxes_id.ids)],
- })]
- })
-
- # I check that the total of the order is equal to 450*2 + 300*3*1.05
- # and the tax of the order is equal to 900 -(450 * 2 / 1.1) +
- # 300*0.05*3
- self.assertLess(
- abs(self.pos_order_pos0.amount_total - (450 * 2 + 300 * 3 * 1.05)),
- 0.01, 'The order has a wrong amount, tax included.')
-
- self.assertLess(
- abs(self.pos_order_pos0.amount_tax - (900 - (450 * 2 / 1.1) + 300 * 0.05 * 3)),
- 0.01, 'The order has a wrong tax amount.')
-
- # I want to add a global discount of 5 percent using the wizard
-
- self.pos_discount_0 = self.env['pos.discount'].create({'discount': 5.0})
-
- context = {"active_model": "pos.order", "active_ids": [self.pos_order_pos0.id], "active_id": self.pos_order_pos0.id}
-
- # I click the apply button to set the discount on all lines
- self.pos_discount_0.with_context(context).apply_discount()
-
- # 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),
- 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
- })
-
- # I click on the validate button to register the payment.
- context_payment = {'active_id': self.pos_order_pos0.id}
- self.pos_make_payment_0.with_context(context_payment).check()
-
- # I check that the order is not marked as paid yet
- self.assertEqual(self.pos_order_pos0.state, 'draft', 'Order should be in draft state.')
-
- # On the second payment proposition, I check that it proposes me the
- # remaining balance which is 1790.0 EUR
- 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.")
-
- #'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
- })
-
- # I click on the validate button to register the payment.
- self.pos_make_payment_1.with_context(context_make_payment).check()
-
- # I check that the order is marked as paid
- 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()
-
- # 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.")
-
def test_order_refund(self):
# I create a new PoS order with 2 lines
order = self.PosOrder.create({
@@ -128,6 +30,8 @@ class TestPointOfSaleFlow(TestPointOfSaleCommon):
'discount': 5.0,
'qty': 2.0,
'tax_ids': [(6, 0, self.product3.taxes_id.ids)],
+ 'price_subtotal': 450 * (1 - 5/100.0) * 2,
+ 'price_subtotal_incl': 450 * (1 - 5/100.0) * 2,
}), (0, 0, {
'name': "OL/0002",
'product_id': self.product4.id,
@@ -135,7 +39,13 @@ class TestPointOfSaleFlow(TestPointOfSaleCommon):
'discount': 5.0,
'qty': 3.0,
'tax_ids': [(6, 0, self.product4.taxes_id.ids)],
- })]
+ 'price_subtotal': 300 * (1 - 5/100.0) * 3,
+ 'price_subtotal_incl': 300 * (1 - 5/100.0) * 3,
+ })],
+ 'amount_total': 1710.0,
+ 'amount_tax': 0.0,
+ 'amount_paid': 1710.0,
+ 'amount_return': 0.0,
})
# I create a refund
@@ -163,11 +73,22 @@ class TestPointOfSaleFlow(TestPointOfSaleCommon):
TODO: Check the negative picking after changing the picking relation to One2many (also for a mixed use case),
check the quantity, the locations and return picking logic
"""
+
+ def compute_tax(product, price, qty=1, taxes=None):
+ if taxes is None:
+ taxes = product.taxes_id.filtered(lambda t: t.company_id.id == self.env.user.id)
+ currency = self.pos_config.pricelist_id.currency_id
+ res = taxes.compute_all(price, currency, qty, product=product)
+ untax = res['total_excluded']
+ return untax, sum(tax.get('amount', 0.0) for tax in res['taxes'])
+
# I click on create a new session button
self.pos_config.open_session_cb()
# I create a PoS order with 2 units of PCSC234 at 450 EUR
# and 3 units of PCSC349 at 300 EUR.
+ untax1, atax1 = compute_tax(self.product3, 450, 2)
+ untax2, atax2 = compute_tax(self.product4, 300, 3)
self.pos_order_pos1 = self.PosOrder.create({
'company_id': self.company_id,
'pricelist_id': self.partner1.property_product_pricelist.id,
@@ -179,6 +100,8 @@ class TestPointOfSaleFlow(TestPointOfSaleCommon):
'discount': 0.0,
'qty': 2.0,
'tax_ids': [(6, 0, self.product3.taxes_id.ids)],
+ 'price_subtotal': untax1,
+ 'price_subtotal_incl': untax1 + atax1,
}), (0, 0, {
'name': "OL/0002",
'product_id': self.product4.id,
@@ -186,7 +109,13 @@ class TestPointOfSaleFlow(TestPointOfSaleCommon):
'discount': 0.0,
'qty': 3.0,
'tax_ids': [(6, 0, self.product4.taxes_id.ids)],
- })]
+ 'price_subtotal': untax2,
+ 'price_subtotal_incl': untax2 + atax2,
+ })],
+ 'amount_tax': atax1 + atax2,
+ 'amount_total': untax1 + untax2 + atax1 + atax2,
+ 'amount_paid': 0,
+ 'amount_return': 0,
})
context_make_payment = {
@@ -194,13 +123,13 @@ class TestPointOfSaleFlow(TestPointOfSaleCommon):
"active_id": self.pos_order_pos1.id
}
self.pos_make_payment_2 = self.PosMakePayment.with_context(context_make_payment).create({
- 'amount': 1845
+ 'amount': untax1 + untax2 + atax1 + atax2
})
# I click on the validate button to register the payment.
context_payment = {'active_id': self.pos_order_pos1.id}
- self.pos_make_payment_2.with_context(context_payment).check()
+ self.pos_make_payment_2.with_context(context_payment).check()
# I check that the order is marked as paid
self.assertEqual(
self.pos_order_pos1.state,
@@ -208,18 +137,8 @@ class TestPointOfSaleFlow(TestPointOfSaleCommon):
'Order should be in paid state.'
)
- # I generate the journal entries
- self.pos_order_pos1._create_account_move_line()
-
- # I test that the generated journal entry is attached to the PoS order
- self.assertTrue(
- self.pos_order_pos1.account_move,
- "Journal entry has not been attached to Pos order."
- )
-
- # I test that the pickings are created as expected
+ # I test that the pickings are created as expected during payment
# One picking attached and having all the positive move lines in the correct state
- self.pos_order_pos1.create_picking()
self.assertEqual(
self.pos_order_pos1.picking_id.state,
'done',
@@ -231,6 +150,9 @@ class TestPointOfSaleFlow(TestPointOfSaleCommon):
'Move Lines should be in done state.'
)
+ # I create a second order
+ untax1, atax1 = compute_tax(self.product3, 450, -2)
+ untax2, atax2 = compute_tax(self.product4, 300, -3)
self.pos_order_pos2 = self.PosOrder.create({
'company_id': self.company_id,
'pricelist_id': self.partner1.property_product_pricelist.id,
@@ -242,6 +164,8 @@ class TestPointOfSaleFlow(TestPointOfSaleCommon):
'discount': 0.0,
'qty': (-2.0),
'tax_ids': [(6, 0, self.product3.taxes_id.ids)],
+ 'price_subtotal': untax1,
+ 'price_subtotal_incl': untax1 + atax1,
}), (0, 0, {
'name': "OL/0004",
'product_id': self.product4.id,
@@ -249,7 +173,13 @@ class TestPointOfSaleFlow(TestPointOfSaleCommon):
'discount': 0.0,
'qty': (-3.0),
'tax_ids': [(6, 0, self.product4.taxes_id.ids)],
- })]
+ 'price_subtotal': untax2,
+ 'price_subtotal_incl': untax2 + atax2,
+ })],
+ 'amount_tax': atax1 + atax2,
+ 'amount_total': untax1 + untax2 + atax1 + atax2,
+ 'amount_paid': 0,
+ 'amount_return': 0,
})
context_make_payment = {
@@ -257,7 +187,7 @@ class TestPointOfSaleFlow(TestPointOfSaleCommon):
"active_id": self.pos_order_pos2.id
}
self.pos_make_payment_3 = self.PosMakePayment.with_context(context_make_payment).create({
- 'amount': (-1845)
+ 'amount': untax1 + untax2 + atax1 + atax2
})
# I click on the validate button to register the payment.
@@ -271,15 +201,6 @@ class TestPointOfSaleFlow(TestPointOfSaleCommon):
'Order should be in paid state.'
)
- # I generate the journal entries
- self.pos_order_pos2._create_account_move_line()
-
- # I test that the generated journal entry is attached to the PoS order
- self.assertTrue(
- self.pos_order_pos2.account_move,
- "Journal entry has not been attached to PoS order."
- )
-
# I test that the pickings are created as expected
# One picking attached and having all the positive move lines in the correct state
self.pos_order_pos2.create_picking()
@@ -294,6 +215,8 @@ class TestPointOfSaleFlow(TestPointOfSaleCommon):
'Move Lines should be in done state.'
)
+ untax1, atax1 = compute_tax(self.product3, 450, -2)
+ untax2, atax2 = compute_tax(self.product4, 300, 3)
self.pos_order_pos3 = self.PosOrder.create({
'company_id': self.company_id,
'pricelist_id': self.partner1.property_product_pricelist.id,
@@ -305,6 +228,8 @@ class TestPointOfSaleFlow(TestPointOfSaleCommon):
'discount': 0.0,
'qty': (-2.0),
'tax_ids': [(6, 0, self.product3.taxes_id.ids)],
+ 'price_subtotal': untax1,
+ 'price_subtotal_incl': untax1 + atax1,
}), (0, 0, {
'name': "OL/0006",
'product_id': self.product4.id,
@@ -312,7 +237,13 @@ class TestPointOfSaleFlow(TestPointOfSaleCommon):
'discount': 0.0,
'qty': 3.0,
'tax_ids': [(6, 0, self.product4.taxes_id.ids)],
- })]
+ 'price_subtotal': untax2,
+ 'price_subtotal_incl': untax2 + atax2,
+ })],
+ 'amount_tax': atax1 + atax2,
+ 'amount_total': untax1 + untax2 + atax1 + atax2,
+ 'amount_paid': 0,
+ 'amount_return': 0,
})
context_make_payment = {
@@ -320,7 +251,7 @@ class TestPointOfSaleFlow(TestPointOfSaleCommon):
"active_id": self.pos_order_pos3.id
}
self.pos_make_payment_4 = self.PosMakePayment.with_context(context_make_payment).create({
- 'amount': 45,
+ 'amount': untax1 + untax2 + atax1 + atax2,
})
# I click on the validate button to register the payment.
@@ -334,18 +265,8 @@ class TestPointOfSaleFlow(TestPointOfSaleCommon):
'Order should be in paid state.'
)
- # I generate the journal entries
- self.pos_order_pos3._create_account_move_line()
-
- # I test that the generated journal entry is attached to the PoS order
- self.assertTrue(
- self.pos_order_pos3.account_move,
- "Journal entry has not been attached to PoS order."
- )
-
# I test that the pickings are created as expected
# One picking attached and having all the positive move lines in the correct state
- self.pos_order_pos3.create_picking()
self.assertEqual(
self.pos_order_pos3.picking_id.state,
'done',
@@ -356,9 +277,22 @@ class TestPointOfSaleFlow(TestPointOfSaleCommon):
['done'],
'Move Lines should be in done state.'
)
+ # 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):
+ if taxes is None:
+ taxes = product.taxes_id.filtered(lambda t: t.company_id.id == self.env.user.id)
+ currency = self.pos_config.pricelist_id.currency_id
+ res = taxes.compute_all(price, currency, qty, product=product)
+ untax = res['total_excluded']
+ return untax, sum(tax.get('amount', 0.0) for tax in res['taxes'])
+
+ untax1, atax1 = compute_tax(self.product3, 450*0.95, 2)
+ untax2, atax2 = compute_tax(self.product4, 300*0.95, 3)
# I create a new PoS order with 2 units of PC1 at 450 EUR (Tax Incl) and 3 units of PCSC349 at 300 EUR. (Tax Excl)
self.pos_order_pos1 = self.PosOrder.create({
'company_id': self.company_id,
@@ -371,6 +305,8 @@ class TestPointOfSaleFlow(TestPointOfSaleCommon):
'discount': 5.0,
'qty': 2.0,
'tax_ids': [(6, 0, self.product3.taxes_id.ids)],
+ 'price_subtotal': untax1,
+ 'price_subtotal_incl': untax1 + atax1,
}), (0, 0, {
'name': "OL/0002",
'product_id': self.product4.id,
@@ -378,13 +314,19 @@ class TestPointOfSaleFlow(TestPointOfSaleCommon):
'discount': 5.0,
'qty': 3.0,
'tax_ids': [(6, 0, self.product4.taxes_id.ids)],
- })]
+ 'price_subtotal': untax2,
+ 'price_subtotal_incl': untax2 + atax2,
+ })],
+ 'amount_tax': atax1 + atax2,
+ 'amount_total': untax1 + untax2 + atax1 + atax2,
+ 'amount_paid': 0.0,
+ 'amount_return': 0.0,
})
# I click on the "Make Payment" wizard to pay the PoS order
context_make_payment = {"active_ids": [self.pos_order_pos1.id], "active_id": self.pos_order_pos1.id}
self.pos_make_payment = self.PosMakePayment.with_context(context_make_payment).create({
- 'amount': (450 * 2 + 300 * 3 * 1.05) * 0.95,
+ 'amount': untax1 + untax2 + atax1 + atax2,
})
# I click on the validate button to register the payment.
context_payment = {'active_id': self.pos_order_pos1.id}
@@ -401,8 +343,8 @@ class TestPointOfSaleFlow(TestPointOfSaleCommon):
# I test that the total of the attached invoice is correct
invoice = self.env['account.invoice'].browse(res['res_id'])
- self.assertEqual(
- float_compare(invoice.amount_total, 1752.75, precision_digits=2), 0, "Invoice not correct")
+ self.assertAlmostEqual(
+ invoice.amount_total, self.pos_order_pos1.amount_total, places=2, msg="Invoice not correct")
"""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"""
@@ -456,15 +398,14 @@ class TestPointOfSaleFlow(TestPointOfSaleCommon):
"""
Simulation of sales coming from the interface, even after closing the session
"""
- FROMPRODUCT = object()
- def compute_tax(product, price, taxes=FROMPRODUCT, qty=1):
- if taxes is FROMPRODUCT:
- taxes = product.taxes_id
+ def compute_tax(product, price, qty=1, taxes=None):
+ if not taxes:
+ taxes = product.taxes_id.filtered(lambda t: t.company_id.id == self.env.user.id)
currency = self.pos_config.pricelist_id.currency_id
- taxes = taxes.compute_all(price, currency, qty, product=product)['taxes']
- untax = price * qty
- return untax, sum(tax.get('amount', 0.0) for tax in taxes)
+ res = taxes.compute_all(price, currency, qty, product=product)
+ untax = res['total_excluded']
+ return untax, sum(tax.get('amount', 0.0) for tax in res['taxes'])
# I click on create a new session button
self.pos_config.open_session_cb()
@@ -478,7 +419,7 @@ class TestPointOfSaleFlow(TestPointOfSaleCommon):
'amount_return': 0,
'amount_tax': atax,
'amount_total': untax + atax,
- 'creation_date': fields.Datetime.now(),
+ 'creation_date': fields.Datetime.to_string(fields.Datetime.now()),
'fiscal_position_id': False,
'pricelist_id': self.pos_config.available_pricelist_ids[0].id,
'lines': [[0,
@@ -488,6 +429,8 @@ class TestPointOfSaleFlow(TestPointOfSaleCommon):
'pack_lot_ids': [],
'price_unit': 0.9,
'product_id': self.led_lamp.id,
+ 'price_subtotal': 0.9,
+ 'price_subtotal_incl': 1.04,
'qty': 1,
'tax_ids': [(6, 0, self.led_lamp.taxes_id.ids)]}]],
'name': 'Order 00042-003-0014',
@@ -512,7 +455,7 @@ class TestPointOfSaleFlow(TestPointOfSaleCommon):
'amount_return': 0,
'amount_tax': atax,
'amount_total': untax + atax,
- 'creation_date': fields.Datetime.now(),
+ 'creation_date': fields.Datetime.to_string(fields.Datetime.now()),
'fiscal_position_id': False,
'pricelist_id': self.pos_config.available_pricelist_ids[0].id,
'lines': [[0,
@@ -522,6 +465,8 @@ class TestPointOfSaleFlow(TestPointOfSaleCommon):
'pack_lot_ids': [],
'price_unit': 1.2,
'product_id': self.whiteboard_pen.id,
+ 'price_subtotal': 1.2,
+ 'price_subtotal_incl': 1.38,
'qty': 1,
'tax_ids': [(6, 0, self.whiteboard_pen.taxes_id.ids)]}]],
'name': 'Order 00043-003-0014',
@@ -546,7 +491,7 @@ class TestPointOfSaleFlow(TestPointOfSaleCommon):
'amount_return': 0,
'amount_tax': atax,
'amount_total': untax + atax,
- 'creation_date': fields.Datetime.now(),
+ 'creation_date': fields.Datetime.to_string(fields.Datetime.now()),
'fiscal_position_id': False,
'pricelist_id': self.pos_config.available_pricelist_ids[0].id,
'lines': [[0,
@@ -556,6 +501,8 @@ class TestPointOfSaleFlow(TestPointOfSaleCommon):
'pack_lot_ids': [],
'price_unit': 1.28,
'product_id': self.newspaper_rack.id,
+ 'price_subtotal': 1.28,
+ 'price_subtotal_incl': 1.47,
'qty': 1,
'tax_ids': [[6, False, self.newspaper_rack.taxes_id.ids]]}]],
'name': 'Order 00044-003-0014',
diff --git a/addons/point_of_sale/views/pos_order_view.xml b/addons/point_of_sale/views/pos_order_view.xml
index eaf712483a1..d5c61ec4049 100644
--- a/addons/point_of_sale/views/pos_order_view.xml
+++ b/addons/point_of_sale/views/pos_order_view.xml
@@ -41,8 +41,8 @@
<field name="discount" widget="monetary"/>
<field name="tax_ids_after_fiscal_position" widget="many2many_tags" string="Taxes"/>
<field name="tax_ids" widget="many2many_tags" invisible="1"/>
- <field name="price_subtotal" widget="monetary"/>
- <field name="price_subtotal_incl" widget="monetary"/>
+ <field name="price_subtotal" widget="monetary" force_save="1"/>
+ <field name="price_subtotal_incl" widget="monetary" force_save="1"/>
</tree>
<form string="Order lines">
<group col="4">
@@ -50,8 +50,8 @@
<field name="qty"/>
<field name="discount" widget="monetary"/>
<field name="price_unit" widget="monetary"/>
- <field name="price_subtotal" invisible="1" widget="monetary"/>
- <field name="price_subtotal_incl" invisible="1" widget="monetary"/>
+ <field name="price_subtotal" invisible="1" widget="monetary" force_save="1"/>
+ <field name="price_subtotal_incl" invisible="1" widget="monetary" force_save="1"/>
<field name="tax_ids_after_fiscal_position" widget="many2many_tags" string="Taxes"/>
<field name="tax_ids" widget="many2many_tags" invisible="1"/>
<field name="notice"/>
@@ -59,13 +59,19 @@
</form>
</field>
<group class="oe_subtotal_footer oe_right" colspan="2" name="order_total">
- <field name="amount_tax" widget="monetary"/>
+ <field name="amount_tax"
+ force_save="1"
+ widget="monetary"/>
<div class="oe_subtotal_footer_separator oe_inline">
<label for="amount_total" />
<button name="button_dummy"
states="draft" string="(update)" class="oe_edit_only oe_link"/>
</div>
- <field name="amount_total" nolabel="1" class="oe_subtotal_footer_separator" widget="monetary"/>
+ <field name="amount_total"
+ force_save="1"
+ nolabel="1"
+ class="oe_subtotal_footer_separator"
+ widget="monetary"/>
</group>
<div class="oe_clear"/>
</page>
diff --git a/addons/point_of_sale/wizard/__init__.py b/addons/point_of_sale/wizard/__init__.py
index 5801aa23b37..d85d8a81c86 100644
--- a/addons/point_of_sale/wizard/__init__.py
+++ b/addons/point_of_sale/wizard/__init__.py
@@ -3,6 +3,5 @@
from . import pos_box
from . import pos_details
-from . import pos_discount
from . import pos_open_statement
from . import pos_payment
diff --git a/addons/point_of_sale/wizard/pos_discount.py b/addons/point_of_sale/wizard/pos_discount.py
deleted file mode 100644
index d45f81055b8..00000000000
--- a/addons/point_of_sale/wizard/pos_discount.py
+++ /dev/null
@@ -1,17 +0,0 @@
-# -*- coding: utf-8 -*-
-# Part of Odoo. See LICENSE file for full copyright and licensing details.
-
-from odoo import api, fields, models
-
-
-class PosDiscount(models.TransientModel):
- _name = 'pos.discount'
- _description = 'Add a Global Discount'
-
- discount = fields.Float(string='Discount (%)', required=True, digits=(16, 2), default=5)
-
- @api.multi
- def apply_discount(self):
- self.ensure_one()
- for order in self.env['pos.order'].browse(self.env.context.get('active_id', False)):
- order.lines.write({'discount': self.discount})
diff --git a/addons/point_of_sale/wizard/pos_discount.xml b/addons/point_of_sale/wizard/pos_discount.xml
deleted file mode 100644
index ff466c2dadc..00000000000
--- a/addons/point_of_sale/wizard/pos_discount.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<odoo>
- <record id="view_pos_discount" model="ir.ui.view">
- <field name="name">pos.discount.form</field>
- <field name="model">pos.discount</field>
- <field name="arch" type="xml">
- <form string="Apply Discount">
- <group>
- <field name="discount"/>
- </group>
- <footer>
- <button name="apply_discount" string="Apply Discount" type="object" class="btn-primary"/>
- <button string="Cancel" class="btn-secondary" special="cancel" />
- </footer>
- </form>
- </field>
- </record>
-
- <record id="action_pos_discount" model="ir.actions.act_window">
- <field name="name">Apply Discount</field>
- <field name="type">ir.actions.act_window</field>
- <field name="res_model">pos.discount</field>
- <field name="view_type">form</field>
- <field name="view_mode">form</field>
- <field name="target">new</field>
- </record>
-</odoo>