diff --git a/pos_multiple_control/README.rst b/pos_multiple_control/README.rst new file mode 100644 index 00000000..3b06b748 --- /dev/null +++ b/pos_multiple_control/README.rst @@ -0,0 +1,114 @@ +===================================== +Point Of Sale - Multiple Cash Control +===================================== + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! 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-quentinDupont%2Fgrap--odoo--incubator-lightgray.png?logo=github + :target: https://github.com/quentinDupont/grap-odoo-incubator/tree/12.0_pos_multiple_control/pos_multiple_control + :alt: quentinDupont/grap-odoo-incubator + +|badge1| |badge2| |badge3| + +This module extends the functionality of the point of sale by allowing a +better control at the closing of the session. + +* Show differences for all statements chosen (see config part) + +* Allow user to control each statement. (not only the cash statement, by + default) and change his starting and ending balance + +.. figure:: https://raw.githubusercontent.com/quentinDupont/grap-odoo-incubator/12.0_pos_multiple_control/pos_multiple_control/static/description/change_starting_balance.gif + +* Quickly solve differences (we can set an absolute limit), + thanks to pos_move_reason + +See this gif where where we set ending balance with difference, solve it and +close session : + +.. figure:: https://raw.githubusercontent.com/quentinDupont/grap-odoo-incubator/12.0_pos_multiple_control/pos_multiple_control/static/description/end_session_balance_automatic_solve.gif + +As the verification is more complete, allow the user to reopen a new session, +if the first one is in a closed state. + +.. figure:: https://raw.githubusercontent.com/quentinDupont/grap-odoo-incubator/12.0_pos_multiple_control/pos_multiple_control/static/description/open_new_session.png + +Extra checks are done, to prevent user errors: + +* It is not possible to click on the button 'Close Session' if there are some + draft orders. + +**Table of contents** + +.. contents:: + :local: + +Configuration +============= + +To configure this module, you need to: + +**Configure the journals** you want to control in your point of sale (POS) + +* Go to Invoicing / Configuration / Journals / Journals + +* Check the box 'POS Journal Control' if you want to enable this feature for this journal + +* Unchecked journal will be in grey + +.. figure:: https://raw.githubusercontent.com/quentinDupont/grap-odoo-incubator/12.0_pos_multiple_control/pos_multiple_control/static/description/account_journal_config.png + +**Configure your POS configuration** + +* Go to Point of Sale / Configuration / Point of Sale / + +* Choose your Payments methods (1) + +* Check Cash Control (2) + +* (optional) Choose a "pos move reason" to ausolve difference (3) (see pos_move_reason module for more details) + +* (optional) Choose a limit to allow or not the user to autosolve control difference in pos - Set 0 if you don't want any limit. (4) + +.. figure:: https://raw.githubusercontent.com/quentinDupont/grap-odoo-incubator/12.0_pos_multiple_control/pos_multiple_control/static/description/pos_session_config.png + +Bug Tracker +=========== + +Bugs are tracked on `GitHub 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 `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* GRAP + +Contributors +~~~~~~~~~~~~ + +* Sylvain LE GAL +* Julien WESTE +* Quentin DUPONT + +Maintainers +~~~~~~~~~~~ + +This module is part of the `quentinDupont/grap-odoo-incubator `_ project on GitHub. + +You are welcome to contribute. diff --git a/pos_multiple_control/__init__.py b/pos_multiple_control/__init__.py new file mode 100644 index 00000000..134df274 --- /dev/null +++ b/pos_multiple_control/__init__.py @@ -0,0 +1,2 @@ +from . import wizard +from . import models diff --git a/pos_multiple_control/__manifest__.py b/pos_multiple_control/__manifest__.py new file mode 100644 index 00000000..08e2b452 --- /dev/null +++ b/pos_multiple_control/__manifest__.py @@ -0,0 +1,33 @@ +# Copyright (C) 2013 - Today: GRAP (http://www.grap.coop) +# @author Julien WESTE +# @author: Sylvain LE GAL (https://twitter.com/legalsylvain) +# @author: Quentin DUPONT (https://twitter.com/pondupont) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +{ + "name": "Point Of Sale - Multiple Cash Control", + "summary": "Allow user to control each statement and add extra checks", + "version": "12.0.1.0.0", + "category": "Point of Sale", + "author": "GRAP, Odoo Community Association (OCA)", + "website": "http://www.grap.coop", + "license": "AGPL-3", + "depends": [ + "point_of_sale", + "pos_cash_move_reason" + ], + "data": [ + "views/view_account_journal.xml", + "views/view_pos_config.xml", + "views/view_pos_session.xml", + "wizard/wizard_pos_update_statement_balance.xml" + ], + 'demo': [ + 'demo/res_groups.xml', + 'demo/account_account.xml', + 'demo/account_journal.xml', + 'demo/pos_move_reason.xml', + 'demo/pos_config.xml', + ], + "installable": True, +} diff --git a/pos_multiple_control/demo/account_account.xml b/pos_multiple_control/demo/account_account.xml new file mode 100644 index 00000000..9a423af4 --- /dev/null +++ b/pos_multiple_control/demo/account_account.xml @@ -0,0 +1,21 @@ + + + + + + 7582 + Cash Register Error Income + + + + + 6582 + Cash Register Error Expense + + + + diff --git a/pos_multiple_control/demo/account_journal.xml b/pos_multiple_control/demo/account_journal.xml new file mode 100644 index 00000000..a6ab8d9c --- /dev/null +++ b/pos_multiple_control/demo/account_journal.xml @@ -0,0 +1,26 @@ + + + + + + + CHK-C + bank + Check Journal (With Control) + + + + + + CSH-C + cash + Cash Journal (With Control) + + + + + diff --git a/pos_multiple_control/demo/pos_config.xml b/pos_multiple_control/demo/pos_config.xml new file mode 100644 index 00000000..376c1fb2 --- /dev/null +++ b/pos_multiple_control/demo/pos_config.xml @@ -0,0 +1,22 @@ + + + + + + + Point Of Sale (With Mutiple Controls) + + + + + + + + diff --git a/pos_multiple_control/demo/pos_move_reason.xml b/pos_multiple_control/demo/pos_move_reason.xml new file mode 100644 index 00000000..52be6656 --- /dev/null +++ b/pos_multiple_control/demo/pos_move_reason.xml @@ -0,0 +1,23 @@ + + + + + + + Cash register error + + + + + + + + + diff --git a/pos_multiple_control/demo/res_groups.xml b/pos_multiple_control/demo/res_groups.xml new file mode 100644 index 00000000..b9a70b6f --- /dev/null +++ b/pos_multiple_control/demo/res_groups.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + diff --git a/pos_multiple_control/i18n/fr.po b/pos_multiple_control/i18n/fr.po new file mode 100644 index 00000000..c2acfe75 --- /dev/null +++ b/pos_multiple_control/i18n/fr.po @@ -0,0 +1,497 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * pos_multiple_control +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 12.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2020-04-09 15:56+0000\n" +"PO-Revision-Date: 2020-04-09 15:56+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_multiple_control +#: model_terms:pos.config,customer_facing_display_html:pos_multiple_control.pos_config_control +msgid "$ 3.12" +msgstr "3,12 $" + +#. module: pos_multiple_control +#: model_terms:pos.config,customer_facing_display_html:pos_multiple_control.pos_config_control +msgid "$ 4.40" +msgstr "4,40 $" + +#. module: pos_multiple_control +#: model_terms:pos.config,customer_facing_display_html:pos_multiple_control.pos_config_control +msgid "$ 4.50" +msgstr "4,50 $" + +#. module: pos_multiple_control +#: model_terms:pos.config,customer_facing_display_html:pos_multiple_control.pos_config_control +msgid "$ 8.50" +msgstr "8,50 $" + +#. module: pos_multiple_control +#: model_terms:ir.ui.view,arch_db:pos_multiple_control.view_pos_session_form +msgid "+ Transactions" +msgstr "" + +#. module: pos_multiple_control +#: model_terms:ir.ui.view,arch_db:pos_multiple_control.view_pos_session_form +msgid "Set Opening Balance" +msgstr "Réglez le solde d'ouverture" + +#. module: pos_multiple_control +#: model_terms:pos.config,customer_facing_display_html:pos_multiple_control.pos_config_control +msgid "$ 0.86" +msgstr "" + +#. module: pos_multiple_control +#: model_terms:pos.config,customer_facing_display_html:pos_multiple_control.pos_config_control +msgid "Change" +msgstr "" + +#. module: pos_multiple_control +#: model_terms:pos.config,customer_facing_display_html:pos_multiple_control.pos_config_control +msgid "TOTAL" +msgstr "" + +#. module: pos_multiple_control +#: model_terms:pos.config,customer_facing_display_html:pos_multiple_control.pos_config_control +msgid "$ 469.14" +msgstr "" + +#. module: pos_multiple_control +#: model_terms:pos.config,customer_facing_display_html:pos_multiple_control.pos_config_control +msgid "$ 470.00" +msgstr "" + +#. module: pos_multiple_control +#: model_terms:pos.config,customer_facing_display_html:pos_multiple_control.pos_config_control +msgid "Cash (USD):" +msgstr "" + +#. module: pos_multiple_control +#: model_terms:ir.ui.view,arch_db:pos_multiple_control.view_wizard_pos_update_bank_statement_balance_form +msgid "Apply" +msgstr "Appliquer" + +#. module: pos_multiple_control +#: model:ir.model.fields,field_description:pos_multiple_control.field_pos_config__autosolve_limit +msgid "Autosolve limit" +msgstr "Limite de résolution automatique" + +#. module: pos_multiple_control +#: model:ir.model.fields,field_description:pos_multiple_control.field_pos_config__autosolve_pos_move_reason +msgid "Autosolve pos move reason" +msgstr "Raison de résolution automatique" + +#. module: pos_multiple_control +#: model:ir.model.fields,field_description:pos_multiple_control.field_wizard_pos_update_bank_statement_balance_line__balance_end_real +msgid "Balance End Real" +msgstr "Solde réel de fermeture" + +#. module: pos_multiple_control +#: model:ir.model.fields,field_description:pos_multiple_control.field_wizard_pos_update_bank_statement_balance_line__balance_start_real +msgid "Balance Start Real" +msgstr "Solde réel d'ouverture" + +#. module: pos_multiple_control +#: model:ir.model.fields,field_description:pos_multiple_control.field_wizard_pos_update_bank_statement_balance__balance_moment +#: model:ir.model.fields,field_description:pos_multiple_control.field_wizard_pos_update_bank_statement_balance_line__balance_moment +msgid "Balance moment" +msgstr "Moment de balance" + +#. module: pos_multiple_control +#: model:ir.model,name:pos_multiple_control.model_account_bank_statement +#: model:ir.model.fields,field_description:pos_multiple_control.field_pos_session__statement_ids +msgid "Bank Statement" +msgstr "Relevé bancaire" + +#. module: pos_multiple_control +#: model_terms:ir.ui.view,arch_db:pos_multiple_control.view_wizard_pos_update_bank_statement_balance_form +msgid "Cancel" +msgstr "Annuler" + +#. module: pos_multiple_control +#: model:ir.model,name:pos_multiple_control.model_cash_box_in +msgid "Cash Box In" +msgstr "" + +#. module: pos_multiple_control +#: model:ir.model,name:pos_multiple_control.model_cash_box_out +msgid "Cash Box Out" +msgstr "" + +#. module: pos_multiple_control +#: model:ir.model.fields,field_description:pos_multiple_control.field_wizard_pos_update_bank_statement_balance_line__balance_end +msgid "Computed Balance" +msgstr "Solde calculé" + +#. module: pos_multiple_control +#: model:ir.model.fields,field_description:pos_multiple_control.field_account_bank_statement__control_difference +msgid "Control Difference" +msgstr "Différence" + +#. module: pos_multiple_control +#: model:ir.model.fields,field_description:pos_multiple_control.field_pos_session__control_statement_ids +msgid "Control statements" +msgstr "Déclarations de contrôle" + +#. module: pos_multiple_control +#: model:ir.model.fields,field_description:pos_multiple_control.field_account_bank_statement__control_balance +msgid "Controled Balance" +msgstr "Solde contrôlé" + +#. module: pos_multiple_control +#: model:ir.model.fields,field_description:pos_multiple_control.field_wizard_pos_update_bank_statement_balance__create_uid +#: model:ir.model.fields,field_description:pos_multiple_control.field_wizard_pos_update_bank_statement_balance_line__create_uid +msgid "Created by" +msgstr "Créé par" + +#. module: pos_multiple_control +#: model:ir.model.fields,field_description:pos_multiple_control.field_wizard_pos_update_bank_statement_balance__create_date +#: model:ir.model.fields,field_description:pos_multiple_control.field_wizard_pos_update_bank_statement_balance_line__create_date +msgid "Created on" +msgstr "Créé le" + +#. module: pos_multiple_control +#: model:ir.model.fields,field_description:pos_multiple_control.field_wizard_pos_update_bank_statement_balance_line__currency_id +msgid "Currency" +msgstr "Devise" + +#. module: pos_multiple_control +#: model:ir.model.fields,field_description:pos_multiple_control.field_wizard_pos_update_bank_statement_balance__session_id +msgid "Current Session" +msgstr "Session en cours" + +#. module: pos_multiple_control +#: selection:wizard.pos.update.bank.statement.balance,balance_moment:0 +msgid "Default" +msgstr "Par défaut" + +#. module: pos_multiple_control +#: model:product.template,name:pos_multiple_control.demo_product_cash_box +msgid "Demo Product Cash box product" +msgstr "Produit de démonstration Produit de caisse" + +#. module: pos_multiple_control +#: model:product.template,name:pos_multiple_control.demo_product_not_cash_box +msgid "Demo Product Not Cash box product" +msgstr "Produit de démonstration Pas un produit de caisse" + +#. module: pos_multiple_control +#: model_terms:pos.config,customer_facing_display_html:pos_multiple_control.pos_config_control +msgid "Desk Organizer" +msgstr "" + +#. module: pos_multiple_control +#: model:ir.model.fields,field_description:pos_multiple_control.field_wizard_pos_update_bank_statement_balance__display_name +#: model:ir.model.fields,field_description:pos_multiple_control.field_wizard_pos_update_bank_statement_balance_line__display_name +msgid "Display Name" +msgstr "Nom affiché" + +#. module: pos_multiple_control +#: model:ir.model.fields,field_description:pos_multiple_control.field_account_bank_statement__display_autosolve +msgid "Display autosolve" +msgstr "Afficher résolution automatique" + +#. module: pos_multiple_control +#: model_terms:ir.ui.view,arch_db:pos_multiple_control.view_pos_session_form +msgid "Do you really want to solve this difference automatically ?" +msgstr "Voulez-vous vraiment solder cette différence automatiquement ?" + +#. module: pos_multiple_control +#: selection:wizard.pos.update.bank.statement.balance,balance_moment:0 +msgid "Ending balance" +msgstr "Solde de fermeture" + +#. module: pos_multiple_control +#: model_terms:ir.ui.view,arch_db:pos_multiple_control.view_wizard_pos_update_bank_statement_balance_form +msgid "Here you can change the" +msgstr "Ici vous pouvez changer le" + +#. module: pos_multiple_control +#: model:ir.model.fields,field_description:pos_multiple_control.field_wizard_pos_update_bank_statement_balance__id +#: model:ir.model.fields,field_description:pos_multiple_control.field_wizard_pos_update_bank_statement_balance_line__id +msgid "ID" +msgstr "" + +#. module: pos_multiple_control +#: model:ir.model.fields,help:pos_multiple_control.field_account_journal__pos_control +msgid "If you want this journal to be controled at closing of point of sale, check this option" +msgstr "Si vous souhaitez que ce journal soit contrôlé à la fermeture du point de vente, cochez cette option" + +#. module: pos_multiple_control +#: model:ir.model.fields,field_description:pos_multiple_control.field_wizard_pos_update_bank_statement_balance__item_ids +msgid "Items" +msgstr "Articles" + +#. module: pos_multiple_control +#: model:ir.model,name:pos_multiple_control.model_account_journal +#: model:ir.model.fields,field_description:pos_multiple_control.field_wizard_pos_update_bank_statement_balance__journal_id +#: model:ir.model.fields,field_description:pos_multiple_control.field_wizard_pos_update_bank_statement_balance_line__journal_id +msgid "Journal" +msgstr "" + +#. module: pos_multiple_control +#: model:ir.model.fields,field_description:pos_multiple_control.field_wizard_pos_update_bank_statement_balance____last_update +#: model:ir.model.fields,field_description:pos_multiple_control.field_wizard_pos_update_bank_statement_balance_line____last_update +msgid "Last Modified on" +msgstr "Dernière modification le" + +#. module: pos_multiple_control +#: model:ir.model.fields,field_description:pos_multiple_control.field_wizard_pos_update_bank_statement_balance__write_uid +#: model:ir.model.fields,field_description:pos_multiple_control.field_wizard_pos_update_bank_statement_balance_line__write_uid +msgid "Last Updated by" +msgstr "Dernière mise à jour par" + +#. module: pos_multiple_control +#: model:ir.model.fields,field_description:pos_multiple_control.field_wizard_pos_update_bank_statement_balance__write_date +#: model:ir.model.fields,field_description:pos_multiple_control.field_wizard_pos_update_bank_statement_balance_line__write_date +msgid "Last Updated on" +msgstr "Dernière mise à jour le" + +#. module: pos_multiple_control +#: model_terms:pos.config,customer_facing_display_html:pos_multiple_control.pos_config_control +msgid "Led Lamp" +msgstr "Lampe Led" + +#. module: pos_multiple_control +#: model_terms:ir.ui.view,arch_db:pos_multiple_control.view_pos_config_form +msgid "Limit to display autosolve button" +msgstr "Limite d'affichage du bouton de résolution automatique" + +#. module: pos_multiple_control +#: model_terms:pos.config,customer_facing_display_html:pos_multiple_control.pos_config_control +msgid "Monitor Stand" +msgstr "" + +#. module: pos_multiple_control +#: model_terms:ir.ui.view,arch_db:pos_multiple_control.view_pos_config_kanban +#: model_terms:ir.ui.view,arch_db:pos_multiple_control.view_pos_session_form +msgid "New Session" +msgstr "Nouvelle session" + +#. module: pos_multiple_control +#: model_terms:pos.config,customer_facing_display_html:pos_multiple_control.pos_config_control +msgid "Odoo Logo" +msgstr "Logo Odoo" + +#. module: pos_multiple_control +#: model:ir.model.fields,field_description:pos_multiple_control.field_pos_session__control_register_balance_start +msgid "Opening Balances" +msgstr "Solde d'ouverture" + +#. module: pos_multiple_control +#: code:addons/pos_multiple_control/wizard/wizard_pos_update_statement_balance.py:85 +#, python-format +msgid "Out" +msgstr "Sortie" + +#. module: pos_multiple_control +#: model:ir.model.fields,field_description:pos_multiple_control.field_account_journal__pos_control +msgid "POS Journal Control" +msgstr "Journal de contrôle du point de vente" + +#. module: pos_multiple_control +#: model:ir.model,name:pos_multiple_control.model_wizard_pos_update_bank_statement_balance +msgid "POS Update Bank Statement Balance" +msgstr "Mise à jour du solde de relevé de banque" + +#. module: pos_multiple_control +#: model:ir.model,name:pos_multiple_control.model_wizard_pos_update_bank_statement_balance_line +msgid "POS Update Bank Statement Balance Line" +msgstr "Mise à jour des lignes de solde de relevé de banque" + +#. module: pos_multiple_control +#: model:ir.model,name:pos_multiple_control.model_pos_config +msgid "Point of Sale Configuration" +msgstr "Paramétrage du point de vente" + +#. module: pos_multiple_control +#: model:ir.model,name:pos_multiple_control.model_pos_session +msgid "Point of Sale Session" +msgstr "Session du point de vente" + +#. module: pos_multiple_control +#: model:ir.model.fields,field_description:pos_multiple_control.field_account_bank_statement__is_pos_control +msgid "Pos control Bank statement" +msgstr "Contrôle des point de vente Relevé bancaire" + +#. module: pos_multiple_control +#: model:ir.model.fields,field_description:pos_multiple_control.field_account_bank_statement__pos_session_state +msgid "Pos session state" +msgstr "État de la session de vente" + +#. module: pos_multiple_control +#: model_terms:pos.config,customer_facing_display_html:pos_multiple_control.pos_config_control +msgid "Price" +msgstr "Prix" + +#. module: pos_multiple_control +#: model_terms:pos.config,customer_facing_display_html:pos_multiple_control.pos_config_control +msgid "Quantity" +msgstr "Quantité" + +#. module: pos_multiple_control +#: model_terms:ir.ui.view,arch_db:pos_multiple_control.view_pos_session_form +msgid "Real Closing Balance" +msgstr "Solde de clôture réel" + +#. module: pos_multiple_control +#: model:ir.model.fields,field_description:pos_multiple_control.field_pos_session__control_register_balance +msgid "Real Closing Balances" +msgstr "Réel solde de clôture" + +#. module: pos_multiple_control +#: model:ir.model.fields,field_description:pos_multiple_control.field_cash_box_out__ref +#: model:ir.model.fields,field_description:pos_multiple_control.field_wizard_pos_update_bank_statement_balance_line__name +msgid "Reference" +msgstr "Référence" + +#. module: pos_multiple_control +#: model_terms:ir.ui.view,arch_db:pos_multiple_control.view_pos_session_form +msgid "Set your ending balance" +msgstr "Définissez votre solde de clôture" + +#. module: pos_multiple_control +#: model_terms:ir.ui.view,arch_db:pos_multiple_control.view_pos_session_form +msgid "Set your starting balance" +msgstr "Définissez votre solde d'ouverture" + +#. module: pos_multiple_control +#: model_terms:ir.ui.view,arch_db:pos_multiple_control.view_pos_session_form +msgid "Solve difference automatically with a product choosen in pos config" +msgstr "Résoudre la différence automatiquement avec un produit choisi dans la configuration du point de vente" + +#. module: pos_multiple_control +#: model:ir.model.fields,field_description:pos_multiple_control.field_wizard_pos_update_bank_statement_balance_line__balance_start +msgid "Starting Balance" +msgstr "Solde initial" + +#. module: pos_multiple_control +#: selection:wizard.pos.update.bank.statement.balance,balance_moment:0 +msgid "Starting balance" +msgstr "Solde initial" + +#. module: pos_multiple_control +#: model:ir.model.fields,field_description:pos_multiple_control.field_wizard_pos_update_bank_statement_balance_line__statement_id +msgid "Statement" +msgstr "Relevé" + +#. module: pos_multiple_control +#: model_terms:ir.ui.view,arch_db:pos_multiple_control.view_wizard_pos_update_bank_statement_balance_form +msgid "Statements" +msgstr "Relevés" + +#. module: pos_multiple_control +#: model:ir.model.fields,field_description:pos_multiple_control.field_pos_session__summary_statement_ids +msgid "Summary statements" +msgstr "Résumé de relevés" + +#. module: pos_multiple_control +#: model:ir.model.fields,field_description:pos_multiple_control.field_pos_session__control_register_balance_end +#: model_terms:ir.ui.view,arch_db:pos_multiple_control.view_pos_session_form +msgid "Theoretical Closing Balances" +msgstr "Solde de fermeture théorique" + +#. module: pos_multiple_control +#: model_terms:ir.ui.view,arch_db:pos_multiple_control.view_pos_session_form +msgid "Total" +msgstr "" + +#. module: pos_multiple_control +#: model:ir.model.fields,field_description:pos_multiple_control.field_pos_session__control_register_difference +msgid "Total difference" +msgstr "Différence total" + +#. module: pos_multiple_control +#: model:ir.model.fields,help:pos_multiple_control.field_wizard_pos_update_bank_statement_balance_line__total_entry_encoding +msgid "Total of transaction lines." +msgstr "Total des lignes de transaction. " + +#. module: pos_multiple_control +#: model:ir.model.fields,field_description:pos_multiple_control.field_pos_session__control_register_total_entry_encoding +#: model_terms:ir.ui.view,arch_db:pos_multiple_control.view_pos_session_form +msgid "Transactions" +msgstr "" + +#. module: pos_multiple_control +#: model:ir.model.fields,field_description:pos_multiple_control.field_wizard_pos_update_bank_statement_balance_line__total_entry_encoding +msgid "Transactions Subtotal" +msgstr "Sous-total des transactions" + +#. module: pos_multiple_control +#: model:product.template,uom_name:pos_multiple_control.demo_product_cash_box +#: model:product.template,uom_name:pos_multiple_control.demo_product_not_cash_box +msgid "Unit(s)" +msgstr "Unité(s)" + +#. module: pos_multiple_control +#: model_terms:ir.ui.view,arch_db:pos_multiple_control.view_wizard_pos_update_bank_statement_balance_form +msgid "Update Balance" +msgstr "Mettre à jour le solde" + +#. module: pos_multiple_control +#: model:ir.actions.act_window,name:pos_multiple_control.action_wizard_pos_update_bank_statement_balance +msgid "Update Balances" +msgstr "Mettre à jour les soldes" + +#. module: pos_multiple_control +#: code:addons/pos_multiple_control/models/account_bank_statement.py:123 +#, python-format +msgid "We can't autosolve this difference. \n" +"You need to configure the Point Of Sale config and choose a pos move reason for autosolving this difference." +msgstr "On ne peut pas résoudre automatiquement cette différence. \n" +"Vous devez paramétrer la configuration de caisse et choisir un article spécial pour résoudre automatiquement cette différence." + +#. module: pos_multiple_control +#: model_terms:pos.config,customer_facing_display_html:pos_multiple_control.pos_config_control +msgid "Whiteboard Pen" +msgstr "Marqueur pour Tableau Blanc" + +#. module: pos_multiple_control +#: model:ir.model.fields,field_description:pos_multiple_control.field_wizard_pos_update_bank_statement_balance_line__wiz_id +msgid "Wiz" +msgstr "" + +#. module: pos_multiple_control +#: code:addons/pos_multiple_control/models/pos_session.py:125 +#, python-format +msgid "You can not close this session because the journal %s (from %s) has a not null difference: %s%s \n" +" You have to change his starting or ending balance" +msgstr "Vous ne pouvez pas fermer cette session car le relevé %s (de %s) a une différence non nulle : %s%s \n" +"Vous devez modifier son solde de départ ou de fin." + +#. module: pos_multiple_control +#: code:addons/pos_multiple_control/models/pos_session.py:110 +#, python-format +msgid "You can not end this session because there are some draft orders: \n" +"\n" +"- %s" +msgstr "Vous ne pouvez pas fermer cette session car le relevé %s a une différence non nulle : \n" +"\n" +"- %s" + +#. module: pos_multiple_control +#: code:addons/pos_multiple_control/models/pos_box.py:55 +#, python-format +msgid "You cannot put/take money in/out for the statement %s which is closed" +msgstr "Vous ne pouvez pas prendre ou retirer de l'argent sur le relevé %s car celui ci est fermé" + +#. module: pos_multiple_control +#: code:addons/pos_multiple_control/wizard/wizard_pos_update_statement_balance.py:67 +#, python-format +msgid "You cannot start the closing balance for multiple POS sessions" +msgstr "Vous ne pouvez changer en même temps le solde de plusieurs sessions de vente" + +#. module: pos_multiple_control +#: model:product.template,weight_uom_name:pos_multiple_control.demo_product_cash_box +#: model:product.template,weight_uom_name:pos_multiple_control.demo_product_not_cash_box +msgid "kg" +msgstr "" diff --git a/pos_multiple_control/models/__init__.py b/pos_multiple_control/models/__init__.py new file mode 100644 index 00000000..9e8ac940 --- /dev/null +++ b/pos_multiple_control/models/__init__.py @@ -0,0 +1,4 @@ +from . import account_bank_statement +from . import account_journal +from . import pos_session +from . import pos_config diff --git a/pos_multiple_control/models/account_bank_statement.py b/pos_multiple_control/models/account_bank_statement.py new file mode 100644 index 00000000..d5e9da96 --- /dev/null +++ b/pos_multiple_control/models/account_bank_statement.py @@ -0,0 +1,144 @@ +# Copyright (C) 2017 - Today: GRAP (http://www.grap.coop) +# @author: Sylvain LE GAL (https://twitter.com/legalsylvain) +# @author Quentin DUPONT (quentin.dupont@grap.coop) +# 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 +import odoo.addons.decimal_precision as dp + + +class AccountBankStatement(models.Model): + _inherit = "account.bank.statement" + + # Columns Section + control_balance = fields.Float( + string="Controled Balance", + compute="_compute_control_balance", + digits=dp.get_precision('Account'), + ) + + control_difference = fields.Float( + string="Control Difference", + compute="_compute_control_difference", + digits=dp.get_precision('Account'), + ) + + is_pos_control = fields.Boolean( + string="Pos control Bank statement", + compute="_compute_is_pos_control", store=True, + ) + + pos_session_state = fields.Char( + string="Pos session state", compute="_compute_pos_session_state" + ) + + display_autosolve = fields.Boolean( + string="Display autosolve", compute="_compute_display_autosolve" + ) + + # Compute Section + @api.multi + @api.depends("line_ids") + def _compute_control_balance(self): + for statement in self: + statement.control_balance = sum( + statement.mapped("balance_end_real") + ) + + @api.multi + @api.depends("control_balance", "total_entry_encoding", "balance_end_real") + def _compute_control_difference(self): + for statement in self: + statement.control_difference = ( + + statement.balance_end_real + - statement.balance_start + - statement.total_entry_encoding + ) + + @api.multi + @api.depends("journal_id.pos_control", "pos_session_state", + "balance_start") + def _compute_is_pos_control(self): + for statement in self: + journal = statement.journal_id + statement.is_pos_control = journal.pos_control + + @api.multi + @api.depends("pos_session_id.state") + def _compute_pos_session_state(self): + for statement in self: + statement.pos_session_state = statement.pos_session_id.state + + # Display button autosolve with some conditions + @api.multi + def _compute_display_autosolve(self): + for statement in self: + if not statement.journal_id.pos_control: + statement.display_autosolve = False + else: + if statement.pos_session_id.config_id.autosolve_limit: + difference_with_limit = ( + abs(statement.control_difference) + - statement.pos_session_id.config_id.autosolve_limit + ) + else: + difference_with_limit = -1 + statement.display_autosolve = ( + statement.pos_session_state not in + ["closed"] + and difference_with_limit < 0 + and abs(round(statement.control_difference, 3)) != 0 + ) + + @api.multi + @api.depends("pos_session_state") + def automatic_solve(self): + self.WizardReason = self.env['wizard.pos.move.reason'] + for statement in self: + pos_move_reason = statement.\ + pos_session_id.config_id.autosolve_pos_move_reason + if pos_move_reason: + cb_pos_move_reason_id = pos_move_reason.id + cb_difference = statement.control_difference + cb_journal_id = statement.journal_id.id + if cb_difference < 0: + default_move_type = "expense" + else: + default_move_type = "income" + + wizard = self.WizardReason.with_context( + active_id=statement.pos_session_id.id, + default_move_type=default_move_type).create({ + 'move_reason_id': cb_pos_move_reason_id, + 'journal_id': cb_journal_id, + 'statement_id': statement.id, + 'amount': abs(cb_difference), + 'name': 'Automatic solve', + }) + wizard.apply() + + else: + raise UserError( + _( + "We can't autosolve this difference. \nYou need to " + "configure the Point Of Sale config and choose a " + "pos move reason for autosolving this difference." + ) + ) + + def open_cashbox_starting_balance(self): + return self.open_cashbox_balance('starting') + + def open_cashbox_ending_balance(self): + return self.open_cashbox_balance('ending') + + def open_cashbox_balance(self, balance_moment): + action = self.env.ref( + "pos_multiple_control." + "action_wizard_pos_update_bank_statement_balance").read()[0] + action['context'] = {'balance_moment': balance_moment, + 'active_id': [self.id], + 'active_pos_id': [self.pos_session_id.id], + 'active_model': 'pos.session'} + return action diff --git a/pos_multiple_control/models/account_journal.py b/pos_multiple_control/models/account_journal.py new file mode 100644 index 00000000..a4a37e5b --- /dev/null +++ b/pos_multiple_control/models/account_journal.py @@ -0,0 +1,24 @@ +# Copyright (C) 2017 - 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 AccountJournal(models.Model): + _inherit = "account.journal" + + pos_control = fields.Boolean( + string="POS Journal Control", + help="If you want this journal" + " to be controled at closing of point of sale, check this option", + default=False + ) + + @api.onchange("type") + def onchange_type(self): + for journal in self: + if journal.type in ["bank", "cash"]: + journal.pos_control = True + else: + journal.pos_control = False diff --git a/pos_multiple_control/models/pos_config.py b/pos_multiple_control/models/pos_config.py new file mode 100644 index 00000000..429b1f7e --- /dev/null +++ b/pos_multiple_control/models/pos_config.py @@ -0,0 +1,49 @@ +# Copyright (C) 2017 - Today: GRAP (http://www.grap.coop) +# @author Quentin DUPONT (quentin.dupont@grap.coop) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + + +from odoo import api, fields, models + + +class PosConfig(models.Model): + _inherit = "pos.config" + + # Columns Section + autosolve_pos_move_reason = fields.Many2one( + string="Autosolve pos move reason", + description="Product used to autosolve control difference", + comodel_name="pos.move.reason", + domain="['|', \ + ('is_income_reason', '=', True), ('is_expense_reason', '=', True)]", + default="", + ) + + autosolve_limit = fields.Float( + string="Autosolve limit", + description="Limit for autosolving bank statement", default=20 + ) + + @api.multi + def open_new_session(self, openui): + self.ensure_one() + # Check if some opening / opened session exists + session_obj = self.env['pos.session'] + sessions = session_obj.search([ + ('user_id', '=', self.env.uid), + ('config_id', '=', self.id), + ('state', 'in', ['opened', 'opening_control']), + ], limit=1) + if sessions: + # An opening / opened session exists + session = sessions[0] + else: + # Create a session + session = session_obj.create({ + 'user_id': self.env.uid, + 'config_id': self.id, + }) + + if session.state == 'opening_control' or openui is False: + return self._open_session(session.id) + return self.open_ui() diff --git a/pos_multiple_control/models/pos_session.py b/pos_multiple_control/models/pos_session.py new file mode 100644 index 00000000..d73dd6ed --- /dev/null +++ b/pos_multiple_control/models/pos_session.py @@ -0,0 +1,168 @@ +# Copyright (C) 2017 - 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 +from odoo.exceptions import Warning as UserError + + +class PosSession(models.Model): + _inherit = "pos.session" + + # Columns Section + statement_ids = fields.One2many(readonly=False) + + control_statement_ids = fields.One2many( + string="Control statements", + comodel_name="account.bank.statement", + related="statement_ids", + ) + + summary_statement_ids = fields.One2many( + string="Summary statements", + comodel_name="account.bank.statement", + related="statement_ids", + ) + + control_register_balance_start = fields.Float( + compute='_compute_control_register_balance_start', + string='Opening Balances') + + control_register_total_entry_encoding = fields.Float( + compute='_compute_control_register_total_entry_encoding', + string='Transactions') + + control_register_balance_end = fields.Float( + compute='_compute_control_register_balance_end', + string='Theoretical Closing Balances') + + control_register_balance = fields.Float( + compute='_compute_control_register_balance', + string='Real Closing Balances') + + control_register_difference = fields.Float( + compute='_compute_control_register_difference', + string='Total difference') + + # Compute Section + @api.multi + @api.depends('statement_ids.is_pos_control', 'statement_ids.balance_start') + def _compute_control_register_balance_start(self): + for session in self: + session.control_register_balance_start = sum( + session.statement_ids.filtered( + lambda x: x.is_pos_control).mapped('balance_start')) + + @api.multi + @api.depends( + 'statement_ids.is_pos_control', 'statement_ids.total_entry_encoding') + def _compute_control_register_total_entry_encoding(self): + for session in self: + session.control_register_total_entry_encoding = sum( + session.statement_ids.filtered( + lambda x: x.is_pos_control).mapped('total_entry_encoding')) + + @api.multi + @api.depends( + 'statement_ids.is_pos_control', 'statement_ids.balance_end_real') + def _compute_control_register_balance_end(self): + for session in self: + session.control_register_balance_end = sum( + session.statement_ids.filtered( + lambda x: x.is_pos_control).mapped('balance_end')) + + @api.multi + @api.depends( + 'statement_ids.is_pos_control', 'statement_ids.control_balance') + def _compute_control_register_balance(self): + for session in self: + session.control_register_balance = sum( + session.statement_ids.filtered( + lambda x: x.is_pos_control).mapped('control_balance')) + + @api.multi + @api.depends( + 'statement_ids.is_pos_control', 'statement_ids.control_difference') + def _compute_control_register_difference(self): + for session in self: + session.control_register_difference = sum( + session.statement_ids.filtered( + lambda x: x.is_pos_control).mapped('control_difference')) + + # Model + @api.multi + def open_cashbox_opening(self): + return super(PosSession, self).open_cashbox() + + @api.multi + def action_pos_session_new_session(self): + return self.config_id.open_new_session(False) + + @api.multi + def wkf_action_closing_control(self): + for session in self: + draft_orders = session.order_ids.filtered( + lambda x: x.state == "draft" + ) + if len(draft_orders): + raise UserError( + _( + "You can not end this session because there are some" + " draft orders: \n\n- %s" + ) + % ("\n- ".join([x.name for x in draft_orders])) + ) + return super(PosSession, self).wkf_action_closing_control() + + @api.multi + def action_pos_session_validate(self): + for session in self: + for statement in session.statement_ids: + if statement.journal_id.pos_control is True: + if abs(statement.control_difference) > 0.001: + raise UserError( + _( + "You can not close this session because the " + "journal %s (from %s) has a not null " + "difference: %s%s \n You have to change his " + "starting or ending balance" + ) + % (statement.journal_id.name, statement.name, + str(round(statement.control_difference, 3)), + statement.currency_id.symbol) + ) + return super(PosSession, self).action_pos_session_validate() + + # Constraints + @api.multi + @api.constrains('user_id', 'state') + def _check_unicity(self): + for session in self: + domain = [ + ("state", "in", ["opening_control", "opened"]), + ("user_id", "=", session.user_id.id), + ] + if self.search_count(domain) > 1: + raise ValidationError( + _( + "You cannot create two active sessions " + "with the same responsible!" + ) + ) + + @api.multi + @api.constrains('config_id', 'state') + def _check_pos_config(self): + for session in self: + domain = [ + ("state", "in", ["opening_control", "opened"]), + ("config_id", "=", session.config_id.id), + ] + if self.search_count(domain) > 1: + raise ValidationError( + _( + "You cannot create two active sessions related" + " to the same point of sale!" + ) + ) diff --git a/pos_multiple_control/readme/CONFIGURE.rst b/pos_multiple_control/readme/CONFIGURE.rst new file mode 100644 index 00000000..01fbc8e0 --- /dev/null +++ b/pos_multiple_control/readme/CONFIGURE.rst @@ -0,0 +1,25 @@ +To configure this module, you need to: + +**Configure the journals** you want to control in your point of sale (POS) + +* Go to Invoicing / Configuration / Journals / Journals + +* Check the box 'POS Journal Control' if you want to enable this feature for this journal + +* Unchecked journal will be in grey + +.. figure:: ../static/description/account_journal_config.png + +**Configure your POS configuration** + +* Go to Point of Sale / Configuration / Point of Sale / + +* Choose your Payments methods (1) + +* Check Cash Control (2) + +* (optional) Choose a "pos move reason" to ausolve difference (3) (see pos_move_reason module for more details) + +* (optional) Choose a limit to allow or not the user to autosolve control difference in pos - Set 0 if you don't want any limit. (4) + +.. figure:: ../static/description/pos_session_config.png diff --git a/pos_multiple_control/readme/CONTRIBUTORS.rst b/pos_multiple_control/readme/CONTRIBUTORS.rst new file mode 100644 index 00000000..4b1742c4 --- /dev/null +++ b/pos_multiple_control/readme/CONTRIBUTORS.rst @@ -0,0 +1,3 @@ +* Sylvain LE GAL +* Julien WESTE +* Quentin DUPONT diff --git a/pos_multiple_control/readme/DESCRIPTION.rst b/pos_multiple_control/readme/DESCRIPTION.rst new file mode 100644 index 00000000..bb6b494f --- /dev/null +++ b/pos_multiple_control/readme/DESCRIPTION.rst @@ -0,0 +1,27 @@ +This module extends the functionality of the point of sale by allowing a +better control at the closing of the session. + +* Show differences for all statements chosen (see config part) + +* Allow user to control each statement. (not only the cash statement, by + default) and change his starting and ending balance + +.. figure:: ../static/description/change_starting_balance.gif + +* Quickly solve differences (we can set an absolute limit), + thanks to pos_move_reason + +See this gif where where we set ending balance with difference, solve it and +close session : + +.. figure:: ../static/description/end_session_balance_automatic_solve.gif + +As the verification is more complete, allow the user to reopen a new session, +if the first one is in a closed state. + +.. figure:: ../static/description/open_new_session.png + +Extra checks are done, to prevent user errors: + +* It is not possible to click on the button 'Close Session' if there are some + draft orders. diff --git a/pos_multiple_control/static/description/account_journal_config.png b/pos_multiple_control/static/description/account_journal_config.png new file mode 100644 index 00000000..ca7bae99 Binary files /dev/null and b/pos_multiple_control/static/description/account_journal_config.png differ diff --git a/pos_multiple_control/static/description/change_starting_balance.gif b/pos_multiple_control/static/description/change_starting_balance.gif new file mode 100644 index 00000000..cc9bcf92 Binary files /dev/null and b/pos_multiple_control/static/description/change_starting_balance.gif differ diff --git a/pos_multiple_control/static/description/end_session_balance_automatic_solve.gif b/pos_multiple_control/static/description/end_session_balance_automatic_solve.gif new file mode 100644 index 00000000..7162513d Binary files /dev/null and b/pos_multiple_control/static/description/end_session_balance_automatic_solve.gif differ diff --git a/pos_multiple_control/static/description/icon.png b/pos_multiple_control/static/description/icon.png new file mode 100644 index 00000000..c5e0266e Binary files /dev/null and b/pos_multiple_control/static/description/icon.png differ diff --git a/pos_multiple_control/static/description/index.html b/pos_multiple_control/static/description/index.html new file mode 100644 index 00000000..471d0110 --- /dev/null +++ b/pos_multiple_control/static/description/index.html @@ -0,0 +1,469 @@ + + + + + + +Point Of Sale - Multiple Cash Control + + + +
+

Point Of Sale - Multiple Cash Control

+ + +

Beta License: AGPL-3 quentinDupont/grap-odoo-incubator

+

This module extends the functionality of the point of sale by allowing a +better control at the closing of the session.

+
    +
  • Show differences for all statements chosen (see config part)
  • +
  • Allow user to control each statement. (not only the cash statement, by +default) and change his starting and ending balance
  • +
+
+https://raw.githubusercontent.com/quentinDupont/grap-odoo-incubator/12.0_pos_multiple_control/pos_multiple_control/static/description/change_starting_balance.gif +
+
    +
  • Quickly solve differences (we can set an absolute limit), +thanks to pos_move_reason
  • +
+

See this gif where where we set ending balance with difference, solve it and +close session :

+
+https://raw.githubusercontent.com/quentinDupont/grap-odoo-incubator/12.0_pos_multiple_control/pos_multiple_control/static/description/end_session_balance_automatic_solve.gif +
+

As the verification is more complete, allow the user to reopen a new session, +if the first one is in a closed state.

+
+https://raw.githubusercontent.com/quentinDupont/grap-odoo-incubator/12.0_pos_multiple_control/pos_multiple_control/static/description/open_new_session.png +
+

Extra checks are done, to prevent user errors:

+
    +
  • It is not possible to click on the button ‘Close Session’ if there are some +draft orders.
  • +
+

Table of contents

+ +
+

Configuration

+

To configure this module, you need to:

+

Configure the journals you want to control in your point of sale (POS)

+
    +
  • Go to Invoicing / Configuration / Journals / Journals
  • +
  • Check the box ‘POS Journal Control’ if you want to enable this feature for this journal
  • +
  • Unchecked journal will be in grey
  • +
+
+https://raw.githubusercontent.com/quentinDupont/grap-odoo-incubator/12.0_pos_multiple_control/pos_multiple_control/static/description/account_journal_config.png +
+

Configure your POS configuration

+
    +
  • Go to Point of Sale / Configuration / Point of Sale / <your pos>
  • +
  • Choose your Payments methods (1)
  • +
  • Check Cash Control (2)
  • +
  • (optional) Choose a “pos move reason” to ausolve difference (3) (see pos_move_reason module for more details)
  • +
  • (optional) Choose a limit to allow or not the user to autosolve control difference in pos - Set 0 if you don’t want any limit. (4)
  • +
+
+https://raw.githubusercontent.com/quentinDupont/grap-odoo-incubator/12.0_pos_multiple_control/pos_multiple_control/static/description/pos_session_config.png +
+
+
+

Bug Tracker

+

Bugs are tracked on GitHub 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.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • GRAP
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is part of the quentinDupont/grap-odoo-incubator project on GitHub.

+

You are welcome to contribute.

+
+
+
+ + diff --git a/pos_multiple_control/static/description/open_new_session.png b/pos_multiple_control/static/description/open_new_session.png new file mode 100644 index 00000000..317fe7f5 Binary files /dev/null and b/pos_multiple_control/static/description/open_new_session.png differ diff --git a/pos_multiple_control/static/description/pos_session.png b/pos_multiple_control/static/description/pos_session.png new file mode 100644 index 00000000..69f67988 Binary files /dev/null and b/pos_multiple_control/static/description/pos_session.png differ diff --git a/pos_multiple_control/static/description/pos_session_config.png b/pos_multiple_control/static/description/pos_session_config.png new file mode 100644 index 00000000..ea4c3494 Binary files /dev/null and b/pos_multiple_control/static/description/pos_session_config.png differ diff --git a/pos_multiple_control/tests/__init__.py b/pos_multiple_control/tests/__init__.py new file mode 100644 index 00000000..c1eb9f55 --- /dev/null +++ b/pos_multiple_control/tests/__init__.py @@ -0,0 +1 @@ +from . import test_pos_multiple_control diff --git a/pos_multiple_control/tests/test_pos_multiple_control.py b/pos_multiple_control/tests/test_pos_multiple_control.py new file mode 100644 index 00000000..bb97ea71 --- /dev/null +++ b/pos_multiple_control/tests/test_pos_multiple_control.py @@ -0,0 +1,170 @@ +# 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.tests.common import TransactionCase +from odoo.exceptions import ValidationError +from odoo.exceptions import Warning as UserError + + +class TestMultipleControl(TransactionCase): + """Tests for 'Point of Sale - Multiple Control' Module""" + + def setUp(self): + super(TestMultipleControl, self).setUp() + self.session_obj = self.env["pos.session"] + self.order_obj = self.env["pos.order"] + self.payment_obj = self.env["pos.make.payment"] + self.partner = self.env.ref("base.partner_demo_portal") + self.product = self.env.ref("product.product_product_3") + self.pos_move_reason = self.env.ref( + "pos_multiple_control.cash_register_error" + ) + self.pos_config = self.env.ref( + "pos_multiple_control.pos_config_control" + ) + self.check_journal = self.env.ref("pos_multiple_control.check_journal") + self.cash_journal = self.env.ref("pos_multiple_control.cash_journal") + + def _order(self, session, price, journal): + # I create a new PoS order with 2 lines + order = self.order_obj.create({ + 'session_id': session.id, + 'partner_id': self.partner.id, + 'pricelist_id': self.partner.property_product_pricelist.id, + 'lines': [(0, 0, { + 'name': "OL/0001", + 'product_id': self.product.id, + 'price_unit': price, + 'qty': 1.0, + # 'tax_ids': [(6, 0, self.product.taxes_id.ids)], + 'price_subtotal': price, + 'price_subtotal_incl': price, + })], + 'amount_total': price, + 'amount_tax': 0.0, + 'amount_paid': 0.0, + 'amount_return': 0.0, + }) + return order + + def _order_and_pay(self, session, price, journal): + order = self._order(session, price, journal) + + context_make_payment = { + "active_ids": [order.id], + "active_id": order.id + } + self.pos_make_payment = self.payment_obj.with_context( + context_make_payment).create({'amount': price}) + + # I click on the validate button to register the payment. + context_payment = {'active_id': order.id} + self.pos_make_payment.with_context(context_payment).check() + + return order + + # Test Section + def test_01_two_opening_session(self): + # I create new session + self.session_obj.create({"config_id": self.pos_config.id}) + + # I Try to create a new session + with self.assertRaises(ValidationError): + self.session_obj.create({"config_id": self.pos_config.id}) + + def test_02_opening_and_opened_session(self): + # I create new session and open it + session = self.session_obj.create({"config_id": self.pos_config.id}) + self.pos_config.open_session_cb() + self.pos_config._open_session(session.id) + + # I Try to create a new session + with self.assertRaises(ValidationError): + self.session_obj.create({"config_id": self.pos_config.id}) + + def test_03_check_close_session_with_draft_order(self): + # I create new session and open it + session = self.session_obj.create({"config_id": self.pos_config.id}) + self.pos_config.open_session_cb() + self.pos_config._open_session(session.id) + + # Create a Draft order, and try to close the session + self._order(session, 1, self.check_journal) + + with self.assertRaises(UserError): + session.wkf_action_closing_control() + + def test_04_check_bank_statement_control(self): + # I create new session and open it + session = self.session_obj.create({"config_id": self.pos_config.id}) + + # Make 2 Sales of 1100 and check transactions and theoritical balance + self.pos_config.open_session_cb() + self.pos_config._open_session(session.id) + self._order_and_pay(session, 100, self.check_journal) + self._order_and_pay(session, 1000, self.check_journal) + self.assertEqual( + session.control_register_total_entry_encoding, + 1100, + "Incorrect transactions total", + ) + self.assertEqual( + session.control_register_balance_end, + session.control_register_balance_start + 1100, + "Incorrect theoritical ending balance", + ) + + with self.assertRaises(UserError): + session.action_pos_session_validate() + + def test_05_check_autosolve(self): + # I create new session and open it + self.pos_config.write( + { + "autosolve_pos_move_reason": self.pos_move_reason.id, + "autosolve_limit": 20, + } + ) + session = self.session_obj.create({"config_id": self.pos_config.id}) + + # Make sales and autosolve + self.pos_config.open_session_cb() + self.pos_config._open_session(session.id) + sale = self._order_and_pay(session, 18, self.check_journal) + sale.statement_ids[0].statement_id.automatic_solve() + self.assertEqual( + session.summary_statement_ids[1].control_difference, + 0, + "Incorrect transactions total", + ) + + def test_06_check_display_button(self): + # I create new session and open it + self.pos_config.write( + { + "autosolve_pos_move_reason": self.pos_move_reason.id, + "autosolve_limit": 30, + } + ) + session = self.session_obj.create({"config_id": self.pos_config.id}) + + # Make sales too important + self.pos_config.open_session_cb() + self.pos_config._open_session(session.id) + sale = self._order_and_pay(session, 31, self.check_journal) + self.assertEqual( + sale.statement_ids[0].statement_id.display_autosolve, + False, + "Autosolve button should be hidden", + ) + + # Autosolve and second sales + sale.statement_ids[0].statement_id.automatic_solve() + sale2 = self._order_and_pay(session, 29, self.check_journal) + sale2.statement_ids[0].statement_id._compute_display_autosolve() + self.assertEqual( + sale2.statement_ids[0].statement_id.display_autosolve, + True, + "Autosolve button should not be hidden", + ) diff --git a/pos_multiple_control/views/view_account_journal.xml b/pos_multiple_control/views/view_account_journal.xml new file mode 100644 index 00000000..e6a62fad --- /dev/null +++ b/pos_multiple_control/views/view_account_journal.xml @@ -0,0 +1,31 @@ + + + + + + + account.journal + + + + + + + + + + account.journal + + + + + + + + + + diff --git a/pos_multiple_control/views/view_pos_config.xml b/pos_multiple_control/views/view_pos_config.xml new file mode 100644 index 00000000..91d369b7 --- /dev/null +++ b/pos_multiple_control/views/view_pos_config.xml @@ -0,0 +1,45 @@ + + + + + + + pos.config + + + +
+
+
+
+
+
+
+
+
+
+
+ + + pos.config + + + + + + + + + +
diff --git a/pos_multiple_control/views/view_pos_session.xml b/pos_multiple_control/views/view_pos_session.xml new file mode 100644 index 00000000..dc290467 --- /dev/null +++ b/pos_multiple_control/views/view_pos_session.xml @@ -0,0 +1,135 @@ + + + + + + + pos.session + + + + + 1 + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + +
+
+
+ + + + + + + + + + +
+ +
diff --git a/pos_multiple_control/wizard/__init__.py b/pos_multiple_control/wizard/__init__.py new file mode 100644 index 00000000..9be145ff --- /dev/null +++ b/pos_multiple_control/wizard/__init__.py @@ -0,0 +1 @@ +from . import wizard_pos_update_statement_balance diff --git a/pos_multiple_control/wizard/wizard_pos_update_statement_balance.py b/pos_multiple_control/wizard/wizard_pos_update_statement_balance.py new file mode 100644 index 00000000..83e45668 --- /dev/null +++ b/pos_multiple_control/wizard/wizard_pos_update_statement_balance.py @@ -0,0 +1,153 @@ +# Copyright 2020 ForgeFlow, S.L. +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +from odoo import api, fields, models, _ +from odoo.exceptions import UserError + + +class WizardPOSBankStatementUpdateBalance(models.TransientModel): + _name = "wizard.pos.update.bank.statement.balance" + _description = 'POS Update Bank Statement Balance' + + def _default_session_id(self): + return self.env.context.get('active_pos_id', False) + + _BALANCE_MOMENT_SELECTION = [ + ('bydefault', 'Default'), + ('starting', 'Starting balance'), + ('ending', 'Ending balance'), + ] + + item_ids = fields.One2many( + comodel_name="wizard.pos.update.bank.statement.balance.line", + inverse_name="wiz_id", + string="Items", + ) + + balance_moment = fields.Selection( + selection=_BALANCE_MOMENT_SELECTION, string='Balance moment', + default='bydefault') + + journal_id = fields.Many2one( + comodel_name='account.journal', string="Journal", + domain="[('id', 'in', journal_ids)]", required=True) + + session_id = fields.Many2one( + comodel_name='pos.session', string="Current Session", + default=_default_session_id, required=True, readonly=True) + + @api.model + def _prepare_item(self, statement): + return { + "statement_id": statement.id, + "name": statement.name, + "journal_id": statement.journal_id.id, + "balance_start": statement.balance_start, + "balance_end": statement.balance_end, + "total_entry_encoding": statement.total_entry_encoding, + "currency_id": statement.currency_id.id, + } + + @api.model + def default_get(self, flds): + res = super().default_get(flds) + # Load objects + session_obj = self.env["pos.session"] + bank_statement_obj = self.env["account.bank.statement"] + # Load context + active_ids = self.env.context["active_id"] or [] + active_pos_id = self.env.context["active_pos_id"] or [] + active_model = self.env.context["active_model"] or [] + balance_moment = self.env.context["balance_moment"] or [] + # Check propagation + if not active_pos_id: + return res + assert active_model == "pos.session", \ + "Bad context propagation" + if len(active_pos_id) > 1: + raise UserError(_('You cannot start the closing ' + 'balance for multiple POS sessions')) + # Add bank statement lines + session = session_obj.browse(active_pos_id[0]) + bank_statement = bank_statement_obj.browse(active_ids[0]) + items = [] + items.append([0, 0, self._prepare_item(bank_statement)]) + # Give values for wizard + res["session_id"] = session.id + res["item_ids"] = items + res["balance_moment"] = balance_moment + res["journal_id"] = bank_statement.journal_id.id + return res + + @api.model + def _prepare_cash_box_journal(self, item): + return { + 'amount': abs(item.difference), + 'name': _('Out'), + "journal_id": item.journal_id.id, + } + + @api.multi + def action_confirm(self): + self.ensure_one() + # record new values from wizard + for item in self.item_ids: + if item.balance_moment == 'starting': + item.statement_id.balance_start = item.balance_start_real + elif item.balance_moment == 'ending': + item.statement_id.balance_end_real = item.balance_end_real + # item.statement_id.balance_end = item.balance_end_real + return True + + +class WizardPOSBankStatementUpdateBalanceLine(models.TransientModel): + _name = "wizard.pos.update.bank.statement.balance.line" + _description = 'POS Update Bank Statement Balance Line' + + wiz_id = fields.Many2one( + comodel_name='wizard.pos.update.bank.statement.balance', + required=True, + ) + statement_id = fields.Many2one( + comodel_name='account.bank.statement', + ) + name = fields.Char( + related='statement_id.name' + ) + journal_id = fields.Many2one( + comodel_name='account.journal', + related='statement_id.journal_id', + ) + balance_start = fields.Monetary( + string="Starting Balance", + default=0.0, + compute='_compute_balance_start' + ) + balance_start_real = fields.Monetary( + default=0.0 + ) + total_entry_encoding = fields.Monetary( + related='statement_id.total_entry_encoding', + ) + balance_end = fields.Monetary( + string="Computed Balance", + default=0.0, + compute='_compute_balance_end' + ) + balance_end_real = fields.Monetary( + default=0.0 + ) + currency_id = fields.Many2one( + comodel_name='res.currency', + related='statement_id.currency_id' + ) + balance_moment = fields.Selection( + related='wiz_id.balance_moment', + default='bydefault') + + def _compute_balance_start(self): + for rec in self: + rec.balance_start = rec.statement_id.balance_start + + def _compute_balance_end(self): + for rec in self: + rec.balance_end = rec.statement_id.balance_end diff --git a/pos_multiple_control/wizard/wizard_pos_update_statement_balance.xml b/pos_multiple_control/wizard/wizard_pos_update_statement_balance.xml new file mode 100644 index 00000000..5936aed9 --- /dev/null +++ b/pos_multiple_control/wizard/wizard_pos_update_statement_balance.xml @@ -0,0 +1,38 @@ + + + + + wizard.pos.update.bank.statement.balance + +
+ Here you can change the + + + + + + + + + + + + +
+
+
+
+
+ + + Update Balances + ir.actions.act_window + wizard.pos.update.bank.statement.balance + form + form + new + + +