From f4458dd39868fb5c6e06b7a56f49b8fd864f98e3 Mon Sep 17 00:00:00 2001 From: Florian Mounier Date: Wed, 31 Jan 2024 16:01:28 +0100 Subject: [PATCH] [IMP] pos_sale_order: Improve pos order cancelation --- pos_sale_order/__manifest__.py | 1 + pos_sale_order/i18n/fr.po | 257 +++++++++++------- pos_sale_order/models/pos_session.py | 15 +- pos_sale_order/models/sale_order.py | 121 ++++++++- pos_sale_order/models/sale_order_line.py | 17 +- pos_sale_order/security/ir.model.access.csv | 1 + pos_sale_order/tests/common.py | 12 + pos_sale_order/tests/test_closing_session.py | 242 ++++++++++++++++- pos_sale_order/views/sale_view.xml | 9 + pos_sale_order/wizards/__init__.py | 2 + pos_sale_order/wizards/pos_payment_wizard.py | 41 +-- .../wizards/pos_sale_order_cancel_wizard.py | 26 ++ .../pos_sale_order_cancel_wizard_view.xml | 37 +++ .../wizards/pos_session_wizard_mixin.py | 52 ++++ 14 files changed, 686 insertions(+), 147 deletions(-) create mode 100644 pos_sale_order/wizards/pos_sale_order_cancel_wizard.py create mode 100644 pos_sale_order/wizards/pos_sale_order_cancel_wizard_view.xml create mode 100644 pos_sale_order/wizards/pos_session_wizard_mixin.py diff --git a/pos_sale_order/__manifest__.py b/pos_sale_order/__manifest__.py index 1684c7d..1e9588e 100644 --- a/pos_sale_order/__manifest__.py +++ b/pos_sale_order/__manifest__.py @@ -29,6 +29,7 @@ "security/ir.model.access.csv", "wizards/pos_payment_wizard_view.xml", "wizards/pos_delivery_wizard_view.xml", + "wizards/pos_sale_order_cancel_wizard_view.xml", "views/sale_view.xml", "views/pos_payment_method_views.xml", "views/point_of_sale_view.xml", diff --git a/pos_sale_order/i18n/fr.po b/pos_sale_order/i18n/fr.po index 65226f1..89461a6 100644 --- a/pos_sale_order/i18n/fr.po +++ b/pos_sale_order/i18n/fr.po @@ -4,84 +4,135 @@ # msgid "" msgstr "" -"Project-Id-Version: Odoo Server 8.0\n" +"Project-Id-Version: Odoo Server 14.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-26 17:34+0000\n" -"PO-Revision-Date: 2015-02-26 18:41+0100\n" -"Last-Translator: David BEAL \n" -"Language-Team: Akretion \n" -"Language: fr_FR\n" +"POT-Creation-Date: 2024-01-31 14:54+0000\n" +"PO-Revision-Date: 2024-01-31 14:54+0000\n" +"Last-Translator: \n" +"Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Generator: Poedit 1.5.4\n" -"X-Poedit-SourceCharset: UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: pos_sale_order +#: code:addons/pos_sale_order/models/sale_order_line.py:0 +#, python-format +msgid " REFUND" +msgstr " REMBOURSEMENT" + +#. module: pos_sale_order +#: model:ir.model,name:pos_sale_order.model_account_move +msgid "Account Move" +msgstr "Facture" #. module: pos_sale_order #: model:ir.model.fields,field_description:pos_sale_order.field_pos_payment_wizard__amount msgid "Amount" msgstr "Montant" +#. module: pos_sale_order +#: code:addons/pos_sale_order/wizards/pos_payment_wizard.py:0 +#, python-format +msgid "Amount must be superior to 0" +msgstr "Le montant doit être supérieur à 0" + #. module: pos_sale_order #: model:ir.model.fields,field_description:pos_sale_order.field_pos_config__anonymous_partner_id msgid "Anonymous Partner" msgstr "Client Anonyme" +#. module: pos_sale_order +#: model_terms:ir.ui.view,arch_db:pos_sale_order.pos_sale_order_cancel_wizard_view_form +msgid "" +"Are you sure you want to cancel this point of sale order?\n" +" A credit note will be generated for the order." +msgstr "" +"Êtes-vous sûr de vouloir annuler cette commande de point de vente ?\n" +" Un avoir sera généré pour la commande." + + #. module: pos_sale_order #: model:ir.model.fields,field_description:pos_sale_order.field_pos_config__iface_print_auto msgid "Automatic Receipt Printing" -msgstr "" +msgstr "Impression automatique du reçu" #. module: pos_sale_order #: model:ir.model.fields,field_description:pos_sale_order.field_pos_payment_wizard__available_payment_method_ids +#: model:ir.model.fields,field_description:pos_sale_order.field_pos_sale_order_cancel_wizard__available_payment_method_ids +#: model:ir.model.fields,field_description:pos_sale_order.field_pos_session_wizard_mixin__available_payment_method_ids msgid "Available Payment Method" -msgstr "" - -#. module: pos_sale_order -#: model:ir.model,name:pos_sale_order.model_account_bank_statement -msgid "Bank Statement" -msgstr "" +msgstr "Méthode de paiement disponible" #. module: pos_sale_order #: model:ir.model,name:pos_sale_order.model_account_bank_statement_line msgid "Bank Statement Line" -msgstr "" +msgstr "Ligne de relevé bancaire" #. module: pos_sale_order #: model:ir.model.fields,help:pos_sale_order.field_pos_config__iface_print_via_proxy msgid "Bypass browser printing and prints via the hardware proxy." -msgstr "" +msgstr "Ignorer l'impression du navigateur et imprimer via le proxy matériel." + +#. module: pos_sale_order +#: model:ir.model.fields,field_description:pos_sale_order.field_sale_order__pos_can_be_reinvoiced +msgid "Can be reinvoiced" +msgstr "Peut être refacturé" #. module: pos_sale_order #: model_terms:ir.ui.view,arch_db:pos_sale_order.pos_delivery_wizard_view_form #: model_terms:ir.ui.view,arch_db:pos_sale_order.pos_payment_wizard_view_form +#: model_terms:ir.ui.view,arch_db:pos_sale_order.pos_sale_order_cancel_wizard_view_form msgid "Cancel" msgstr "Annuler" +#. module: pos_sale_order +#: code:addons/pos_sale_order/models/sale_order.py:0 +#, python-format +msgid "" +"Can’t generate invoice for this order since it has been already invoiced or " +"its session is still open." +msgstr "" +"Impossible de générer une facture pour cette commande car elle a déjà été " +"facturée ou que sa session est toujours ouverte." + +#. module: pos_sale_order +#: model:ir.model,name:pos_sale_order.model_cash_box_out +msgid "Cash Box Out" +msgstr "Sortie de caisse" + #. module: pos_sale_order #: model:ir.model.fields,field_description:pos_sale_order.field_pos_payment__company_id msgid "Company" -msgstr "" +msgstr "Entreprise" #. module: pos_sale_order #: model_terms:ir.ui.view,arch_db:pos_sale_order.pos_delivery_wizard_view_form +#: model_terms:ir.ui.view,arch_db:pos_sale_order.pos_sale_order_cancel_wizard_view_form msgid "Confirm" msgstr "Valider" #. module: pos_sale_order #: model:ir.model.fields,field_description:pos_sale_order.field_pos_payment__currency_rate msgid "Conversion Rate" -msgstr "" +msgstr "Taux de conversion" #. module: pos_sale_order #: model:ir.model.fields,help:pos_sale_order.field_pos_payment__currency_rate msgid "Conversion rate from company currency to order currency." -msgstr "" +msgstr "Taux de conversion de la devise de l'entreprise à la devise de la commande." + +#. module: pos_sale_order +#: code:addons/pos_sale_order/models/sale_order.py:0 +#, python-format +msgid "Created Invoice" +msgstr "Facture créée" #. module: pos_sale_order #: model:ir.model.fields,field_description:pos_sale_order.field_pos_delivery_wizard__create_uid #: model:ir.model.fields,field_description:pos_sale_order.field_pos_delivery_wizard_line__create_uid #: model:ir.model.fields,field_description:pos_sale_order.field_pos_payment_wizard__create_uid +#: model:ir.model.fields,field_description:pos_sale_order.field_pos_sale_order_cancel_wizard__create_uid msgid "Created by" msgstr "" @@ -89,19 +140,20 @@ msgstr "" #: model:ir.model.fields,field_description:pos_sale_order.field_pos_delivery_wizard__create_date #: model:ir.model.fields,field_description:pos_sale_order.field_pos_delivery_wizard_line__create_date #: model:ir.model.fields,field_description:pos_sale_order.field_pos_payment_wizard__create_date +#: model:ir.model.fields,field_description:pos_sale_order.field_pos_sale_order_cancel_wizard__create_date msgid "Created on" msgstr "" #. module: pos_sale_order #: model:ir.model.fields,field_description:pos_sale_order.field_pos_payment__currency_id msgid "Currency" -msgstr "" +msgstr "Devise" #. module: pos_sale_order #: model:ir.model.fields,field_description:pos_sale_order.field_pos_payment__partner_id #: model:ir.ui.menu,name:pos_sale_order.menu_pos_customer msgid "Customer" -msgstr "" +msgstr "Client" #. module: pos_sale_order #: model_terms:ir.ui.view,arch_db:pos_sale_order.pos_delivery_wizard_view_form @@ -121,18 +173,21 @@ msgstr "Retrait" #. module: pos_sale_order #: model:ir.model.fields,field_description:pos_sale_order.field_account_bank_statement_line__display_name #: model:ir.model.fields,field_description:pos_sale_order.field_account_move__display_name +#: model:ir.model.fields,field_description:pos_sale_order.field_cash_box_out__display_name #: model:ir.model.fields,field_description:pos_sale_order.field_pos_config__display_name #: model:ir.model.fields,field_description:pos_sale_order.field_pos_delivery_wizard__display_name #: model:ir.model.fields,field_description:pos_sale_order.field_pos_delivery_wizard_line__display_name #: model:ir.model.fields,field_description:pos_sale_order.field_pos_payment__display_name #: model:ir.model.fields,field_description:pos_sale_order.field_pos_payment_method__display_name #: model:ir.model.fields,field_description:pos_sale_order.field_pos_payment_wizard__display_name +#: model:ir.model.fields,field_description:pos_sale_order.field_pos_sale_order_cancel_wizard__display_name #: model:ir.model.fields,field_description:pos_sale_order.field_pos_session__display_name +#: model:ir.model.fields,field_description:pos_sale_order.field_pos_session_wizard_mixin__display_name #: model:ir.model.fields,field_description:pos_sale_order.field_report_point_of_sale_report_invoice__display_name #: model:ir.model.fields,field_description:pos_sale_order.field_sale_order__display_name #: model:ir.model.fields,field_description:pos_sale_order.field_sale_order_line__display_name msgid "Display Name" -msgstr "" +msgstr "Afficher le nom" #. module: pos_sale_order #: model:ir.model.fields.selection,name:pos_sale_order.selection__sale_order__pos_payment_state__done @@ -142,7 +197,7 @@ msgstr "Fait" #. module: pos_sale_order #: code:addons/pos_sale_order/wizards/pos_delivery_wizard.py:0 -#, fuzzy, python-format +#, python-format msgid "" "Fail to deliver picking have been split, please do it from delivery menu" msgstr "" @@ -166,22 +221,38 @@ msgid "Following order have a draft invoice, please fix it %s" msgstr "" "Les commandes suivant ont des factures brouillons, merci de corriger %s" +#. module: pos_sale_order +#: code:addons/pos_sale_order/models/pos_session.py:0 +#, python-format +msgid "" +"Following sales are not invoiced, please invoice it: \n" +"- %s" +msgstr "" + #. module: pos_sale_order #: model:res.groups,name:pos_sale_order.group_show_pos_order msgid "Group to show pos order (do not use)" msgstr "" "Groupe pour afficher le menu natif du points de vente (ne pas utiliser)" +#. module: pos_sale_order +#: model:ir.model.fields,field_description:pos_sale_order.field_pos_sale_order_cancel_wizard__has_payments +msgid "Has Payments" +msgstr "A des paiements" + #. module: pos_sale_order #: model:ir.model.fields,field_description:pos_sale_order.field_account_bank_statement_line__id #: model:ir.model.fields,field_description:pos_sale_order.field_account_move__id +#: model:ir.model.fields,field_description:pos_sale_order.field_cash_box_out__id #: model:ir.model.fields,field_description:pos_sale_order.field_pos_config__id #: model:ir.model.fields,field_description:pos_sale_order.field_pos_delivery_wizard__id #: model:ir.model.fields,field_description:pos_sale_order.field_pos_delivery_wizard_line__id #: model:ir.model.fields,field_description:pos_sale_order.field_pos_payment__id #: model:ir.model.fields,field_description:pos_sale_order.field_pos_payment_method__id #: model:ir.model.fields,field_description:pos_sale_order.field_pos_payment_wizard__id +#: model:ir.model.fields,field_description:pos_sale_order.field_pos_sale_order_cancel_wizard__id #: model:ir.model.fields,field_description:pos_sale_order.field_pos_session__id +#: model:ir.model.fields,field_description:pos_sale_order.field_pos_session_wizard_mixin__id #: model:ir.model.fields,field_description:pos_sale_order.field_report_point_of_sale_report_invoice__id #: model:ir.model.fields,field_description:pos_sale_order.field_sale_order__id #: model:ir.model.fields,field_description:pos_sale_order.field_sale_order_line__id @@ -191,24 +262,9 @@ msgstr "" #. module: pos_sale_order #: model:ir.model.fields,help:pos_sale_order.field_pos_payment_method__split_transactions msgid "If ticked, each payment will generate a separated journal item." -msgstr "" +msgstr "Si coché, chaque paiement générera une écriture séparée." #. module: pos_sale_order -#: code:addons/pos_sale_order/models/pos_session.py:0 -#, python-format -msgid "" -"Impossible to reconcile all the entries.\n" -"Please check that the following ones are invoiced:\n" -"\n" -"%s" -msgstr "" -"Impossible de lettrer les écritures.\n" -"Veuillez vérifier que les commandes suivantes sont bien facturé:\n" -"\n" -"%s" - -#. module: pos_sale_order -#: model:ir.model,name:pos_sale_order.model_account_invoice #: model:ir.model.fields,field_description:pos_sale_order.field_sale_order__account_move msgid "Invoice" msgstr "Facture" @@ -220,45 +276,43 @@ msgstr "Factures" #. module: pos_sale_order #: model:ir.model.fields,field_description:pos_sale_order.field_sale_order__is_invoiced -#, fuzzy msgid "Is Invoiced" msgstr "Facture" #. module: pos_sale_order #: model:ir.model.fields,field_description:pos_sale_order.field_pos_payment_method__cash_journal_id msgid "Journal" -msgstr "Journal" - -#. module: pos_sale_order -#: model:ir.model,name:pos_sale_order.model_account_move -msgid "Journal Entry" msgstr "" #. module: pos_sale_order #: model:ir.model.fields,help:pos_sale_order.field_pos_payment_method__cash_journal_id msgid "Journal used for generating payment" -msgstr "" +msgstr "Journal utilisé pour générer le paiement" #. module: pos_sale_order #: model:ir.model.fields,field_description:pos_sale_order.field_account_bank_statement_line____last_update #: model:ir.model.fields,field_description:pos_sale_order.field_account_move____last_update +#: model:ir.model.fields,field_description:pos_sale_order.field_cash_box_out____last_update #: model:ir.model.fields,field_description:pos_sale_order.field_pos_config____last_update #: model:ir.model.fields,field_description:pos_sale_order.field_pos_delivery_wizard____last_update #: model:ir.model.fields,field_description:pos_sale_order.field_pos_delivery_wizard_line____last_update #: model:ir.model.fields,field_description:pos_sale_order.field_pos_payment____last_update #: model:ir.model.fields,field_description:pos_sale_order.field_pos_payment_method____last_update #: model:ir.model.fields,field_description:pos_sale_order.field_pos_payment_wizard____last_update +#: model:ir.model.fields,field_description:pos_sale_order.field_pos_sale_order_cancel_wizard____last_update #: model:ir.model.fields,field_description:pos_sale_order.field_pos_session____last_update +#: model:ir.model.fields,field_description:pos_sale_order.field_pos_session_wizard_mixin____last_update #: model:ir.model.fields,field_description:pos_sale_order.field_report_point_of_sale_report_invoice____last_update #: model:ir.model.fields,field_description:pos_sale_order.field_sale_order____last_update #: model:ir.model.fields,field_description:pos_sale_order.field_sale_order_line____last_update msgid "Last Modified on" -msgstr "" +msgstr "Dernière modification le" #. module: pos_sale_order #: model:ir.model.fields,field_description:pos_sale_order.field_pos_delivery_wizard__write_uid #: model:ir.model.fields,field_description:pos_sale_order.field_pos_delivery_wizard_line__write_uid #: model:ir.model.fields,field_description:pos_sale_order.field_pos_payment_wizard__write_uid +#: model:ir.model.fields,field_description:pos_sale_order.field_pos_sale_order_cancel_wizard__write_uid msgid "Last Updated by" msgstr "" @@ -266,6 +320,7 @@ msgstr "" #: model:ir.model.fields,field_description:pos_sale_order.field_pos_delivery_wizard__write_date #: model:ir.model.fields,field_description:pos_sale_order.field_pos_delivery_wizard_line__write_date #: model:ir.model.fields,field_description:pos_sale_order.field_pos_payment_wizard__write_date +#: model:ir.model.fields,field_description:pos_sale_order.field_pos_sale_order_cancel_wizard__write_date msgid "Last Updated on" msgstr "" @@ -291,13 +346,20 @@ msgstr "Ligne de mouvement" msgid "" "No cash statement found for this session. Unable to record returned cash." msgstr "" +"Aucun relevé de caisse trouvé pour cette session. Impossible d'enregistrer " #. module: pos_sale_order #: code:addons/pos_sale_order/models/pos_invoice.py:0 -#, fuzzy, python-format +#, python-format msgid "No link to an invoice for %s." msgstr "Aucune facture pour %s., " +#. module: pos_sale_order +#: code:addons/pos_sale_order/models/sale_order.py:0 +#, python-format +msgid "No payment method found for refunding this order." +msgstr "Aucun mode de paiement trouvé pour rembourser cette commande." + #. module: pos_sale_order #: model:ir.model.fields.selection,name:pos_sale_order.selection__sale_order__pos_payment_state__none #: model_terms:ir.ui.view,arch_db:pos_sale_order.sale_order_view_form @@ -307,11 +369,10 @@ msgstr "Non nécessaire" #. module: pos_sale_order #: model:ir.model.fields,field_description:pos_sale_order.field_pos_config__picking_type_id msgid "Operation Type" -msgstr "" +msgstr "Type d'opération" #. module: pos_sale_order #: model:ir.model.fields,field_description:pos_sale_order.field_pos_payment__pos_order_id -#, fuzzy msgid "Order" msgstr "Commandes" @@ -337,7 +398,7 @@ msgstr "Montant point de vente à payer" #. module: pos_sale_order #: model:ir.model.fields,field_description:pos_sale_order.field_sale_order__amount_paid msgid "Paid" -msgstr "" +msgstr "Payé" #. module: pos_sale_order #: code:addons/pos_sale_order/models/sale_order.py:0 @@ -358,13 +419,13 @@ msgstr "Paiement" #. module: pos_sale_order #: model:ir.model.fields,field_description:pos_sale_order.field_pos_payment__payment_method_id -#, fuzzy msgid "Payment Method" msgstr "Mode de paiement" #. module: pos_sale_order #: model:ir.model.fields,field_description:pos_sale_order.field_pos_payment_wizard__payment_method_id -#, fuzzy +#: model:ir.model.fields,field_description:pos_sale_order.field_pos_sale_order_cancel_wizard__payment_method_id +#: model:ir.model.fields,field_description:pos_sale_order.field_pos_session_wizard_mixin__payment_method_id msgid "Payment method" msgstr "Mode de paiement" @@ -389,11 +450,15 @@ msgstr "Livraison" msgid "Please select the journal and the amount to paid" msgstr "Merci de choisir le journal et le saisir le montant à payer" +#. module: pos_sale_order +#: model:ir.model,name:pos_sale_order.model_pos_sale_order_cancel_wizard +msgid "PoS Sales Order Cancel" +msgstr "" + #. module: pos_sale_order #: model:ir.model.fields,field_description:pos_sale_order.field_account_bank_statement_line__session_id #: model:ir.model.fields,field_description:pos_sale_order.field_account_move__session_id #: model:ir.model.fields,field_description:pos_sale_order.field_account_payment__session_id -#, fuzzy msgid "PoS Session" msgstr "Session" @@ -406,39 +471,28 @@ msgstr "Point de vente" #. module: pos_sale_order #: model:ir.model,name:pos_sale_order.model_pos_config -#, fuzzy msgid "Point of Sale Configuration" -msgstr "Point de vente" +msgstr "Paramétrage du point de vente" #. module: pos_sale_order #: model:ir.model,name:pos_sale_order.model_report_point_of_sale_report_invoice -#, fuzzy msgid "Point of Sale Invoice Report" msgstr "Point de vente" -#. module: pos_sale_order -#: model:ir.model,name:pos_sale_order.model_pos_order -#, fuzzy -msgid "Point of Sale Orders" -msgstr "Point de vente" - #. module: pos_sale_order #: model:ir.model,name:pos_sale_order.model_pos_payment_method -#, fuzzy msgid "Point of Sale Payment Methods" msgstr "Point de vente" #. module: pos_sale_order #: model:ir.model,name:pos_sale_order.model_pos_payment -#, fuzzy msgid "Point of Sale Payments" msgstr "Point de vente" #. module: pos_sale_order #: model:ir.model,name:pos_sale_order.model_pos_session -#, fuzzy msgid "Point of Sale Session" -msgstr "Point de vente" +msgstr "Session du point de vente" #. module: pos_sale_order #: model:ir.model.fields,field_description:pos_sale_order.field_account_bank_statement_line__pos_anonyme_invoice @@ -467,10 +521,15 @@ msgstr "Etat de paiement du point de vente" msgid "Pos Payment Wizard" msgstr "Assitant de paiement" +#. module: pos_sale_order +#: model:ir.model,name:pos_sale_order.model_pos_session_wizard_mixin +msgid "Pos Session Wizard Mixin" +msgstr "Mixin d'assistant de session du point de vente" + #. module: pos_sale_order #: model:ir.model.fields,field_description:pos_sale_order.field_pos_config__iface_print_via_proxy msgid "Print via Proxy" -msgstr "" +msgstr "Imprimer via le proxy" #. module: pos_sale_order #: model:ir.model.fields,field_description:pos_sale_order.field_pos_delivery_wizard_line__product_id @@ -492,27 +551,33 @@ msgstr "Devis" msgid "Receipt Ref" msgstr "Réf PdV" +#. module: pos_sale_order +#: code:addons/pos_sale_order/models/sale_order.py:0 +#, python-format +msgid "Refund" +msgstr "Avoir" + +#. module: pos_sale_order +#: model_terms:ir.ui.view,arch_db:pos_sale_order.view_order_form +msgid "Reinvoice" +msgstr "Refacturer" + #. module: pos_sale_order #: model:ir.actions.act_window,name:pos_sale_order.action_sale_order_pos +#: model:ir.model,name:pos_sale_order.model_sale_order #: model:ir.model.fields,field_description:pos_sale_order.field_account_bank_statement_line__pos_sale_order_id #: model:ir.model.fields,field_description:pos_sale_order.field_pos_payment__pos_sale_order_id #: model:ir.model.fields,field_description:pos_sale_order.field_pos_payment_wizard__sale_order_id +#: model:ir.model.fields,field_description:pos_sale_order.field_pos_sale_order_cancel_wizard__sale_order_id +#: model:ir.model.fields,field_description:pos_sale_order.field_pos_session_wizard_mixin__sale_order_id #: model_terms:ir.ui.view,arch_db:pos_sale_order.sale_order_view_tree -#, fuzzy msgid "Sale Order" msgstr "Commandes de Vente" -#. module: pos_sale_order -#: model:ir.model,name:pos_sale_order.model_sale_order -#, fuzzy -msgid "Sales Order" -msgstr "Commandes de Vente" - #. module: pos_sale_order #: model:ir.model,name:pos_sale_order.model_sale_order_line -#, fuzzy msgid "Sales Order Line" -msgstr "Ligne de commandes de Vente" +msgstr "Ligne de bons de commande" #. module: pos_sale_order #: model_terms:ir.ui.view,arch_db:pos_sale_order.pos_delivery_wizard_view_form @@ -523,17 +588,17 @@ msgstr "Choisir les produits à livrer" #: model:ir.model.fields,field_description:pos_sale_order.field_pos_payment__session_id #: model:ir.model.fields,field_description:pos_sale_order.field_sale_order__session_id msgid "Session" -msgstr "Session" +msgstr "" #. module: pos_sale_order #: model:ir.model.fields,field_description:pos_sale_order.field_pos_config__iface_print_skip_screen msgid "Skip Preview Screen" -msgstr "" +msgstr "Ignorer l'écran de prévisualisation" #. module: pos_sale_order #: model:ir.model.fields,field_description:pos_sale_order.field_pos_payment_method__split_transactions msgid "Split Transactions" -msgstr "" +msgstr "Diviser les transactions" #. module: pos_sale_order #: code:addons/pos_sale_order/models/pos_session.py:0 @@ -550,7 +615,7 @@ msgstr "L'écriture %s est déjà lettré, veuillez corriger" #. module: pos_sale_order #: model:ir.model.fields,help:pos_sale_order.field_sale_order__config_id msgid "The physical point of sale you will use." -msgstr "" +msgstr "Le point de vente physique que vous utiliserez." #. module: pos_sale_order #: model:ir.model.constraint,message:pos_sale_order.constraint_sale_order_pos_reference_uniq @@ -564,29 +629,39 @@ msgid "" "The receipt screen will be skipped if the receipt can be printed " "automatically." msgstr "" +"L'écran de reçu sera ignoré si le reçu peut être imprimé " +"automatiquement." #. module: pos_sale_order #: model:ir.model.fields,help:pos_sale_order.field_pos_config__iface_print_auto msgid "The receipt will automatically be printed at the end of each order." -msgstr "" +msgstr "Le reçu sera automatiquement imprimé à la fin de chaque commande." #. module: pos_sale_order -#: code:addons/pos_sale_order/wizards/pos_payment_wizard.py:0 +#: code:addons/pos_sale_order/wizards/pos_session_wizard_mixin.py:0 #, python-format -msgid "There is no session Opened with payment method, please open one" +msgid "There is no session opened with payment method, please open one" +msgstr "Il n'y a pas de session ouverte avec un mode de paiement, veuillez en ouvrir une" + +#. module: pos_sale_order +#: model_terms:ir.ui.view,arch_db:pos_sale_order.pos_sale_order_cancel_wizard_view_form +msgid "" +"This order has payments and they will be refunded if you continue.
\n" +"\n" +" Please choose a payment method for the refund:" msgstr "" -"Il n'y a pas de session de point de vente ouverte avec des méthodes de " -"paiements, merci d'en ouvrir une" +"Cette commande a des paiements et ils seront remboursés si vous continuez.
\n" +"\n" +" Veuillez choisir un mode de paiement pour le remboursement :" #. module: pos_sale_order #: code:addons/pos_sale_order/models/pos_session.py:0 #, python-format msgid "This session is already closed." -msgstr "" +msgstr "Cette session est déjà fermée." #. module: pos_sale_order #: model:ir.model.fields,field_description:pos_sale_order.field_sale_order__to_invoice -#, fuzzy msgid "To invoice" msgstr "Facture" @@ -604,4 +679,4 @@ msgstr "Entrepots" #: code:addons/pos_sale_order/models/sale_order.py:0 #, python-format msgid "return" -msgstr "" +msgstr "Retour" diff --git a/pos_sale_order/models/pos_session.py b/pos_sale_order/models/pos_session.py index 102aa52..c8b6f7f 100644 --- a/pos_sale_order/models/pos_session.py +++ b/pos_sale_order/models/pos_session.py @@ -70,7 +70,7 @@ def _prepare_sale_statement(self, payments, record_ref): if record_ref._name == "sale.order": ref = " - ".join([record_ref.name] + record_ref.invoice_ids.mapped("name")) elif record_ref._name == "account.move": - ref = record_ref.name + ref = " - ".join(record_ref.mapped("name")) method = payments.payment_method_id return { "date": fields.Date.context_today(self), @@ -98,8 +98,10 @@ def _get_receivable_line(self, payments): == "posted" ).line_ids.filtered( # get receivable line - lambda s: s.account_id - == payments.payment_method_id.receivable_account_id + lambda s: s.account_id == payments.payment_method_id.receivable_account_id + # only not reconciled line, this can happen if the payment + # is a refund of a previous invoice + and not s.reconciled ) def _create_bank_statement_line(self, statement, payments, record_ref): @@ -142,6 +144,9 @@ def _create_bank_statement_line_and_reconcile(self): "name": self.name, } ) + if not statement: + # Should we raise? + continue for record_ref, payments in payment_per_key.items(): if not float_is_zero( sum(payments.mapped("amount")), @@ -177,7 +182,8 @@ def _create_account_move(self): self._check_no_draft_invoice() partner_to_orders = defaultdict(lambda: self.env["sale.order"].browse(False)) - for order in self._get_order_to_invoice(): + orders_to_invoice = self._get_order_to_invoice() + for order in orders_to_invoice: partner_to_orders[order.partner_id] += order for partner, orders in partner_to_orders.items(): @@ -186,6 +192,7 @@ def _create_account_move(self): default_journal_id=self.config_id.journal_id.id ) orders._create_invoices(final=True) + # Create credit notes for invoiced cancelled orders orders.invoice_ids.filtered(lambda s: s.state == "draft").action_post() self._create_bank_statement_line_and_reconcile() return True diff --git a/pos_sale_order/models/sale_order.py b/pos_sale_order/models/sale_order.py index 8fa22b4..7988e38 100644 --- a/pos_sale_order/models/sale_order.py +++ b/pos_sale_order/models/sale_order.py @@ -84,6 +84,9 @@ class SaleOrder(models.Model): default=0, ) is_invoiced = fields.Boolean("Is Invoiced", compute="_compute_is_invoiced") + pos_can_be_reinvoiced = fields.Boolean( + "Can be reinvoiced", compute="_compute_pos_can_be_reinvoiced" + ) @api.model def _payment_fields(self, order, ui_paymentline): @@ -101,6 +104,19 @@ def _compute_is_invoiced(self): for order in self: order.is_invoiced = bool(order.account_move) + @api.depends("is_invoiced", "state") + def _compute_pos_can_be_reinvoiced(self): + for order in self: + # We can reinvoice only if the order if session is closed and + # the order is not invoiced or it is invoiced only in the global + # pos invoice + order.pos_can_be_reinvoiced = ( + # + order.session_id.state == "closed" + and set(order.invoice_ids.mapped("journal_id")) + == set(order.session_id.config_id.journal_id) + ) + @api.depends("amount_total", "payment_ids.amount", "state") def _compute_pos_payment(self): for record in self: @@ -144,15 +160,17 @@ def _compute_unit_to_deliver(self): ) def open_pos_payment_wizard(self): - self.ensure_one() - wizard = self.env["pos.payment.wizard"].create_wizard(self) - action = wizard.get_formview_action() - action["target"] = "new" - return action + return self.open_pos_wizard("pos.payment.wizard") + + def open_pos_cancel_order_wizard(self): + return self.open_pos_wizard("pos.sale.order.cancel.wizard") def open_pos_delivery_wizard(self): + return self.open_pos_wizard("pos.delivery.wizard") + + def open_pos_wizard(self, model_name): self.ensure_one() - wizard = self.env["pos.delivery.wizard"].create_wizard(self) + wizard = self.env[model_name].create_wizard(self) action = wizard.get_formview_action() action["target"] = "new" return action @@ -312,3 +330,94 @@ def add_payment(self, data): # skip useless 0 payment that will bloc closing the session if data.get("amount"): return super().add_payment(data) + + def _prepare_refund_payment(self, amount): + payment_method = self.env["pos.payment.method"].browse( + self._context.get("pos_cancel_payment_method_id") + ) + + # The refund payment should always happen in the current session + + return { + "name": _("Refund"), + "session_id": self.session_id.config_id.current_session_id.id, + "pos_sale_order_id": self.id, + "amount": -amount, + "payment_date": fields.Datetime.now(), + "payment_method_id": payment_method.id, + } + + def action_cancel(self): + if self.session_id: + if not self._context.get("disable_pos_cancel_warning"): + return self.open_pos_cancel_order_wizard() + + if not self._context.get("pos_cancel_payment_method_id"): + raise UserError(_("No payment method found for refunding this order.")) + + if self.session_id.state in ("opened", "closing_control"): + # If the session is still open, we need to create the invoice + # and then refund it. + if self.state == "draft": + self.action_confirm() + if not self.invoice_ids: + self._create_invoices(final=True) + self.invoice_ids.action_post() + + rv = super().action_cancel() + + if self.session_id: + # We need to refund any payment made, the payment method should + # have been set in the wizard + if self.payment_ids: + self.add_payment(self._prepare_refund_payment(self.amount_paid)) + + if self.session_id.state in ("opened", "closing_control"): + # If the session is still open, we need to refund the invoice + # cancel=True allows for reconciliation + self.invoice_ids._reverse_moves(cancel=True) + + elif self.session_id.state == "closed": + # If the session is closed, we need to create a credit note + # in the current session, it will be reconciled at the + # session close. + # sale_order_qty_to_invoice_cancelled will take care of + # creating the credit note on reinvoicing + refund = self._create_invoices(final=True) + refund.action_post() + + return rv + + def action_reinvoice_pos_order(self): + self.ensure_one() + if not self.pos_can_be_reinvoiced: + raise UserError( + _( + "Can’t generate invoice for this order since it has been " + "already invoiced or its session is still open." + ) + ) + # We use a little hack here to force a refund creation + for line in self.order_line: + line.qty_to_invoice = -line.qty_invoiced + # Create the refund + refund = self._create_invoices(final=True) + refund.action_post() + # Now we can create the standalone invoice + invoice = self._create_invoices(final=True) + invoice.action_post() + # We need to reconcile these two invoices + (refund | invoice).line_ids.filtered( + lambda ml: ml.account_id.reconcile + or ml.account_id.internal_type == "liquidity" + ).reconcile() + # Then we display the new invoice + return { + "name": _("Created Invoice"), + "view_mode": "form", + "res_id": invoice.id, + "res_model": "account.move", + "view_id": False, + "type": "ir.actions.act_window", + "context": self.env.context, + } diff --git a/pos_sale_order/models/sale_order_line.py b/pos_sale_order/models/sale_order_line.py index e3941c2..b1461e9 100644 --- a/pos_sale_order/models/sale_order_line.py +++ b/pos_sale_order/models/sale_order_line.py @@ -2,16 +2,17 @@ # @author Sébastien BEAU # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -from odoo import api, models +from odoo import _, models class SaleOrderLine(models.Model): _inherit = "sale.order.line" - @api.depends("order_id.session_id") - def _get_to_invoice_qty(self): - for record in self: - if record.order_id.session_id: - record.qty_to_invoice = record.product_uom_qty - record.qty_invoiced - else: - super(SaleOrderLine, record)._get_to_invoice_qty() + def _prepare_refund_data(self, refund_order): + self.ensure_one() + return { + "name": self.name + _(" REFUND"), + "product_uom_qty": -self.product_uom_qty, + "price_unit": self.price_unit, + "order_id": refund_order.id, + } diff --git a/pos_sale_order/security/ir.model.access.csv b/pos_sale_order/security/ir.model.access.csv index 916d35d..c751e84 100644 --- a/pos_sale_order/security/ir.model.access.csv +++ b/pos_sale_order/security/ir.model.access.csv @@ -2,3 +2,4 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink access_pos_payment_wizard,pos.payment.wizard,model_pos_payment_wizard,sales_team.group_sale_salesman,1,1,1,1 access_pos_delivery_wizard,pos.delivery.wizard,model_pos_delivery_wizard,sales_team.group_sale_salesman,1,1,1,1 access_pos_delivery_wizard_line,pos.delivery.wizard.line,model_pos_delivery_wizard_line,sales_team.group_sale_salesman,1,1,1,1 +access_pos_sale_order_cancel_wizard,pos.sale.order.cancel.wizard,model_pos_sale_order_cancel_wizard,sales_team.group_sale_salesman,1,1,1,1 diff --git a/pos_sale_order/tests/common.py b/pos_sale_order/tests/common.py index f9a2876..61055b1 100644 --- a/pos_sale_order/tests/common.py +++ b/pos_sale_order/tests/common.py @@ -71,6 +71,18 @@ def setUpClass(cls, chart_template_ref=None): ("company_id", "=", cls.company.id), ] ) + bank = cls.basic_config.payment_method_ids.filtered( + lambda pm: not pm.is_cash_count and not pm.split_transactions + )[:1] + bank.receivable_account_id = cls.env["account.account"].search( + [ + ("name", "=", "Account Receivable"), + ("company_id", "=", cls.company.id), + ] + ) + # Bank payment method should have a cash journal in pos sale order + bank.cash_journal_id = cls.company_data["default_journal_bank"] + cls.config = cls.basic_config.with_context(**ctx) cls.open_new_session(cls) # open_new_session is not a class method, hack it cls.partner_2 = cls.env.ref("base.res_partner_2") diff --git a/pos_sale_order/tests/test_closing_session.py b/pos_sale_order/tests/test_closing_session.py index 5fe50d7..c06a836 100644 --- a/pos_sale_order/tests/test_closing_session.py +++ b/pos_sale_order/tests/test_closing_session.py @@ -5,6 +5,7 @@ from odoo.exceptions import UserError from odoo.tests.common import tagged +from odoo.tools import float_compare, float_is_zero from odoo.addons.queue_job.job import Job @@ -85,8 +86,13 @@ def test_no_cash_payment(self): self.assertEqual(sum(moves.mapped("amount_total")), 390) def test_payment_sum_zero(self): - self.sales.action_cancel() - self.sales[1:].unlink() + for sale in self.sales[1:]: + sale.with_context( + disable_pos_cancel_warning=True, + pos_cancel_payment_method_id=self.cash_pm.id, + ).action_cancel() + sale.unlink() + data = self._get_pos_data( lines=[ (self.product0, -3.0), @@ -335,6 +341,238 @@ def test_job_execute_after_closing(self): Job.load(self.env, jobs[0].uuid).perform() + def test_close_session_with_cancelled_orders(self): + # We cancel an anonymous order and two partner 3 orders + anonymous_so = self.sales[0] + partner_3_so = self.sales.filtered(lambda s: s.partner_id == self.partner_3) + cancelled_so = anonymous_so | partner_3_so + not_cancelled_so = self.sales - cancelled_so + + for so in cancelled_so: + rv = so.action_cancel() + self.assertEqual(rv["type"], "ir.actions.act_window") + self.assertEqual(rv["res_model"], "pos.sale.order.cancel.wizard") + + rv = so.with_context( + disable_pos_cancel_warning=True, + pos_cancel_payment_method_id=self.cash_pm.id, + ).action_cancel() + self.assertEqual(rv, True) + + # Each cancelled order should have an invoice and a refund + # and the to_invoice order should have an invoice + invoices = self.sales.invoice_ids.filtered( + lambda invoice: invoice.move_type == "out_invoice" + ) + refunds = self.sales.invoice_ids.filtered( + lambda invoice: invoice.move_type == "out_refund" + ) + + self.assertEqual(len(invoices), 4) + self.assertEqual(len(refunds), 3) + + self._close_session() + + # We should now have the 2 more invoice, one for the anonymous SO + # and one for the non to_invoice partner 2 SO + + invoices = self.sales.invoice_ids.filtered( + lambda invoice: invoice.move_type == "out_invoice" + ) + refunds = self.sales.invoice_ids.filtered( + lambda invoice: invoice.move_type == "out_refund" + ) + self.assertEqual(len(invoices), 6) + self.assertEqual(len(refunds), 3) + + precision = self.env["decimal.precision"].precision_get( + "Product Unit of Measure" + ) + + self.assertTrue( + all( + [ + float_compare( + sol.qty_invoiced, + sol.product_uom_qty, + precision_digits=precision, + ) + >= 0 + for sale in not_cancelled_so + for sol in sale.order_line + ] + ), + "All the not cancelled sale order lines should be invoiced", + ) + + self.assertTrue( + all( + [ + float_is_zero( + sol.qty_invoiced, + precision_digits=precision, + ) + for sale in cancelled_so + for sol in sale.order_line + ] + ), + "All the cancelled sale order lines should be invoiced and refunded", + ) + + self.assertEquals( + sum([sale.amount_total for sale in self.sales]), + sum([invoice.amount_total for invoice in invoices]), + "All sales should be fully invoiced", + ) + + self.assertEquals( + sum([sale.amount_total for sale in cancelled_so]), + sum([invoice.amount_total for invoice in refunds]), + "All cancelled sales should be fully refunded", + ) + + self.assertEqual(self.pos_session.invoice_ids, self.sales.invoice_ids) + self.assertEqual(len(self.sales.invoice_ids), 6 + 3) + + self.assertEqual(set(invoices.mapped("state")), {"posted"}) + self.assertEqual(set(refunds.mapped("state")), {"posted"}) + + self.assertEqual(set(invoices.mapped("payment_state")), {"paid", "reversed"}) + # The cancelled orders should have been refunded + self.assertEqual(set(refunds.mapped("payment_state")), {"paid"}) + + move = self.env["account.move"].search( + [("journal_id", "=", self.cash_pm.cash_journal_id.id)] + ) + # we now have only 3 payments (one per non refunded invoice) + self.assertEqual(len(move), 3) + self.assertEqual(sum(move.mapped("amount_total")), 195) + + def test_cancel_order_after_session_close(self): + # Close session + self._close_session() + # Cancel first order of partner 3 + partner_3_so = self.sales.filtered(lambda s: s.partner_id == self.partner_3)[0] + # We have one invoice one payment + self.assertEqual(len(partner_3_so.invoice_ids), 1) + self.assertEqual(len(partner_3_so.payment_ids), 1) + + # Without session, we can't cancel the order + with self.assertRaisesRegex(UserError, "There is no session opened"): + partner_3_so.action_cancel() + + # Open a new session + self.config.open_session_cb(check_coa=False) + self.pos_session = self.config.current_session_id + self._create_session_sale(pos_session=self.pos_session) + + # Cancel the order + rv = partner_3_so.action_cancel() + self.assertEqual(rv["type"], "ir.actions.act_window") + self.assertEqual(rv["res_model"], "pos.sale.order.cancel.wizard") + + rv = partner_3_so.with_context( + disable_pos_cancel_warning=True, + pos_cancel_payment_method_id=self.cash_pm.id, + ).action_cancel() + self.assertEqual(rv, True) + + partner_3_so.action_cancel() + + # We should now have 2 invoices 2 payments for the partner 3 sale order + self.assertEqual(len(partner_3_so.invoice_ids), 2) + self.assertEqual(len(partner_3_so.payment_ids), 2) + self.assertEqual(set(partner_3_so.invoice_ids.mapped("state")), {"posted"}) + # An invoice and a refund + self.assertEqual( + set(partner_3_so.invoice_ids.mapped("move_type")), + {"out_invoice", "out_refund"}, + ) + + # Close the current session + self._close_session() + # The sale order should still have 2 invoices and 2 payments + self.assertEqual(len(partner_3_so.invoice_ids), 2) + self.assertEqual(len(partner_3_so.payment_ids), 2) + # The refund should have been reconciled + self.assertEqual( + set(partner_3_so.invoice_ids.mapped("payment_state")), {"paid"} + ) + + def test_close_session_with_cancelled_orders_with_different_payment_method(self): + # We cancel an anonymous order with bank payment + so = self.sales[0] + rv = so.with_context( + disable_pos_cancel_warning=True, + pos_cancel_payment_method_id=self.bank_pm.id, + ).action_cancel() + + self.assertEqual(rv, True) + self.assertEqual(len(so.invoice_ids), 2) + self.assertEqual( + set(so.invoice_ids.mapped("move_type")), + {"out_invoice", "out_refund"}, + ) + self._close_session() + self.assertEqual(len(so.invoice_ids), 2) + self.assertEqual( + set(so.invoice_ids.mapped("move_type")), + {"out_invoice", "out_refund"}, + ) + self.assertEqual(set(so.invoice_ids.mapped("state")), {"posted"}) + # The invoices should have been reconciled anyway + self.assertEqual( + set(so.invoice_ids.mapped("payment_state")), {"paid", "reversed"} + ) + + def test_cancel_order_after_session_close_with_different_payment_method(self): + # Close session + self._close_session() + + # Open a new session + self.config.open_session_cb(check_coa=False) + self.pos_session = self.config.current_session_id + self._create_session_sale(pos_session=self.pos_session) + # We cancel an anonymous order with bank payment + + so = self.sales[0] + rv = so.with_context( + disable_pos_cancel_warning=True, + pos_cancel_payment_method_id=self.bank_pm.id, + ).action_cancel() + + self.assertEqual(rv, True) + self.assertEqual(len(so.invoice_ids), 2) + self.assertEqual( + set(so.invoice_ids.mapped("move_type")), + {"out_invoice", "out_refund"}, + ) + self._close_session() + self.assertEqual(len(so.invoice_ids), 2) + self.assertEqual( + set(so.invoice_ids.mapped("move_type")), + {"out_invoice", "out_refund"}, + ) + self.assertEqual(set(so.invoice_ids.mapped("state")), {"posted"}) + # The invoices should have been reconciled anyway + self.assertEqual(set(so.invoice_ids.mapped("payment_state")), {"paid"}) + + def test_reinvoicing(self): + self._close_session() + so = self.sales[0] + self.assertEqual(len(so.invoice_ids), 1) + self.assertEqual(so.invoice_status, "invoiced") + self.assertEqual(so.invoice_ids.state, "posted") + self.assertEqual(so.invoice_ids.payment_state, "paid") + self.assertEqual(so.invoice_ids.move_type, "out_invoice") + so.action_reinvoice_pos_order() + self.assertEqual(len(so.invoice_ids), 3) + self.assertEqual(so.invoice_status, "invoiced") + self.assertEqual(set(so.invoice_ids.mapped("state")), {"posted"}) + self.assertEqual(so.invoice_ids[1].move_type, "out_refund") + self.assertEqual(so.invoice_ids[2].move_type, "out_invoice") + self.assertEqual(set(so.invoice_ids.mapped("payment_state")), {"paid"}) + @tagged("-at_install", "post_install") class SpecialCase(CommonCase): diff --git a/pos_sale_order/views/sale_view.xml b/pos_sale_order/views/sale_view.xml index d41ece4..a580883 100644 --- a/pos_sale_order/views/sale_view.xml +++ b/pos_sale_order/views/sale_view.xml @@ -5,6 +5,15 @@ sale.order +
+ +
+# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class PosSaleOrderCancelWizard(models.TransientModel): + _name = "pos.sale.order.cancel.wizard" + _description = "PoS Sales Order Cancel" + _inherit = "pos.session.wizard.mixin" + + has_payments = fields.Boolean("Has Payments", readonly=True) + + def _prepare_wizard(self, sale, payment_methods, default_method): + vals = super()._prepare_wizard(sale, payment_methods, default_method) + vals["has_payments"] = bool(sale.payment_ids) + return vals + + def action_cancel(self): + return self.sale_order_id.with_context( + { + "disable_pos_cancel_warning": True, + "pos_cancel_payment_method_id": self.payment_method_id.id, + } + ).action_cancel() diff --git a/pos_sale_order/wizards/pos_sale_order_cancel_wizard_view.xml b/pos_sale_order/wizards/pos_sale_order_cancel_wizard_view.xml new file mode 100644 index 0000000..8ba6f25 --- /dev/null +++ b/pos_sale_order/wizards/pos_sale_order_cancel_wizard_view.xml @@ -0,0 +1,37 @@ + + + + pos.sale.order.cancel.wizard.form + pos.sale.order.cancel.wizard + +
+ + +

+ Are you sure you want to cancel this point of sale order? + A credit note will be generated for the order. +

+
+

+ This order has payments and they will be refunded if you continue.
+ + Please choose a payment method for the refund: +

+ +
+
+
+ +
+
+
diff --git a/pos_sale_order/wizards/pos_session_wizard_mixin.py b/pos_sale_order/wizards/pos_session_wizard_mixin.py new file mode 100644 index 0000000..524bf20 --- /dev/null +++ b/pos_sale_order/wizards/pos_session_wizard_mixin.py @@ -0,0 +1,52 @@ +# Copyright 2020-2024 Akretion (https://www.akretion.com). +# @author Sébastien BEAU +# @author Florian Mounier +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import _, fields, models +from odoo.exceptions import UserError + + +class PosSessionWizardMixin(models.AbstractModel): + _name = "pos.session.wizard.mixin" + _description = "Pos Session Wizard Mixin" + + sale_order_id = fields.Many2one("sale.order") + payment_method_id = fields.Many2one("pos.payment.method", "Payment method") + available_payment_method_ids = fields.Many2many( + comodel_name="pos.payment.method", + string="Available Payment Method", + readonly=True, + ) + + def _get_session(self): + session = self.env["pos.session"].search( + [ + ("state", "=", "opened"), + ("user_id", "=", self._uid), + ("rescue", "=", False), + ("payment_method_ids", "!=", False), + ] + ) + if not session: + raise UserError( + _("There is no session opened with payment method, please open one") + ) + return session + + def _prepare_wizard(self, sale, payment_methods, default_method): + return { + "sale_order_id": sale.id, + "available_payment_method_ids": [(6, 0, payment_methods.ids)], + "payment_method_id": default_method.id, + } + + def create_wizard(self, sale): + payment_methods = self._get_session().payment_method_ids + default_method = payment_methods[0] + for method in payment_methods: + if method.is_cash_count: + default_method = method + break + + return self.create(self._prepare_wizard(sale, payment_methods, default_method))