Browse Source

Merge PR #480 into 12.0

Signed-off-by legalsylvain
pull/511/head
OCA-git-bot 4 years ago
parent
commit
1546cdd246
  1. 8
      pos_payment_change/README.rst
  2. 2
      pos_payment_change/__init__.py
  3. 22
      pos_payment_change/__manifest__.py
  4. 215
      pos_payment_change/i18n/fr.po
  5. 206
      pos_payment_change/i18n/pos_payment_change.pot
  6. 2
      pos_payment_change/models/__init__.py
  7. 46
      pos_payment_change/models/pos_config.py
  8. 100
      pos_payment_change/models/pos_order.py
  9. 23
      pos_payment_change/readme/CONFIGURE.rst
  10. 2
      pos_payment_change/readme/CONTRIBUTORS.rst
  11. 4
      pos_payment_change/readme/CREDITS.rst
  12. 6
      pos_payment_change/readme/DESCRIPTION.rst
  13. 19
      pos_payment_change/readme/USAGE.rst
  14. BIN
      pos_payment_change/static/description/pos_config_form.png
  15. BIN
      pos_payment_change/static/description/pos_order_form.png
  16. BIN
      pos_payment_change/static/description/pos_order_tree.png
  17. BIN
      pos_payment_change/static/description/pos_payment_change_wizard_form.png
  18. 1
      pos_payment_change/tests/__init__.py
  19. 172
      pos_payment_change/tests/test_module.py
  20. 29
      pos_payment_change/views/view_pos_config.xml
  21. 21
      pos_payment_change/views/view_pos_order.xml
  22. 3
      pos_payment_change/wizards/__init__.py
  23. 98
      pos_payment_change/wizards/pos_payment_change_wizard.py
  24. 45
      pos_payment_change/wizards/pos_payment_change_wizard_new_line.py
  25. 23
      pos_payment_change/wizards/pos_payment_change_wizard_old_line.py
  26. 49
      pos_payment_change/wizards/view_pos_payment_change_wizard.xml

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

22
pos_payment_change/__manifest__.py

@ -0,0 +1,22 @@
# 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 Payments",
"version": "12.0.1.0.0",
"summary": "Allow cashier to change order payments, as long as"
" the session is not closed.",
"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,
}

215
pos_payment_change/i18n/fr.po

@ -0,0 +1,215 @@
# 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-05-18 17:33+0000\n"
"PO-Revision-Date: 2020-05-18 17: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
#: code:addons/pos_payment_change/models/pos_order.py:79
#, python-format
msgid " (Refund Order: %s ; Resale Order: %s)"
msgstr " (Remboursement: %s ; Revente : %s)"
#. 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_new_line__amount
#: model:ir.model.fields,field_description:pos_payment_change.field_pos_payment_change_wizard_old_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_new_line__create_uid
#: model:ir.model.fields,field_description:pos_payment_change.field_pos_payment_change_wizard_old_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_new_line__create_date
#: model:ir.model.fields,field_description:pos_payment_change.field_pos_payment_change_wizard_old_line__create_date
msgid "Created on"
msgstr "Créé le"
#. module: pos_payment_change
#: code:addons/pos_payment_change/wizards/pos_payment_change_wizard.py:69
#, 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_new_line__display_name
#: model:ir.model.fields,field_description:pos_payment_change.field_pos_payment_change_wizard_old_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_new_line__id
#: model:ir.model.fields,field_description:pos_payment_change.field_pos_payment_change_wizard_old_line__id
msgid "ID"
msgstr "ID"
#. module: pos_payment_change
#: model:ir.model.fields,field_description:pos_payment_change.field_pos_payment_change_wizard_new_line__new_journal_id
#: model:ir.model.fields,field_description:pos_payment_change.field_pos_payment_change_wizard_old_line__old_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_new_line____last_update
#: model:ir.model.fields,field_description:pos_payment_change.field_pos_payment_change_wizard_old_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_new_line__write_uid
#: model:ir.model.fields,field_description:pos_payment_change.field_pos_payment_change_wizard_old_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_new_line__write_date
#: model:ir.model.fields,field_description:pos_payment_change.field_pos_payment_change_wizard_old_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__new_line_ids
msgid "New Payment Lines"
msgstr "Nouveaux paiements"
#. module: pos_payment_change
#: model:ir.model.fields,field_description:pos_payment_change.field_pos_payment_change_wizard__old_line_ids
msgid "Old Payment Lines"
msgstr "Anciens paiements"
#. 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,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_new_line
msgid "PoS Payment Change Wizard New Line"
msgstr "Nouvelle ligne de l'ssistant de changement de paiement du Point de Vente"
#. module: pos_payment_change
#: model:ir.model,name:pos_payment_change.model_pos_payment_change_wizard_old_line
msgid "PoS Payment Change Wizard Old Line"
msgstr "Ancienne ligne de l'ssistant 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
#: code:addons/pos_payment_change/models/pos_order.py:43
#, python-format
msgid "The payments of the Order %s (Ref: %s) has been changed by %s at %s."
msgstr "Les paiements de la commande %s (Réf: %s) ont été changés par %s à %s."
#. 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_new_line__wizard_id
#: model:ir.model.fields,field_description:pos_payment_change.field_pos_payment_change_wizard_old_line__wizard_id
msgid "Wizard"
msgstr "Assistant"
#. module: pos_payment_change
#: code:addons/pos_payment_change/models/pos_order.py:97
#, 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é !"

206
pos_payment_change/i18n/pos_payment_change.pot

@ -0,0 +1,206 @@
# 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-05-18 17:33+0000\n"
"PO-Revision-Date: 2020-05-18 17: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
#: code:addons/pos_payment_change/models/pos_order.py:79
#, python-format
msgid " (Refund Order: %s ; Resale Order: %s)"
msgstr ""
#. 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 ""
#. module: pos_payment_change
#: model:ir.model.fields,field_description:pos_payment_change.field_pos_payment_change_wizard_new_line__amount
#: model:ir.model.fields,field_description:pos_payment_change.field_pos_payment_change_wizard_old_line__amount
msgid "Amount"
msgstr ""
#. module: pos_payment_change
#: model_terms:ir.ui.view,arch_db:pos_payment_change.view_pos_payment_change_wizard_form
msgid "Cancel"
msgstr ""
#. 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 ""
#. 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_new_line__create_uid
#: model:ir.model.fields,field_description:pos_payment_change.field_pos_payment_change_wizard_old_line__create_uid
msgid "Created by"
msgstr ""
#. 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_new_line__create_date
#: model:ir.model.fields,field_description:pos_payment_change.field_pos_payment_change_wizard_old_line__create_date
msgid "Created on"
msgstr ""
#. module: pos_payment_change
#: code:addons/pos_payment_change/wizards/pos_payment_change_wizard.py:69
#, 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 ""
#. 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_new_line__display_name
#: model:ir.model.fields,field_description:pos_payment_change.field_pos_payment_change_wizard_old_line__display_name
msgid "Display Name"
msgstr ""
#. 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_new_line__id
#: model:ir.model.fields,field_description:pos_payment_change.field_pos_payment_change_wizard_old_line__id
msgid "ID"
msgstr ""
#. module: pos_payment_change
#: model:ir.model.fields,field_description:pos_payment_change.field_pos_payment_change_wizard_new_line__new_journal_id
#: model:ir.model.fields,field_description:pos_payment_change.field_pos_payment_change_wizard_old_line__old_journal_id
msgid "Journal"
msgstr ""
#. 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_new_line____last_update
#: model:ir.model.fields,field_description:pos_payment_change.field_pos_payment_change_wizard_old_line____last_update
msgid "Last Modified on"
msgstr ""
#. 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_new_line__write_uid
#: model:ir.model.fields,field_description:pos_payment_change.field_pos_payment_change_wizard_old_line__write_uid
msgid "Last Updated by"
msgstr ""
#. 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_new_line__write_date
#: model:ir.model.fields,field_description:pos_payment_change.field_pos_payment_change_wizard_old_line__write_date
msgid "Last Updated on"
msgstr ""
#. module: pos_payment_change
#: model:ir.model.fields,field_description:pos_payment_change.field_pos_payment_change_wizard__new_line_ids
msgid "New Payment Lines"
msgstr ""
#. module: pos_payment_change
#: model:ir.model.fields,field_description:pos_payment_change.field_pos_payment_change_wizard__old_line_ids
msgid "Old Payment Lines"
msgstr ""
#. module: pos_payment_change
#: model:ir.model.fields,field_description:pos_payment_change.field_pos_payment_change_wizard__order_id
msgid "Order"
msgstr ""
#. module: pos_payment_change
#: model:ir.model.fields,field_description:pos_payment_change.field_pos_config__payment_change_policy
msgid "Payment Change Policy"
msgstr ""
#. 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 ""
#. module: pos_payment_change
#: model:ir.model,name:pos_payment_change.model_pos_payment_change_wizard
msgid "PoS Payment Change Wizard"
msgstr ""
#. module: pos_payment_change
#: model:ir.model,name:pos_payment_change.model_pos_payment_change_wizard_new_line
msgid "PoS Payment Change Wizard New Line"
msgstr ""
#. module: pos_payment_change
#: model:ir.model,name:pos_payment_change.model_pos_payment_change_wizard_old_line
msgid "PoS Payment Change Wizard Old Line"
msgstr ""
#. module: pos_payment_change
#: model:ir.model,name:pos_payment_change.model_pos_config
msgid "Point of Sale Configuration"
msgstr ""
#. module: pos_payment_change
#: model:ir.model,name:pos_payment_change.model_pos_order
msgid "Point of Sale Orders"
msgstr ""
#. module: pos_payment_change
#: selection:pos.config,payment_change_policy:0
msgid "Refund and Resale"
msgstr ""
#. module: pos_payment_change
#: code:addons/pos_payment_change/models/pos_order.py:43
#, python-format
msgid "The payments of the Order %s (Ref: %s) has been changed by %s at %s."
msgstr ""
#. 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 ""
#. 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 ""
#. module: pos_payment_change
#: selection:pos.config,payment_change_policy:0
msgid "Update Payments"
msgstr ""
#. module: pos_payment_change
#: model:ir.model.fields,field_description:pos_payment_change.field_pos_payment_change_wizard_new_line__wizard_id
#: model:ir.model.fields,field_description:pos_payment_change.field_pos_payment_change_wizard_old_line__wizard_id
msgid "Wizard"
msgstr ""
#. module: pos_payment_change
#: code:addons/pos_payment_change/models/pos_order.py:97
#, python-format
msgid "You can not change payments of the POS '%s' because the associated session '%s' has been closed!"
msgstr ""

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"].sudo().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."
))

100
pos_payment_change/models/pos_order.py

@ -0,0 +1,100 @@
# 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 datetime import datetime
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()
orders = self
# 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()
comment = _(
"The payments of the Order %s (Ref: %s) has been changed"
" by %s at %s." % (
self.name,
self.pos_reference,
self.env.user.name,
datetime.today(),
)
)
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(
default={"pos_reference": self.pos_reference}
)
for line in payment_lines:
resale_order.add_payment(line)
resale_order.action_pos_order_paid()
orders += refund_order + resale_order
comment += _(" (Refund Order: %s ; Resale Order: %s)" % (
refund_order.name, resale_order.name))
for order in orders:
order.note = "%s\n%s" % (order.note or "", comment)
return orders
@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'.

2
pos_payment_change/readme/CONTRIBUTORS.rst

@ -0,0 +1,2 @@
* Sylvain LE GAL <https://twitter.com/legalsylvain>
* Julien WESTE

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: 969  |  Height: 498  |  Size: 30 KiB

1
pos_payment_change/tests/__init__.py

@ -0,0 +1 @@
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.PosPaymentChangeWizardNewLine = self.env[
"pos.payment.change.wizard.new.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.PosPaymentChangeWizardNewLine.with_context(
active_id=order.id
).create(
{
"wizard_id": wizard.id,
"new_journal_id": journal_1.id,
"amount": amount_1,
}
)
if journal_2:
self.PosPaymentChangeWizardNewLine.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>

21
pos_payment_change/views/view_pos_order.xml

@ -0,0 +1,21 @@
<?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_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 name="refund" position="before">
<button name="%(action_pos_payment_change_wizard)d"
context="{'pos_session_id' : session_id}"
string="Change Payments" type="action" states="paid,invoiced"/>
</button>
</field>
</record>
</odoo>

3
pos_payment_change/wizards/__init__.py

@ -0,0 +1,3 @@
from . import pos_payment_change_wizard
from . import pos_payment_change_wizard_new_line
from . import pos_payment_change_wizard_old_line

98
pos_payment_change/wizards/pos_payment_change_wizard.py

@ -0,0 +1,98 @@
# 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
)
old_line_ids = fields.One2many(
comodel_name="pos.payment.change.wizard.old.line",
inverse_name="wizard_id",
string="Old Payment Lines",
readonly=True,
)
new_line_ids = fields.One2many(
comodel_name="pos.payment.change.wizard.new.line",
inverse_name="wizard_id",
string="New 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"))
old_lines_vals = []
for statement_line in order.statement_ids:
old_lines_vals.append((0, 0, {
"old_journal_id": statement_line.statement_id.journal_id.id,
"amount": statement_line.amount
}
))
res.update({
"order_id": order.id,
"amount_total": order.amount_total,
"old_line_ids": old_lines_vals,
})
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 = sum(self.mapped("new_line_ids.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.new_line_ids]
orders = order.change_payment(new_payments)
# Note. Because of the poor design of the closing session process
# in Odoo, we call _check_pos_session_balance() that sets
# balance_end_real with balance_end for "non cash" journals
if order.session_id.state == "closing_control":
order.session_id._check_pos_session_balance()
if len(orders) == 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', orders.ids)]
return action

45
pos_payment_change/wizards/pos_payment_change_wizard_new_line.py

@ -0,0 +1,45 @@
# 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.new.line"
_description = "PoS Payment Change Wizard New 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.mapped(
"session_id.statement_ids.journal_id").ids)]
# View Section
@api.model
def default_get(self, fields):
res = super().default_get(fields)
if "new_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(
"new_line_ids",
self._context["new_line_ids"],
fields=["amount"]):
balance -= line.get("amount")
res.update({'amount': balance})
return res

23
pos_payment_change/wizards/pos_payment_change_wizard_old_line.py

@ -0,0 +1,23 @@
# 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 fields, models
class PosPaymentChangeWizardOldLine(models.TransientModel):
_name = "pos.payment.change.wizard.old.line"
_description = "PoS Payment Change Wizard Old Line"
wizard_id = fields.Many2one(
comodel_name="pos.payment.change.wizard", required=True,
)
old_journal_id = fields.Many2one(
comodel_name="account.journal",
string="Journal",
required=True,
readonly=True,
)
amount = fields.Float(string="Amount", required=True, readonly=True)

49
pos_payment_change/wizards/view_pos_payment_change_wizard.xml

@ -0,0 +1,49 @@
<?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" invisible="1"/>
<field name="old_line_ids" colspan="4">
<tree>
<field name="old_journal_id"/>
<field name="amount" sum="Total"/>
</tree>
</field>
<newline />
<field name="new_line_ids" colspan="4"
context="{'new_line_ids': new_line_ids, 'amount_total': amount_total}">
<tree 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