Browse Source

[MIG][pos_payment_change] from 8.0 to 12.0

- Black python code
- OCA Convention
- Add tests
- add configuration on pos.config, with two option 'refund' or 'update'
- The 'refund' option makes the module compatible with (French) certification
- make the module compatible with pos_order_return
pull/480/head
Sylvain LE GAL 5 years ago
parent
commit
0d0d4275b9
  1. 91
      pos_change_payment/README.rst
  2. 6
      pos_change_payment/__init__.py
  3. 23
      pos_change_payment/__openerp__.py
  4. 168
      pos_change_payment/i18n/fr.po
  5. 9
      pos_change_payment/models/__init__.py
  6. 66
      pos_change_payment/models/account_bank_statement_line.py
  7. 72
      pos_change_payment/models/pos_change_payments_wizard.py
  8. 25
      pos_change_payment/models/pos_change_payments_wizard_line.py
  9. 25
      pos_change_payment/models/pos_make_payment.py
  10. 57
      pos_change_payment/models/pos_order.py
  11. 78
      pos_change_payment/models/pos_switch_journal_wizard.py
  12. 34
      pos_change_payment/readme/DESCRIPTION.rst
  13. BIN
      pos_change_payment/static/description/icon.png
  14. BIN
      pos_change_payment/static/description/pos_order_change_payments.png
  15. BIN
      pos_change_payment/static/description/pos_order_switch_payment.png
  16. 157
      pos_change_payment/tests/test_module.py
  17. 26
      pos_change_payment/views/action.xml
  18. 21
      pos_change_payment/views/view_account_bank_statement.xml
  19. 36
      pos_change_payment/views/view_pos_change_payments_wizard.xml
  20. 29
      pos_change_payment/views/view_pos_switch_journal_wizard.xml
  21. 8
      pos_payment_change/README.rst
  22. 2
      pos_payment_change/__init__.py
  23. 20
      pos_payment_change/__manifest__.py
  24. 183
      pos_payment_change/i18n/fr.po
  25. 2
      pos_payment_change/models/__init__.py
  26. 46
      pos_payment_change/models/pos_config.py
  27. 82
      pos_payment_change/models/pos_order.py
  28. 23
      pos_payment_change/readme/CONFIGURE.rst
  29. 0
      pos_payment_change/readme/CONTRIBUTORS.rst
  30. 4
      pos_payment_change/readme/CREDITS.rst
  31. 6
      pos_payment_change/readme/DESCRIPTION.rst
  32. 19
      pos_payment_change/readme/USAGE.rst
  33. BIN
      pos_payment_change/static/description/pos_config_form.png
  34. BIN
      pos_payment_change/static/description/pos_order_form.png
  35. BIN
      pos_payment_change/static/description/pos_order_tree.png
  36. BIN
      pos_payment_change/static/description/pos_payment_change_wizard_form.png
  37. 1
      pos_payment_change/tests/__init__.py
  38. 172
      pos_payment_change/tests/test_module.py
  39. 29
      pos_payment_change/views/view_pos_config.xml
  40. 15
      pos_payment_change/views/view_pos_order.xml
  41. 2
      pos_payment_change/wizards/__init__.py
  42. 77
      pos_payment_change/wizards/pos_payment_change_wizard.py
  43. 46
      pos_payment_change/wizards/pos_payment_change_wizard_line.py
  44. 43
      pos_payment_change/wizards/view_pos_payment_change_wizard.xml

91
pos_change_payment/README.rst

@ -1,91 +0,0 @@
===============================
Point Of Sale - Payments Change
===============================
.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
:target: https://odoo-community.org/page/development-status
:alt: Beta
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-grap%2Fgrap--odoo--incubator-lightgray.png?logo=github
:target: https://github.com/grap/grap-odoo-incubator/tree/8.0/pos_change_payment
:alt: grap/grap-odoo-incubator
|badge1| |badge2| |badge3|
Improve payment changes when user did a mistake and disable some actions on
Point of Sale Bank Statement Line
* Add the possibility to switch a payment (account.bank.statement.line)
of an order from a journal to another. This feature is usefull when
the user realized that he did a mistake, during the close of the session,
or just after he marked the order as paid
(Only if entries has not been generated)
.. figure:: https://raw.githubusercontent.com/grap/grap-odoo-incubator/8.0/pos_change_payment/static/description/pos_order_switch_payment.png
* Add the possibility to change all payments (method and amount) of a POS
(Only if entries has not been generated)
.. figure:: https://raw.githubusercontent.com/grap/grap-odoo-incubator/8.0/pos_change_payment/static/description/pos_order_change_payments.png
Bug Fixes / Improvement
~~~~~~~~~~~~~~~~~~~~~~~
* In the pos.payment wizard, display only the payment methods defined in
the current POS session
* Disable the possibility to edit / delete a bank statement line on a POS
Order that has generated his entries, except using the wizard of this
module. This will prevent the generation of bad account move during
the close of the session; (mainly unbalanced moves)
* All the cash payment are merged into a single one statement line. This
feature is usefull if the user use OpenERP as a calculator, writing
for a payment:
1. Payment 1/ Cash 50 €;
2. Payment 2/ Cash -3,56 €;
3. With this module, the final statement line is a single line Payment 1/ Cash 46,44 €
**Table of contents**
.. contents::
:local:
Bug Tracker
===========
Bugs are tracked on `GitHub Issues <https://github.com/grap/grap-odoo-incubator/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us smashing it by providing a detailed and welcomed
`feedback <https://github.com/grap/grap-odoo-incubator/issues/new?body=module:%20pos_change_payment%0Aversion:%208.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
Do not contact contributors directly about support or help with technical issues.
Credits
=======
Authors
~~~~~~~
* GRAP
Contributors
~~~~~~~~~~~~
* Sylvain LE GAL <https://twitter.com/legalsylvain>
* Julien WESTE
Maintainers
~~~~~~~~~~~
This module is part of the `grap/grap-odoo-incubator <https://github.com/grap/grap-odoo-incubator/tree/8.0/pos_change_payment>`_ project on GitHub.
You are welcome to contribute.

6
pos_change_payment/__init__.py

@ -1,6 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2015-Today GRAP (http://www.grap.coop)
# @author: Sylvain LE GAL (https://twitter.com/legalsylvain)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from . import models

23
pos_change_payment/__openerp__.py

@ -1,23 +0,0 @@
# coding: utf-8
# Copyright (C) 2013 - Today: GRAP (http://www.grap.coop)
# @author: Sylvain LE GAL (https://twitter.com/legalsylvain)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
{
'name': 'Point Of Sale - Payments Change',
'version': '8.0.1.0.0',
'category': 'Point Of Sale',
'author': 'GRAP',
'website': 'http://www.grap.coop',
'license': 'AGPL-3',
'depends': [
'point_of_sale',
],
'data': [
'views/action.xml',
'views/view_account_bank_statement.xml',
'views/view_pos_change_payments_wizard.xml',
'views/view_pos_order.xml',
'views/view_pos_switch_journal_wizard.xml',
],
'installable': True,
}

168
pos_change_payment/i18n/fr.po

@ -1,168 +0,0 @@
# Translation of OpenERP Server.
# This file contains the translation of the following modules:
# * pos_change_payment
#
msgid ""
msgstr ""
"Project-Id-Version: OpenERP Server 7.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-04-01 14:25+0000\n"
"PO-Revision-Date: 2015-04-01 14:25+0000\n"
"Last-Translator: <>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: pos_change_payment
#: field:pos.change.payments.wizard.line,amount:0
#: field:pos.switch.journal.wizard,amount:0
msgid "Amount"
msgstr "Montant"
#. module: pos_change_payment
#: code:_description:0
#: model:ir.model,name:pos_change_payment.model_account_bank_statement_line
#, python-format
msgid "Bank Statement Line"
msgstr "Ligne de relevé de banque"
#. module: pos_change_payment
#: view:pos.change.payments.wizard:0
#: view:pos.switch.journal.wizard:0
msgid "Cancel"
msgstr "Annuler"
#. module: pos_change_payment
#: model:ir.actions.act_window,name:pos_change_payment.action_pos_change_payments_wizard
#: view:pos.change.payments.wizard:0
#: view:pos.order:0
msgid "Change Payments"
msgstr "Changer les paiements"
#. module: pos_change_payment
#: code:addons/pos_change_payment/model/pos_change_payments_wizard.py:77
#, python-format
msgid "Differences between the two values for the POS Order '%s':\n"
"\n"
" * Total of all the new payments %s;\n"
" * Total of the POS Order %s;\n"
"\n"
"Please change the payments."
msgstr "Différences entre les deux valeurs pour la vente '%s':\n"
"\n"
" * Total des nouveaux paiements %s;\n"
" * Total de la vente %s;\n"
"\n"
"Veuillez changer les paiements."
#. module: pos_change_payment
#: code:addons/pos_change_payment/model/account_bank_statement_line.py:78
#: code:addons/pos_change_payment/model/pos_change_payments_wizard.py:36
#: code:addons/pos_change_payment/model/pos_change_payments_wizard.py:72
#: code:addons/pos_change_payment/model/pos_order.py:76
#: code:addons/pos_change_payment/model/pos_switch_journal_wizard.py:64
#, python-format
msgid "Error!"
msgstr "Erreur !"
#. module: pos_change_payment
#: code:addons/pos_change_payment/model/pos_change_payments_wizard.py:36
#: code:addons/pos_change_payment/model/pos_switch_journal_wizard.py:64
#, python-format
msgid "Incorrect Call!"
msgstr "Appel incorrect !"
#. module: pos_change_payment
#: code:_description:0
#: model:ir.model,name:pos_change_payment.model_account_journal
#: field:pos.change.payments.wizard.line,journal_id:0
#, python-format
msgid "Journal"
msgstr "Journal"
#. module: pos_change_payment
#: field:pos.switch.journal.wizard,new_statement_id:0
msgid "New Journal"
msgstr "Nouveau journal"
#. module: pos_change_payment
#: field:pos.switch.journal.wizard,old_journal_id:0
msgid "Old Journal"
msgstr "Ancien journal"
#. module: pos_change_payment
#: field:pos.change.payments.wizard,order_id:0
msgid "POS Order"
msgstr "Vente"
#. module: pos_change_payment
#: view:pos.order:0
msgid "Payment"
msgstr "Paiement"
#. module: pos_change_payment
#: view:pos.change.payments.wizard:0
msgid "Payment Lines"
msgstr "Lignes de paiement"
#. module: pos_change_payment
#: code:_description:0
#: model:ir.model,name:pos_change_payment.model_pos_order
#, python-format
msgid "Point of Sale"
msgstr "Point de Vente"
#. module: pos_change_payment
#: code:_description:0
#: model:ir.model,name:pos_change_payment.model_pos_make_payment
#, python-format
msgid "Point of Sale Payment"
msgstr "Paiement du ticket"
#. module: pos_change_payment
#: field:pos.switch.journal.wizard,statement_line_id:0
msgid "Statement"
msgstr "Relevé bancaire"
#. module: pos_change_payment
#: view:account.bank.statement:0
#: model:ir.actions.act_window,name:pos_change_payment.action_pos_switch_journal_wizard
#: view:pos.order:0
#: view:pos.switch.journal.wizard:0
msgid "Switch Journal"
msgstr "Changer de Journal"
#. module: pos_change_payment
#: field:pos.change.payments.wizard,amount_total:0
msgid "Total"
msgstr "Total"
#. module: pos_change_payment
#: field:pos.change.payments.wizard,line_ids:0
msgid "Wizard Lines"
msgstr "Lignes"
#. module: pos_change_payment
#: field:pos.change.payments.wizard.line,wizard_id:0
msgid "Wizard Ref"
msgstr "Référence"
#. module: pos_change_payment
#: code:addons/pos_change_payment/model/account_bank_statement_line.py:79
#, python-format
msgid "You can not change payments of POS by this way. Please use the regular wizard in POS view!"
msgstr "Vous ne pouvez pas changer les paiements de cette vente de cette façon. Veuillez utiliser l'interface prévue à cet effet dans la vue de la vente !"
#. module: pos_change_payment
#: code:addons/pos_change_payment/model/pos_order.py:78
#, python-format
msgid "You can not change payments of the POS '%s' because the associated session '%s' has been closed!"
msgstr "Vous ne pouvez pas changer les paiements de la Vente '%s' car la session associée '%s' a été clôturé !"
#. module: pos_change_payment
#: view:pos.change.payments.wizard:0
#: view:pos.switch.journal.wizard:0
msgid "or"
msgstr "ou"

9
pos_change_payment/models/__init__.py

@ -1,9 +0,0 @@
# coding: utf-8
from . import pos_order
from . import account_bank_statement_line
from . import pos_make_payment
from . import pos_switch_journal_wizard
from . import pos_change_payments_wizard
from . import pos_change_payments_wizard_line

66
pos_change_payment/models/account_bank_statement_line.py

@ -1,66 +0,0 @@
# coding: utf-8
# Copyright (C) 2015-Today GRAP (http://www.grap.coop)
# @author: Sylvain LE GAL (https://twitter.com/legalsylvain)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp import _, api, models
from openerp.exceptions import ValidationError
class AccountBankStatementLine(models.Model):
_inherit = 'account.bank.statement.line'
_POS_PAYMENT_ALLOW_WRITE = [
'sequence', 'journal_entry_id',
]
# Private Function Section
@api.multi
def _check_allow_change_pos_payment(self, vals):
"""Allow or block change of account bank statement line, linked to
a non draft POS Order.
* if 'change_pos_payment' is in the context, changes are allowed;
* otherwise:
* allow write of some fields only;
* forbid deletion;"""
values = vals.copy() if vals else {}
check_pos_order = False
if values:
# Allow some write
for key in self._POS_PAYMENT_ALLOW_WRITE:
if key in values:
del values[key]
if not values:
return
# Allow changes, if user use the wizard
if self._context.get('change_pos_payment', False):
check_pos_order = True
for statement_line in self:
order = statement_line.pos_statement_id
if order:
if order.state != 'draft':
if check_pos_order:
order._allow_change_payments()
else:
if values.keys() == ['partner_id']:
order._allow_change_payments()
else:
raise ValidationError(_(
"You can not change payments of POS by this"
" way. Please use the regular wizard in POS"
" view!"))
# Overload Section
@api.multi
def write(self, vals):
self._check_allow_change_pos_payment(vals)
return super(AccountBankStatementLine, self).write(vals)
@api.multi
def unlink(self):
self._check_allow_change_pos_payment(None)
return super(AccountBankStatementLine, self).unlink()

72
pos_change_payment/models/pos_change_payments_wizard.py

@ -1,72 +0,0 @@
# coding: utf-8
# Copyright (C) 2015-Today GRAP (http://www.grap.coop)
# @author: Sylvain LE GAL (https://twitter.com/legalsylvain)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp import _, api, fields, models
from openerp.exceptions import Warning as UserError
class PosChangePaymentsWizard(models.TransientModel):
_name = 'pos.change.payments.wizard'
# Column Section
order_id = fields.Many2one(
comodel_name='pos.order', string='Order', readonly=True)
session_id = fields.Many2one(
comodel_name='pos.session', string='Session', readonly=True)
line_ids = fields.One2many(
comodel_name='pos.change.payments.wizard.line',
inverse_name='wizard_id', string='Payment Lines')
amount_total = fields.Float(string='Total', readonly=True)
# View Section
@api.model
def default_get(self, fields):
order_obj = self.env['pos.order']
res = super(PosChangePaymentsWizard, self).default_get(fields)
order = order_obj.browse(self._context.get('active_id'))
res.update({'order_id': order.id})
res.update({'session_id': order.session_id.id})
res.update({'amount_total': order.amount_total})
return res
# View section
@api.multi
def button_change_payments(self):
self.ensure_one()
order = self.order_id
# Check if the total is correct
total = 0
for line in self.line_ids:
total += line.amount
if total != self.amount_total:
raise UserError(_(
"Differences between the two values for the POS"
" Order '%s':\n\n"
" * Total of all the new payments %s;\n"
" * Total of the POS Order %s;\n\n"
"Please change the payments." % (
order.name, total, order.amount_total)))
# Check if change payments is allowed
order._allow_change_payments()
# Remove old statements
order.statement_ids.with_context(change_pos_payment=True).unlink()
# Create new payment
for line in self.line_ids:
order.add_payment_v8({
'journal': line.new_journal_id.id,
'amount': line.amount,
})
return {
'type': 'ir.actions.client',
'tag': 'reload',
}

25
pos_change_payment/models/pos_change_payments_wizard_line.py

@ -1,25 +0,0 @@
# coding: utf-8
# Copyright (C) 2015 - Today: GRAP (http://www.grap.coop)
# @author: Sylvain LE GAL (https://twitter.com/legalsylvain)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp import api, fields, models
class PosChangePaymentsWizardLine(models.TransientModel):
_name = 'pos.change.payments.wizard.line'
wizard_id = fields.Many2one(
comodel_name='pos.change.payments.wizard', ondelete='cascade')
new_journal_id = fields.Many2one(
comodel_name='account.journal', string='Journal', required=True,
domain=lambda s: s._domain_new_journal_id())
amount = fields.Float(string='Amount', required=True)
@api.model
def _domain_new_journal_id(self):
PosOrder = self.env['pos.order']
order = PosOrder.browse(self.env.context.get('active_id'))
return [('id', 'in', order.session_id.journal_ids.ids)]

25
pos_change_payment/models/pos_make_payment.py

@ -1,25 +0,0 @@
# coding: utf-8
# Copyright (C) 2015-Today GRAP (http://www.grap.coop)
# @author Julien WESTE
# @author: Sylvain LE GAL (https://twitter.com/legalsylvain)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp import api, models, fields
class PosMakePayment(models.TransientModel):
_inherit = 'pos.make.payment'
# # Column Section (Overload)
journal_id = fields.Many2one(
default=False,
domain=lambda s: s._domain_journal_id())
@api.model
def _domain_journal_id(self):
session_obj = self.env['pos.session']
if self.env.context.get('pos_session_id', False):
session = session_obj.browse(
int(self._context.get('pos_session_id')))
return [('id', 'in', session.journal_ids.ids)]
return []

57
pos_change_payment/models/pos_order.py

@ -1,57 +0,0 @@
# coding: utf-8
# Copyright (C) 2015 - Today: GRAP (http://www.grap.coop)
# @author: Julien WESTE
# @author: Sylvain LE GAL (https://twitter.com/legalsylvain)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp import _, api, models
from openerp.exceptions import Warning as UserError
class PosOrder(models.Model):
_inherit = 'pos.order'
# Overload Section
@api.multi
def action_paid(self):
""" Merge all cash statement line of the Order"""
self._merge_cash_payment()
return super(PosOrder, self).action_paid()
@api.multi
def add_payment_v8(self, data):
"""Hack to call old api. TODO-V10 : remove me."""
for order in self:
self.pool['pos.order'].add_payment(
self._cr, self._uid, order.id, data, context=self._context)
return True
# Private Function Section
@api.multi
def _merge_cash_payment(self):
for order in self:
cash_statements = order.statement_ids.filtered(
lambda x: x.journal_id.type == 'cash')
if len(cash_statements) < 2:
continue
main_statement = cash_statements[0]
cash_total = sum(cash_statements.mapped('amount'))
# Unlink all statements except one
cash_statements.filtered(
lambda x: x.id != main_statement.id).unlink()
main_statement.write({'amount': cash_total})
@api.multi
def _allow_change_payments(self):
"""Return True if the user can change the payment of a POS, depending
of the state of the current session."""
closed_orders = self.filtered(
lambda x: x.session_id.state == 'closed')
if len(closed_orders):
raise UserError(_(
"You can not change payments of the POS '%s' because"
" the associated session '%s' has been closed!" % (
', '.join(closed_orders.mapped('name')),
', '.join(closed_orders.mapped('session_id.name')))))

78
pos_change_payment/models/pos_switch_journal_wizard.py

@ -1,78 +0,0 @@
# coding: utf-8
# Copyright (C) 2015 - Today: GRAP (http://www.grap.coop)
# @author: Sylvain LE GAL (https://twitter.com/legalsylvain)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp import api, fields, models
class PosSwitchJournalWizard(models.TransientModel):
_name = 'pos.switch.journal.wizard'
order_id = fields.Many2one(
comodel_name='pos.order', string='PoS Order',
required=True, readonly=True)
statement_line_id = fields.Many2one(
comodel_name='account.bank.statement.line', string='Statement',
required=True, readonly=True)
old_journal_id = fields.Many2one(
comodel_name='account.journal', string='Old Journal',
required=True, readonly=True)
new_journal_id = fields.Many2one(
comodel_name='account.journal', string='New Journal',
required=True, domain=lambda s: s._domain_new_journal_id())
amount = fields.Float(string='Amount', readonly=True)
@api.model
def _domain_new_journal_id(self):
AccountBankStatementLine = self.env['account.bank.statement.line']
statement_line = AccountBankStatementLine.browse(
self.env.context.get('active_id'))
return [(
'id', 'in',
statement_line.pos_statement_id.session_id.journal_ids.ids)]
@api.model
def default_get(self, fields):
AccountBankStatementLine = self.env['account.bank.statement.line']
res = super(PosSwitchJournalWizard, self).default_get(fields)
statement_line = AccountBankStatementLine.browse(
self.env.context.get('active_id'))
res.update({'statement_line_id': statement_line.id})
res.update({'order_id': statement_line.pos_statement_id.id})
res.update({'old_journal_id': statement_line.journal_id.id})
res.update({'amount': statement_line.amount})
return res
# Action section
@api.multi
def button_switch_journal(self):
self.ensure_one()
# Check if changing payment is allowed
self.order_id._allow_change_payments()
# TODO (FIXME) when upstream is fixed.
# We do 2 write, one in the old statement, one in the new, with
# 'amount' value each time to recompute all the functional fields
# of the Account Bank Statements
amount = self.amount
self.statement_line_id.with_context(change_pos_payment=True).write({
'amount': 0.0,
})
# Change statement of the statement line
new_statement = self.order_id.session_id.statement_ids.filtered(
lambda x: x.journal_id == self.new_journal_id)[0]
self.statement_line_id.with_context(change_pos_payment=True).write({
'amount': amount,
'statement_id': new_statement.id,
})
return {
'type': 'ir.actions.client',
'tag': 'reload',
}

34
pos_change_payment/readme/DESCRIPTION.rst

@ -1,34 +0,0 @@
Improve payment changes when user did a mistake and disable some actions on
Point of Sale Bank Statement Line
* Add the possibility to switch a payment (account.bank.statement.line)
of an order from a journal to another. This feature is usefull when
the user realized that he did a mistake, during the close of the session,
or just after he marked the order as paid
(Only if entries has not been generated)
.. figure:: ../static/description/pos_order_switch_payment.png
* Add the possibility to change all payments (method and amount) of a POS
(Only if entries has not been generated)
.. figure:: ../static/description/pos_order_change_payments.png
Bug Fixes / Improvement
~~~~~~~~~~~~~~~~~~~~~~~
* In the pos.payment wizard, display only the payment methods defined in
the current POS session
* Disable the possibility to edit / delete a bank statement line on a POS
Order that has generated his entries, except using the wizard of this
module. This will prevent the generation of bad account move during
the close of the session; (mainly unbalanced moves)
* All the cash payment are merged into a single one statement line. This
feature is usefull if the user use OpenERP as a calculator, writing
for a payment:
1. Payment 1/ Cash 50 €;
2. Payment 2/ Cash -3,56 €;
3. With this module, the final statement line is a single line Payment 1/ Cash 46,44 €

BIN
pos_change_payment/static/description/icon.png

Before

Width: 128  |  Height: 128  |  Size: 8.6 KiB

BIN
pos_change_payment/static/description/pos_order_change_payments.png

Before

Width: 892  |  Height: 435  |  Size: 28 KiB

BIN
pos_change_payment/static/description/pos_order_switch_payment.png

Before

Width: 890  |  Height: 199  |  Size: 26 KiB

157
pos_change_payment/tests/test_module.py

@ -1,157 +0,0 @@
# coding: utf-8
# Copyright (C) 2018 - Today: GRAP (http://www.grap.coop)
# @author: Sylvain LE GAL (https://twitter.com/legalsylvain)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp.tests.common import TransactionCase
from openerp.exceptions import Warning as UserError
class TestModule(TransactionCase):
"""Tests for 'Point of Sale - Change Payment' Module"""
def setUp(self):
super(TestModule, self).setUp()
self.PosSession = self.env['pos.session']
self.PosOrder = self.env['pos.order']
self.PosMakePayment = self.env['pos.make.payment']
self.PosSwitchJournalWizard = self.env['pos.switch.journal.wizard']
self.PosChangePaymentsWizard = self.env['pos.change.payments.wizard']
self.PosChangePaymentsWizardLine =\
self.env['pos.change.payments.wizard.line']
self.product = self.env.ref('product.product_product_3')
self.pos_config = self.env.ref('point_of_sale.pos_config_main')
self.check_journal = self.env.ref('account.check_journal')
self.cash_journal = self.env.ref('account.cash_journal')
# create new session and open it
self.session = self.PosSession.create(
{'config_id': self.pos_config.id})
self.session.open_cb()
self.check_statement = self.session.statement_ids.filtered(
lambda x: x.journal_id == self.check_journal)
self.cash_statement = self.session.statement_ids.filtered(
lambda x: x.journal_id == self.cash_journal)
def _sale(self, session, journal_1, price_1, journal_2=False, price_2=0.0):
order = self.PosOrder.create({
'session_id': session.id,
'lines': [[0, False, {
'name': 'OL/0001',
'product_id': self.product.id,
'qty': 1.0,
'price_unit': price_1 + price_2,
}]],
})
payment = self.PosMakePayment.with_context(active_id=order.id).create({
'journal_id': journal_1.id,
'amount': price_1,
})
payment.with_context(active_id=order.id).check()
if journal_2:
payment = self.PosMakePayment.with_context(
active_id=order.id).create({
'journal_id': journal_2.id,
'amount': price_2,
})
payment.with_context(active_id=order.id).check()
return order
# Test Section
def test_01_pos_switch_journal(self):
# Make a sale with 100 in cash journal
order = self._sale(self.session, self.cash_journal, 100)
statement_line = order.statement_ids[0]
# Switch to check journal
wizard = self.PosSwitchJournalWizard.with_context(
active_id=statement_line.id).create({
'new_journal_id': self.check_journal.id,
})
wizard.button_switch_journal()
# Check Order
self.assertEqual(
len(order.statement_ids.filtered(
lambda x: x.journal_id == self.cash_journal)), 0,
"Altered order should not have the original payment journal")
self.assertEqual(
len(order.statement_ids.filtered(
lambda x: x.journal_id == self.check_journal)), 1,
"Altered order should have the final payment journal")
# Check Session
self.assertEqual(
self.cash_statement.balance_end, 0,
"Bad recompute of the balance for the old statement")
self.assertEqual(
self.check_statement.balance_end, 100,
"Bad recompute of the balance for the new statement")
def test_02_pos_change_payment(self):
# Make a sale with 35 in cash journal and 65 in check
order = self._sale(
self.session, self.cash_journal, 35, self.check_journal, 65)
# Switch to check journal
wizard = self.PosChangePaymentsWizard.with_context(
active_id=order.id).create({})
self.PosChangePaymentsWizardLine.with_context(
active_id=order.id).create({
'wizard_id': wizard.id,
'new_journal_id': self.cash_journal.id,
'amount': 10,
})
self.PosChangePaymentsWizardLine.with_context(
active_id=order.id).create({
'wizard_id': wizard.id,
'new_journal_id': self.check_journal.id,
'amount': 40,
})
with self.assertRaises(UserError):
# Should not work if total is not correct
wizard.button_change_payments()
# Finish payement
self.PosChangePaymentsWizardLine.with_context(
active_id=order.id).create({
'wizard_id': wizard.id,
'new_journal_id': self.check_journal.id,
'amount': 50,
})
wizard.button_change_payments()
# check Session
self.assertEqual(
self.cash_statement.balance_end, 10,
"Bad recompute of the balance for the old statement")
self.assertEqual(
self.check_statement.balance_end, 90,
"Bad recompute of the balance for the new statement")
def test_03_merge_statement(self):
# Make a sale with multiple cash payement
order = self._sale(
self.session, self.cash_journal, 100,
journal_2=self.cash_journal, price_2=200)
# Check that statement has been merged
self.assertEqual(
len(order.statement_ids), 1,
"Adding many cash statement for an order should merge them.")
self.assertEqual(
order.statement_ids[0].amount, 300,
"Invalid total amount for merged cash statements")
# Make a sale with multiple check payement
order = self._sale(
self.session, self.check_journal, 100,
self.check_journal, 200)
# Check that statement has been merged
self.assertEqual(
len(order.statement_ids), 2,
"Adding many check statement for an order should not merge them.")

26
pos_change_payment/views/action.xml

@ -1,26 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2015-Today GRAP (http://www.grap.coop)
@author: Sylvain LE GAL (https://twitter.com/legalsylvain)
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
-->
<openerp><data>
<record id="action_pos_switch_journal_wizard" model="ir.actions.act_window">
<field name="name">Switch Journal</field>
<field name="res_model">pos.switch.journal.wizard</field>
<field name="view_type">form</field>
<field name="view_mode">form</field>
<field name="target">new</field>
</record>
<record id="action_pos_change_payments_wizard" model="ir.actions.act_window">
<field name="name">Change Payments</field>
<field name="res_model">pos.change.payments.wizard</field>
<field name="view_type">form</field>
<field name="view_mode">form</field>
<field name="target">new</field>
</record>
</data></openerp>

21
pos_change_payment/views/view_account_bank_statement.xml

@ -1,21 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2015-Today GRAP (http://www.grap.coop)
@author: Sylvain LE GAL (https://twitter.com/legalsylvain)
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
-->
<openerp><data>
<record id="view_account_bank_statement_form" model="ir.ui.view">
<field name="model">account.bank.statement</field>
<field name="inherit_id" ref="account.view_bank_statement_form" />
<field name="arch" type="xml">
<xpath expr="//field[@name='line_ids']/tree/field[@name='amount']" position="after">
<field name="pos_statement_id" invisible="1" />
<button name="%(action_pos_switch_journal_wizard)d" string="Switch Journal" type="action" icon="gtk-index" attrs="{'invisible':[('pos_statement_id', '=', False)]}"/>
</xpath>
</field>
</record>
</data></openerp>

36
pos_change_payment/views/view_pos_change_payments_wizard.xml

@ -1,36 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2015-Today GRAP (http://www.grap.coop)
@author: Sylvain LE GAL (https://twitter.com/legalsylvain)
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
-->
<openerp><data>
<record id="view_pos_change_payments_wizard_form" model="ir.ui.view">
<field name="model">pos.change.payments.wizard</field>
<field name="arch" type="xml">
<form>
<group col="4">
<field name="order_id" />
<field name="session_id" />
<field name="amount_total" />
<newline />
<field name="line_ids" colspan="4">
<tree string="Payment Lines" editable="bottom">
<field name="new_journal_id" widget="selection"/>
<field name="amount" />
</tree>
</field>
</group>
<footer>
<button name="button_change_payments" string="Change Payments"
type="object" class="oe_highlight"/>
<label string="or" />
<button string="Cancel" class="oe_link" special="cancel" />
</footer>
</form>
</field>
</record>
</data></openerp>

29
pos_change_payment/views/view_pos_switch_journal_wizard.xml

@ -1,29 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2015-Today GRAP (http://www.grap.coop)
@author: Sylvain LE GAL (https://twitter.com/legalsylvain)
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
-->
<openerp><data>
<record id="view_pos_switch_journal_wizard_form" model="ir.ui.view">
<field name="model">pos.switch.journal.wizard</field>
<field name="arch" type="xml">
<form>
<group col="4">
<field name="order_id" />
<field name="statement_line_id" />
<field name="old_journal_id" />
<field name="new_journal_id" widget="selection"/>
<field name="amount" />
</group>
<footer>
<button name="button_switch_journal" string="Switch Journal" type="object" class="oe_highlight"/>
<button string="Cancel" class="oe_link" special="cancel" />
</footer>
</form>
</field>
</record>
</data></openerp>

8
pos_payment_change/README.rst

@ -0,0 +1,8 @@
==============================
Point Of Sale - Change Payment
==============================
.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

2
pos_payment_change/__init__.py

@ -0,0 +1,2 @@
from . import models
from . import wizards

20
pos_payment_change/__manifest__.py

@ -0,0 +1,20 @@
# Copyright (C) 2013 - Today: GRAP (http://www.grap.coop)
# @author: Sylvain LE GAL (https://twitter.com/legalsylvain)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
{
"name": "Point Of Sale - Change Payment",
"version": "12.0.1.0.0",
"category": "Point Of Sale",
"author": "GRAP, Odoo Community Association (OCA)",
"website": "https://www.github.com/OCA/pos",
"license": "AGPL-3",
"depends": ["point_of_sale"],
"maintainers": ["legalsylvain"],
"development_status": "Beta",
"data": [
"wizards/view_pos_payment_change_wizard.xml",
"views/view_pos_config.xml",
"views/view_pos_order.xml",
],
"installable": True,
}

183
pos_payment_change/i18n/fr.po

@ -0,0 +1,183 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * pos_payment_change
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 12.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-04-21 02:33+0000\n"
"PO-Revision-Date: 2020-04-21 02:33+0000\n"
"Last-Translator: <>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: pos_payment_change
#: model_terms:ir.ui.view,arch_db:pos_payment_change.view_pos_config_form
msgid "<span class=\"o_form_label\">Payment Change Policy</span>"
msgstr "<span class=\"o_form_label\">Méthode de changement de paiement</span>"
#. module: pos_payment_change
#: model:ir.model.fields,field_description:pos_payment_change.field_pos_payment_change_wizard_line__amount
msgid "Amount"
msgstr "Montant"
#. module: pos_payment_change
#: model_terms:ir.ui.view,arch_db:pos_payment_change.view_pos_payment_change_wizard_form
msgid "Cancel"
msgstr "Annuler"
#. module: pos_payment_change
#: model:ir.actions.act_window,name:pos_payment_change.action_pos_payment_change_wizard
#: model_terms:ir.ui.view,arch_db:pos_payment_change.view_pos_order_form
#: model_terms:ir.ui.view,arch_db:pos_payment_change.view_pos_payment_change_wizard_form
msgid "Change Payments"
msgstr "Changer les paiements"
#. module: pos_payment_change
#: model:ir.model.fields,field_description:pos_payment_change.field_pos_payment_change_wizard__create_uid
#: model:ir.model.fields,field_description:pos_payment_change.field_pos_payment_change_wizard_line__create_uid
msgid "Created by"
msgstr "Créé par"
#. module: pos_payment_change
#: model:ir.model.fields,field_description:pos_payment_change.field_pos_payment_change_wizard__create_date
#: model:ir.model.fields,field_description:pos_payment_change.field_pos_payment_change_wizard_line__create_date
msgid "Created on"
msgstr "Créé le"
#. module: pos_payment_change
#: code:addons/pos_payment_change/wizards/pos_payment_change_wizard.py:54
#, python-format
msgid "Differences between the two values for the POS Order '%s':\n"
"\n"
" * Total of all the new payments %s;\n"
" * Total of the POS Order %s;\n"
"\n"
"Please change the payments."
msgstr "Différences entre les deux valeurs pour la vente '%s':\n"
"\n"
" * Total des nouveaux paiements %s;\n"
" * Total de la vente %s;\n"
"\n"
"Veuillez changer les paiements."
#. module: pos_payment_change
#: model:ir.model.fields,field_description:pos_payment_change.field_pos_payment_change_wizard__display_name
#: model:ir.model.fields,field_description:pos_payment_change.field_pos_payment_change_wizard_line__display_name
msgid "Display Name"
msgstr "Nom affiché"
#. module: pos_payment_change
#: model:ir.model.fields,field_description:pos_payment_change.field_pos_payment_change_wizard__id
#: model:ir.model.fields,field_description:pos_payment_change.field_pos_payment_change_wizard_line__id
msgid "ID"
msgstr "ID"
#. module: pos_payment_change
#: model:ir.model.fields,field_description:pos_payment_change.field_pos_payment_change_wizard_line__new_journal_id
msgid "Journal"
msgstr "Journal"
#. module: pos_payment_change
#: model:ir.model.fields,field_description:pos_payment_change.field_pos_payment_change_wizard____last_update
#: model:ir.model.fields,field_description:pos_payment_change.field_pos_payment_change_wizard_line____last_update
msgid "Last Modified on"
msgstr "Dernière modification le"
#. module: pos_payment_change
#: model:ir.model.fields,field_description:pos_payment_change.field_pos_payment_change_wizard__write_uid
#: model:ir.model.fields,field_description:pos_payment_change.field_pos_payment_change_wizard_line__write_uid
msgid "Last Updated by"
msgstr "Dernière mise à jour par"
#. module: pos_payment_change
#: model:ir.model.fields,field_description:pos_payment_change.field_pos_payment_change_wizard__write_date
#: model:ir.model.fields,field_description:pos_payment_change.field_pos_payment_change_wizard_line__write_date
msgid "Last Updated on"
msgstr "Dernière mise à jour le"
#. module: pos_payment_change
#: model:ir.model.fields,field_description:pos_payment_change.field_pos_payment_change_wizard__order_id
msgid "Order"
msgstr "Commande"
#. module: pos_payment_change
#: model:ir.model.fields,field_description:pos_payment_change.field_pos_config__payment_change_policy
msgid "Payment Change Policy"
msgstr "Méthode de changement de paiement"
#. module: pos_payment_change
#: model:ir.model.fields,help:pos_payment_change.field_pos_config__payment_change_policy
msgid "Payment Change Policy when users want to change the payment lines of a given PoS Order.\n"
"* 'Refund and Resale': Odoo will refund the current Pos Order to cancel it, and create a new PoS Order with the correct payment lines.\n"
"* 'Update Payments': Odoo will change payment lines.\n"
"\n"
"Note : In some countries the 'Update Payments' Option is not allowed by law, because orders history shouldn't not be altered."
msgstr "Méthode de changement de paiement quand les utilisateurs veulent changer des lignes de paiement d'une vente en caisse.\n"
"* 'Retourner et revendre': Odoo va réaliser un retour du la vente pour l'annuler, puis recréera une nouvelle vente, avec les paiements corrects.\n"
"* 'Modifier les paiements': Odoo va changer les lignes de paiements.\n"
"\n"
"Note : dans certains pays, l'option 'Modifier les paiements' n'est pas autorisé par la loi, parce que l'historique des ventes ne doit pas être altéré."
#. module: pos_payment_change
#: model:ir.model.fields,field_description:pos_payment_change.field_pos_payment_change_wizard__line_ids
#: model_terms:ir.ui.view,arch_db:pos_payment_change.view_pos_payment_change_wizard_form
msgid "Payment Lines"
msgstr "Lignes de paiement"
#. module: pos_payment_change
#: model:ir.model,name:pos_payment_change.model_pos_payment_change_wizard
msgid "PoS Payment Change Wizard"
msgstr "Assistant de changement de paiement du Point de Vente"
#. module: pos_payment_change
#: model:ir.model,name:pos_payment_change.model_pos_payment_change_wizard_line
msgid "PoS Payment Change Wizard Line"
msgstr "Ligne d'assistant de changement de paiement du Point de Vente"
#. module: pos_payment_change
#: model:ir.model,name:pos_payment_change.model_pos_config
msgid "Point of Sale Configuration"
msgstr "Paramétrage du point de vente"
#. module: pos_payment_change
#: model:ir.model,name:pos_payment_change.model_pos_order
msgid "Point of Sale Orders"
msgstr "Commandes du point de vente"
#. module: pos_payment_change
#: selection:pos.config,payment_change_policy:0
msgid "Refund and Resale"
msgstr "Retourner et revendre"
#. module: pos_payment_change
#: model:ir.model.fields,field_description:pos_payment_change.field_pos_payment_change_wizard__amount_total
#: model_terms:ir.ui.view,arch_db:pos_payment_change.view_pos_payment_change_wizard_form
msgid "Total"
msgstr "Total"
#. module: pos_payment_change
#: code:addons/pos_payment_change/models/pos_config.py:43
#, python-format
msgid "Unable to use the 'Update Payments' options for companies that have unalterable accounting."
msgstr "Impossible d'utiliser l'option 'Modifier les paiements' pour les sociétés qui ont une comptabilité inaltérable."
#. module: pos_payment_change
#: selection:pos.config,payment_change_policy:0
msgid "Update Payments"
msgstr "Modifier les paiements"
#. module: pos_payment_change
#: model:ir.model.fields,field_description:pos_payment_change.field_pos_payment_change_wizard_line__wizard_id
msgid "Wizard"
msgstr "Assistant"
#. module: pos_payment_change
#: code:addons/pos_payment_change/models/pos_order.py:80
#, python-format
msgid "You can not change payments of the POS '%s' because the associated session '%s' has been closed!"
msgstr "Vous ne pouvez pas changer les paiements de la Vente '%s' car la session associée '%s' a été clôturé !"

2
pos_payment_change/models/__init__.py

@ -0,0 +1,2 @@
from . import pos_config
from . import pos_order

46
pos_payment_change/models/pos_config.py

@ -0,0 +1,46 @@
# Copyright (C) 2020 - Today: GRAP (http://www.grap.coop)
# @author: Sylvain LE GAL (https://twitter.com/legalsylvain)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from odoo import _, api, fields, models
from odoo.exceptions import ValidationError
class PosConfig(models.Model):
_inherit = "pos.config"
_PAYMENT_CHANGE_POLICY_SELECTION = [
('refund', "Refund and Resale"),
('update', "Update Payments"),
]
payment_change_policy = fields.Selection(
selection=_PAYMENT_CHANGE_POLICY_SELECTION,
default="refund", required=True,
help="Payment Change Policy when users want"
" to change the payment lines of a given PoS Order.\n"
"* 'Refund and Resale': Odoo will refund the current"
" Pos Order to cancel it, and create a new PoS Order"
" with the correct payment lines.\n"
"* 'Update Payments': Odoo will change payment lines.\n\n"
"Note : In some countries the 'Update Payments' Option"
" is not allowed by law, because orders history shouldn't"
" not be altered.")
@api.constrains("payment_change_policy")
def _check_payment_change_policy(self):
# Check if certification module is installed
# and if yes, if 'update payments' option is allowed
module_states = self.env["ir.module.module"].search([
("name", "=", "l10n_fr_certification")]
).mapped("state")
if "installed" not in module_states:
return
for config in self.filtered(
lambda x: x.payment_change_policy == "update"
):
if config.company_id._is_accounting_unalterable():
raise ValidationError(_(
"Unable to use the 'Update Payments' options"
" for companies that have unalterable accounting."
))

82
pos_payment_change/models/pos_order.py

@ -0,0 +1,82 @@
# Copyright (C) 2015 - Today: GRAP (http://www.grap.coop)
# @author: Julien WESTE
# @author: Sylvain LE GAL (https://twitter.com/legalsylvain)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from odoo import _, api, fields, models
from odoo.tools import float_is_zero
from odoo.exceptions import Warning as UserError
class PosOrder(models.Model):
_inherit = "pos.order"
@api.multi
def change_payment(self, payment_lines):
"""
Change payment of a given order.
payment_lines should be a list of data that are
the argument of the Odoo Core function add_payment()
Return a list of order ids, depending on the
payment_change_policy of the related pos_config.
"""
self.ensure_one()
order_ids = [self.id]
# Removing zero lines
precision = self.pricelist_id.currency_id.decimal_places
payment_lines = [
x for x in payment_lines if not float_is_zero(
x["amount"], precision_digits=precision)
]
self._check_payment_change_allowed()
if self.config_id.payment_change_policy == "update":
self.statement_ids.with_context().unlink()
# Create new payment
for line in payment_lines:
self.add_payment(line)
elif self.config_id.payment_change_policy == "refund":
# Refund order and mark it as paid
# with same payment method as the original one
refund_result = self.refund()
refund_order = self.browse(refund_result["res_id"])
for statement in self.statement_ids:
refund_order.add_payment({
"journal": statement.journal_id.id,
"amount": - statement.amount,
"payment_date": fields.Date.context_today(self),
})
refund_order.action_pos_order_paid()
# Resale order and mark it as paid
# with the new payment
resale_order = self.copy()
for line in payment_lines:
resale_order.add_payment(line)
resale_order.action_pos_order_paid()
order_ids += [refund_order.id, resale_order.id]
return order_ids
@api.multi
def _check_payment_change_allowed(self):
"""Return True if the user can change the payment of a POS, depending
of the state of the current session."""
closed_orders = self.filtered(lambda x: x.session_id.state == "closed")
if len(closed_orders):
raise UserError(
_(
"You can not change payments of the POS '%s' because"
" the associated session '%s' has been closed!"
% (
", ".join(closed_orders.mapped("name")),
", ".join(closed_orders.mapped("session_id.name")),
)
)
)

23
pos_payment_change/readme/CONFIGURE.rst

@ -0,0 +1,23 @@
* Go to Point of Sale > Configuration > Point of Sale
* Edit your point of sale, and select a value for the field
'Payment Change Policy'.
Two options are available:
* 'Refund and Resale': Odoo will refund the current
Pos Order to cancel it, and create a new PoS Order
with the correct payment lines
* 'Update Payments': Odoo will change payment lines.
.. figure:: ../static/description/pos_config_form.png
**Note**
In some countries the 'Update Payments' Option
is not allowed by law, because orders history shouldn't not be altered.
For that purpose, a constrains is present to check the value of this
field. If the module ``l10n_fr_certification`` is installed and if the
current company has an inalterable accounting, it will not be possible
to select the value 'Update Payments'.

0
pos_change_payment/readme/CONTRIBUTORS.rst → pos_payment_change/readme/CONTRIBUTORS.rst

4
pos_payment_change/readme/CREDITS.rst

@ -0,0 +1,4 @@
The development of this module has been financially supported by:
* GRAP, Groupement Régional Alimentaire de proximité (www.grap.coop)
* Vracoop (www.vracoop.fr)

6
pos_payment_change/readme/DESCRIPTION.rst

@ -0,0 +1,6 @@
This module extends the functionnality of the Odoo Point of Sale to
allow the cashier to change the payments of a PoS order.
This feature is usefull when the user realized that he did a mistake,
just after he marked the order as paid, or during the close of the session,
Only if entries has not been generated.

19
pos_payment_change/readme/USAGE.rst

@ -0,0 +1,19 @@
* Go to a PoS Order
* Click on the button 'Change Payments'
.. figure:: ../static/description/pos_order_form.png
* In the pop up wizard, select the real payment(s) that have been
used to pay the order
.. figure:: ../static/description/pos_payment_change_wizard_form.png
* Then click on the button 'Change Payments'
**Note**
If the option 'Refund and Resale' is selected, changing the payments will
display the three PoS orders. the oringal one, the refund one, and the new one.
.. figure:: ../static/description/pos_order_tree.png

BIN
pos_payment_change/static/description/pos_config_form.png

After

Width: 951  |  Height: 357  |  Size: 34 KiB

BIN
pos_payment_change/static/description/pos_order_form.png

After

Width: 473  |  Height: 204  |  Size: 14 KiB

BIN
pos_payment_change/static/description/pos_order_tree.png

After

Width: 957  |  Height: 193  |  Size: 32 KiB

BIN
pos_payment_change/static/description/pos_payment_change_wizard_form.png

After

Width: 971  |  Height: 375  |  Size: 24 KiB

1
pos_change_payment/tests/__init__.py → pos_payment_change/tests/__init__.py

@ -1,2 +1 @@
# coding: utf-8
from . import test_module

172
pos_payment_change/tests/test_module.py

@ -0,0 +1,172 @@
# Copyright (C) 2018 - Today: GRAP (http://www.grap.coop)
# @author: Sylvain LE GAL (https://twitter.com/legalsylvain)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from odoo import fields
from odoo.tests.common import TransactionCase
from odoo.exceptions import UserError
class TestModule(TransactionCase):
"""Tests for 'Point of Sale - Change Payment' Module"""
def setUp(self):
super().setUp()
self.PosSession = self.env["pos.session"]
self.PosOrder = self.env["pos.order"]
self.AccountJournal = self.env["account.journal"]
self.PosMakePayment = self.env['pos.make.payment']
self.PosPaymentChangeWizard = self.env["pos.payment.change.wizard"]
self.PosPaymentChangeWizardLine = self.env[
"pos.payment.change.wizard.line"
]
self.product = self.env.ref("product.product_product_3")
self.pos_config = self.env.ref("point_of_sale.pos_config_main").copy()
def _initialize_journals_open_session(self):
self.check_journal = self.AccountJournal.create({
"name": "Demo Check Journal",
"type": "bank",
"journal_user": True,
})
self.cash_journal = self.AccountJournal.create({
"name": "Demo Cash Journal",
"type": "cash",
"journal_user": True,
})
# create new session and open it
self.pos_config.journal_ids = [
self.check_journal.id,
self.cash_journal.id,
]
self.pos_config.open_session_cb()
self.session = self.pos_config.current_session_id
self.check_statement = self.session.statement_ids.filtered(
lambda x: x.journal_id == self.check_journal
)
self.cash_statement = self.session.statement_ids.filtered(
lambda x: x.journal_id == self.cash_journal
)
def _sale(self, journal_1, price_1, journal_2=False, price_2=0.0):
price = price_1 + price_2
line_vals = {
"name": "OL/0001",
"product_id": self.product.id,
"qty": 1.0,
"price_unit": price,
"price_subtotal": price,
"price_subtotal_incl": price,
}
order = self.PosOrder.create({
"session_id": self.session.id,
"amount_tax": 0,
"amount_total": price,
"amount_paid": price,
"amount_return": 0,
"lines": [[0, False, line_vals]],
})
order.add_payment({
'amount': price_1,
'payment_date': fields.Date.today(),
'payment_name': "Demo",
'journal': journal_1.id,
})
if journal_2:
order.add_payment({
'amount': price_2,
'payment_date': fields.Date.today(),
'payment_name': "Demo",
'journal': journal_2.id,
})
order.action_pos_order_paid()
return order
def _change_payment(
self, order, journal_1, amount_1, journal_2=False, amount_2=0.0
):
# Switch to check journal
wizard = self.PosPaymentChangeWizard.with_context(
active_id=order.id
).create({})
self.PosPaymentChangeWizardLine.with_context(
active_id=order.id
).create(
{
"wizard_id": wizard.id,
"new_journal_id": journal_1.id,
"amount": amount_1,
}
)
if journal_2:
self.PosPaymentChangeWizardLine.with_context(
active_id=order.id
).create(
{
"wizard_id": wizard.id,
"new_journal_id": journal_2.id,
"amount": amount_2,
}
)
wizard.button_change_payment()
# Test Section
def test_01_payment_change_policy_update(self):
self.pos_config.payment_change_policy = "update"
self._initialize_journals_open_session()
# Make a sale with 35 in cash journal and 65 in check
order = self._sale(self.cash_journal, 35, self.check_journal, 65)
order_qty = len(self.PosOrder.search([]))
with self.assertRaises(UserError):
# Should not work if total is not correct
self._change_payment(
order, self.cash_journal, 10, self.check_journal, 10)
self._change_payment(
order, self.cash_journal, 10, self.check_journal, 90)
# check Session
self.assertEqual(
self.cash_statement.balance_end,
10,
"Bad recompute of the balance for the statement cash",
)
self.assertEqual(
self.check_statement.balance_end,
90,
"Bad recompute of the balance for the statement check",
)
# Check Order quantity
self.assertEqual(
order_qty,
len(self.PosOrder.search([])),
"In 'Update' mode, changing payment should not create"
" other PoS Orders",
)
def test_02_payment_change_policy_refund(self):
self.pos_config.payment_change_policy = "refund"
self._initialize_journals_open_session()
# Make a sale with 35 in cash journal and 65 in check
order = self._sale(self.cash_journal, 35, self.check_journal, 65)
order_qty = len(self.PosOrder.search([]))
self._change_payment(
order, self.cash_journal, 50, self.check_journal, 50)
# Check Order quantity
self.assertEqual(
order_qty + 2,
len(self.PosOrder.search([])),
"In 'Refund' mode, changing payment should generate"
" two new PoS Orders",
)

29
pos_payment_change/views/view_pos_config.xml

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2020-Today GRAP (http://www.grap.coop)
@author: Sylvain LE GAL (https://twitter.com/legalsylvain)
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
-->
<odoo>
<record id="view_pos_config_form" model="ir.ui.view">
<field name="model">pos.config</field>
<field name="inherit_id" ref="point_of_sale.pos_config_view_form"/>
<field name="arch" type="xml">
<xpath expr="//div[hasclass('o_settings_container')][2]"
position="inside">
<div class="col-12 col-lg-6 o_setting_box">
<div class="o_setting_right_pane">
<span class="o_form_label">Payment Change Policy</span>
<div class="content-group mt16">
<field name="payment_change_policy" colspan="4"
nolabel="1"/>
</div>
</div>
</div>
</xpath>
</field>
</record>
</odoo>

15
pos_change_payment/views/view_pos_order.xml → pos_payment_change/views/view_pos_order.xml

@ -5,26 +5,17 @@ Copyright (C) 2015-Today GRAP (http://www.grap.coop)
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
-->
<openerp><data>
<odoo>
<record id="view_pos_order_form" model="ir.ui.view">
<field name="model">pos.order</field>
<field name="inherit_id" ref="point_of_sale.view_pos_pos_form" />
<field name="arch" type="xml">
<button string="Payment" position="attributes">
<attribute name="context">{'pos_session_id' : session_id}</attribute>
</button>
<button name="refund" position="before">
<button name="%(action_pos_change_payments_wizard)d"
<button name="%(action_pos_payment_change_wizard)d"
context="{'pos_session_id' : session_id}"
string="Change Payments" type="action" states="paid,invoiced"/>
</button>
<field name="journal_id" position="after">
<button name="%(action_pos_switch_journal_wizard)d" string="Switch Journal"
type="action" icon="gtk-index"/>
</field>
</field>
</record>
</data></openerp>
</odoo>

2
pos_payment_change/wizards/__init__.py

@ -0,0 +1,2 @@
from . import pos_payment_change_wizard
from . import pos_payment_change_wizard_line

77
pos_payment_change/wizards/pos_payment_change_wizard.py

@ -0,0 +1,77 @@
# Copyright (C) 2015-Today GRAP (http://www.grap.coop)
# @author: Sylvain LE GAL (https://twitter.com/legalsylvain)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from odoo import _, api, fields, models
from odoo.exceptions import Warning as UserError
class PosPaymentChangeWizard(models.TransientModel):
_name = "pos.payment.change.wizard"
_description = "PoS Payment Change Wizard"
# Column Section
order_id = fields.Many2one(
comodel_name="pos.order", string="Order", readonly=True
)
line_ids = fields.One2many(
comodel_name="pos.payment.change.wizard.line",
inverse_name="wizard_id",
string="Payment Lines",
)
amount_total = fields.Float(string="Total", readonly=True)
# View Section
@api.model
def default_get(self, fields):
PosOrder = self.env["pos.order"]
res = super().default_get(fields)
order = PosOrder.browse(self._context.get("active_id"))
res.update({"order_id": order.id})
res.update({"amount_total": order.amount_total})
return res
# View section
@api.multi
def button_change_payment(self):
self.ensure_one()
order = self.order_id
# Check if the total is correct
total = 0
for line in self.line_ids:
total += line.amount
if total != self.amount_total:
raise UserError(
_(
"Differences between the two values for the POS"
" Order '%s':\n\n"
" * Total of all the new payments %s;\n"
" * Total of the POS Order %s;\n\n"
"Please change the payments."
% (order.name, total, order.amount_total)
)
)
# Change payment
new_payments = [{
"journal": line.new_journal_id.id,
"amount": line.amount,
"payment_date": fields.Date.context_today(self),
} for line in self.line_ids]
order_ids = order.change_payment(new_payments)
if len(order_ids) == 1:
# if policy is 'update', only close the pop up
action = {'type': 'ir.actions.act_window_close'}
else:
# otherwise (refund policy), displays the 3 orders
action = self.env.ref(
"point_of_sale.action_pos_pos_form"
).read()[0]
action['domain'] = [('id', 'in', order_ids)]
return action

46
pos_payment_change/wizards/pos_payment_change_wizard_line.py

@ -0,0 +1,46 @@
# Copyright (C) 2015 - Today: GRAP (http://www.grap.coop)
# @author: Sylvain LE GAL (https://twitter.com/legalsylvain)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from odoo import api, fields, models
class PosPaymentChangeWizardLine(models.TransientModel):
_name = "pos.payment.change.wizard.line"
_description = "PoS Payment Change Wizard Line"
wizard_id = fields.Many2one(
comodel_name="pos.payment.change.wizard", required=True,
)
new_journal_id = fields.Many2one(
comodel_name="account.journal",
string="Journal",
required=True,
domain=lambda s: s._domain_new_journal_id(),
)
amount = fields.Float(string="Amount", required=True)
@api.model
def _domain_new_journal_id(self):
PosOrder = self.env["pos.order"]
order = PosOrder.browse(self.env.context.get("active_id"))
# return [("id", "in", order.session_id.journal_ids.ids)]
return [("id", "in", order.mapped(
"session_id.statement_ids.journal_id").ids)]
# View Section
@api.model
def default_get(self, fields):
res = super().default_get(fields)
if "line_ids" not in self._context:
return res
balance = self._context.get("amount_total", 0.0)
for line in self.wizard_id.resolve_2many_commands(
"line_ids",
self._context["line_ids"],
fields=["amount"]):
balance -= line.get("amount")
res.update({'amount': balance})
return res

43
pos_payment_change/wizards/view_pos_payment_change_wizard.xml

@ -0,0 +1,43 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2015-Today GRAP (http://www.grap.coop)
@author: Sylvain LE GAL (https://twitter.com/legalsylvain)
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
-->
<odoo>
<record id="view_pos_payment_change_wizard_form" model="ir.ui.view">
<field name="model">pos.payment.change.wizard</field>
<field name="arch" type="xml">
<form>
<group col="4">
<field name="order_id" />
<field name="amount_total" />
<newline />
<field name="line_ids" colspan="4"
context="{'line_ids': line_ids, 'amount_total': amount_total}">
<tree string="Payment Lines" editable="bottom">
<field name="new_journal_id" options="{'no_open': True, 'no_create_edit': True}"/>
<field name="amount" sum="Total"/>
</tree>
</field>
</group>
<footer>
<button name="button_change_payment" string="Change Payments"
type="object" class="oe_highlight"/>
<button string="Cancel" class="oe_link" special="cancel" />
</footer>
</form>
</field>
</record>
<record id="action_pos_payment_change_wizard" model="ir.actions.act_window">
<field name="name">Change Payments</field>
<field name="res_model">pos.payment.change.wizard</field>
<field name="view_type">form</field>
<field name="view_mode">form</field>
<field name="target">new</field>
</record>
</odoo>
Loading…
Cancel
Save