diff --git a/pos_cash_move_reason/README.rst b/pos_cash_move_reason/README.rst new file mode 100644 index 00000000..15a9d2ac --- /dev/null +++ b/pos_cash_move_reason/README.rst @@ -0,0 +1,62 @@ +.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 + +====================== +POS cash in-out reason +====================== + +This module allow to define some products as reason for the functionality of +"Put Money In" and "Take Money Out" available in point of sale session. +So, with this module it's possible to impact directly an expense or income +account which is defined on the related product or in its category. + +Configuration +============= + +You need to configure some products that can be used on "Put Money In" and +"Take Money Out". You have to set Point of Sale Cash In or Out and income and +expense account. + +Usage +===== + +You can use configured products on "Put Money In" and "Take Money Out" wizard available in point of sale session: + +.. figure:: static/description/pos_cash_move_reason_02.png + :alt: Take money out wizard + + +.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas + :alt: Try me on Runbot + :target: https://runbot.odoo-community.org/runbot/184/8.0 + + +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 +`here `_. + +Credits +======= + +Contributors +------------ + +* Adrien Peiffer + +Maintainer +---------- + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +This module is maintained by the OCA. + +OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use. + +To contribute to this module, please visit https://odoo-community.org. \ No newline at end of file diff --git a/pos_cash_move_reason/__init__.py b/pos_cash_move_reason/__init__.py new file mode 100644 index 00000000..1b98dd85 --- /dev/null +++ b/pos_cash_move_reason/__init__.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- +# Copyright 2016 ACSONE SA/NV () +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from . import wizard diff --git a/pos_cash_move_reason/__openerp__.py b/pos_cash_move_reason/__openerp__.py new file mode 100644 index 00000000..8bc37a59 --- /dev/null +++ b/pos_cash_move_reason/__openerp__.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +# Copyright 2016 ACSONE SA/NV () +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +{ + 'name': "POS cash in-out reason", + 'summary': """""", + 'author': 'ACSONE SA/NV,' + 'Odoo Community Association (OCA)', + 'website': "http://acsone.eu", + 'category': 'POS', + 'version': '8.0.1.0.0', + 'license': 'AGPL-3', + 'depends': [ + 'point_of_sale', + ], + 'data': [ + 'wizard/pos_box.xml', + ], +} diff --git a/pos_cash_move_reason/i18n/fr.po b/pos_cash_move_reason/i18n/fr.po new file mode 100644 index 00000000..35623bdb --- /dev/null +++ b/pos_cash_move_reason/i18n/fr.po @@ -0,0 +1,45 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * pos_cash_move_reason +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 8.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2016-04-12 11:10+0000\n" +"PO-Revision-Date: 2016-04-12 11:10+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_cash_move_reason +#: code:addons/pos_cash_move_reason/wizard/pos_box.py:36 +#, python-format +msgid "Description" +msgstr "Description" + +#. module: pos_cash_move_reason +#: field:cash.box.in,product_id:0 +#: field:cash.box.out,product_id:0 +msgid "Reason" +msgstr "Motif" + +#. module: pos_cash_move_reason +#: code:addons/pos_cash_move_reason/wizard/pos_box.py:84 +#, python-format +msgid "You have to define an\n" +" expense account on the related product" +msgstr "Vous devez définir un\n" +" compte de charge sur le produit lié" + +#. module: pos_cash_move_reason +#: code:addons/pos_cash_move_reason/wizard/pos_box.py:60 +#, python-format +msgid "You have to define an\n" +" income account on the related product" +msgstr "Vous devez définir un\n" +" compte de produit sur le produit lié" + diff --git a/pos_cash_move_reason/i18n/pos_cash_move_reason.pot b/pos_cash_move_reason/i18n/pos_cash_move_reason.pot new file mode 100644 index 00000000..b80f018d --- /dev/null +++ b/pos_cash_move_reason/i18n/pos_cash_move_reason.pot @@ -0,0 +1,43 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * pos_cash_move_reason +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 8.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2016-04-12 11:09+0000\n" +"PO-Revision-Date: 2016-04-12 11:09+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_cash_move_reason +#: code:addons/pos_cash_move_reason/wizard/pos_box.py:36 +#, python-format +msgid "Description" +msgstr "" + +#. module: pos_cash_move_reason +#: field:cash.box.in,product_id:0 +#: field:cash.box.out,product_id:0 +msgid "Reason" +msgstr "" + +#. module: pos_cash_move_reason +#: code:addons/pos_cash_move_reason/wizard/pos_box.py:84 +#, python-format +msgid "You have to define an\n" +" expense account on the related product" +msgstr "" + +#. module: pos_cash_move_reason +#: code:addons/pos_cash_move_reason/wizard/pos_box.py:60 +#, python-format +msgid "You have to define an\n" +" income account on the related product" +msgstr "" + diff --git a/pos_cash_move_reason/static/description/icon.png b/pos_cash_move_reason/static/description/icon.png new file mode 100644 index 00000000..3a0328b5 Binary files /dev/null and b/pos_cash_move_reason/static/description/icon.png differ diff --git a/pos_cash_move_reason/static/description/pos_cash_move_reason_02.png b/pos_cash_move_reason/static/description/pos_cash_move_reason_02.png new file mode 100644 index 00000000..8bd04f2f Binary files /dev/null and b/pos_cash_move_reason/static/description/pos_cash_move_reason_02.png differ diff --git a/pos_cash_move_reason/tests/__init__.py b/pos_cash_move_reason/tests/__init__.py new file mode 100644 index 00000000..003a28f5 --- /dev/null +++ b/pos_cash_move_reason/tests/__init__.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- +# © 2015 ACSONE SA/NV () +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from . import test_pos_cash_move_reason diff --git a/pos_cash_move_reason/tests/test_pos_cash_move_reason.py b/pos_cash_move_reason/tests/test_pos_cash_move_reason.py new file mode 100644 index 00000000..dd56136b --- /dev/null +++ b/pos_cash_move_reason/tests/test_pos_cash_move_reason.py @@ -0,0 +1,106 @@ +# -*- coding: utf-8 -*- +# © 2015 ACSONE SA/NV () +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from openerp.tests import common + + +class TestPosCashMoveReason(common.TransactionCase): + + def setUp(self): + super(TestPosCashMoveReason, self).setUp() + self.pos_session_obj = self.env['pos.session'] + self.aml_obj = self.env['account.move.line'] + self.cash_in_obj = self.env['cash.box.in'] + self.cash_out_obj = self.env['cash.box.out'] + self.cash_move_reason_obj = self.env['product.template'] + self.main_config = self.env.ref('point_of_sale.pos_config_main') + self.cash_journal = self.env.ref('account.cash_journal') + self.income_account = self.env.ref('account.o_income') + self.expense_account = self.env.ref('account.a_expense') + + def test01(self): + # I create one move reason + vals = {'name': 'Miscellaneous income', + 'property_account_income': self.income_account.id, + 'income_pdt': True} + move_reason = self.cash_move_reason_obj.create(vals) + # I set cash control on cash journal + self.cash_journal.cash_control = True + # I create and open a new session + self.session_01 = self.pos_session_obj.create( + {'config_id': self.main_config.id}) + ctx = self.env.context.copy() + # context is updated in open_cb + # -> Need to call with old api to give unfrozen context + self.registry['pos.session'].open_cb( + self.cr, self.uid, [self.session_01.id], context=ctx) + ctx['active_ids'] = self.session_01.id + ctx['active_model'] = self.session_01._name + # I put the session in validation control + self.session_01.signal_workflow('cashbox_control') + ctx['active_ids'] = self.session_01.id + ctx['active_model'] = self.session_01._name + # I create a cash in + cash_in = self.cash_in_obj.with_context(ctx).create( + {'name': 'Initialization', + 'product_id': move_reason.id, + 'amount': 500.0}) + cash_in.with_context(ctx).run() + # I close the session + self.session_01.signal_workflow('close') + # I get the statement from the session + statement = self.env['account.bank.statement'].search( + [('pos_session_id', '=', self.session_01.id), + ('journal_id', '=', self.cash_journal.id)]) + # I get all move lines of this statement + move_line_ids = statement.move_line_ids.ids + move_line = self.env['account.move.line'].search( + [('account_id', '=', self.income_account.id), + ('credit', '=', 500.0), + ('id', 'in', move_line_ids)]) + # I check the created move line from the cash in + self.assertEquals(len(move_line.ids), 1) + + def test02(self): + # I create one move reason + vals = {'name': 'Miscellaneous expense', + 'property_account_expense': self.expense_account.id, + 'expense_pdt': True} + move_reason = self.cash_move_reason_obj.create(vals) + # I set cash control on cash journal + self.cash_journal.cash_control = True + # I create and open a new session + self.session_01 = self.pos_session_obj.create( + {'config_id': self.main_config.id}) + ctx = self.env.context.copy() + # context is updated in open_cb + # -> Need to call with old api to give unfrozen context + self.registry['pos.session'].open_cb( + self.cr, self.uid, [self.session_01.id], context=ctx) + ctx['active_ids'] = self.session_01.id + ctx['active_model'] = self.session_01._name + # I put the session in validation control + self.session_01.signal_workflow('cashbox_control') + ctx['active_ids'] = self.session_01.id + ctx['active_model'] = self.session_01._name + # I create a cash out + cash_out = self.cash_out_obj.with_context(ctx).create( + {'name': 'Miscellaneous expense', + 'product_id': move_reason.id, + 'amount': 500.0}) + cash_out.with_context(ctx).run() + # I close the session + self.session_01.signal_workflow('close') + # I get the statement from the session + statement = self.env['account.bank.statement'].search( + [('pos_session_id', '=', self.session_01.id), + ('journal_id', '=', self.cash_journal.id)]) + # I get all move lines of this statement + move_line_ids = statement.move_line_ids.ids + move_line = self.env['account.move.line'].search( + [('account_id', '=', self.expense_account.id), + ('debit', '=', 500.0), + ('id', 'in', move_line_ids)]) + # I check the created move line from the cash in + self.assertEquals(len(move_line.ids), 1) diff --git a/pos_cash_move_reason/wizard/__init__.py b/pos_cash_move_reason/wizard/__init__.py new file mode 100644 index 00000000..362246b1 --- /dev/null +++ b/pos_cash_move_reason/wizard/__init__.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- +# © 2015 ACSONE SA/NV () +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from . import pos_box diff --git a/pos_cash_move_reason/wizard/pos_box.py b/pos_cash_move_reason/wizard/pos_box.py new file mode 100644 index 00000000..0dde7aaa --- /dev/null +++ b/pos_cash_move_reason/wizard/pos_box.py @@ -0,0 +1,86 @@ +# -*- coding: utf-8 -*- +# © 2015 ACSONE SA/NV () +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from openerp import api, exceptions, fields, _ +from openerp.addons.point_of_sale.wizard.pos_box import PosBox + +from lxml import etree +import simplejson + + +class PosBoxCashMoveReason(PosBox): + _register = False + + @api.onchange('product_id') + def onchange_reason(self): + for record in self: + if record.product_id.id: + record.name = record.product_id.name + + @api.model + def fields_view_get(self, view_id=None, view_type='form', + toolbar=False, submenu=False): + res = super(PosBoxCashMoveReason, self).fields_view_get( + view_id=view_id, view_type=view_type, toolbar=toolbar, + submenu=submenu) + doc = etree.XML(res['arch']) + if self.env.context.get('active_model', '') != 'pos.session': + for node in doc.xpath("//field[@name='product_id']"): + modifiers = {'invisible': True, 'required': False} + node.set('invisible', '1') + node.set('required', '0') + node.set('modifiers', simplejson.dumps(modifiers)) + else: + for node in doc.xpath("//field[@name='name']"): + node.set('string', _('Description')) + res['arch'] = etree.tostring(doc) + return res + + +class PosBoxIn(PosBoxCashMoveReason): + _inherit = 'cash.box.in' + + product_id = fields.Many2one( + comodel_name='product.template', string='Reason', + domain="[('income_pdt', '=', True)]") + + @api.model + def _compute_values_for_statement_line(self, box, record): + values = super(PosBoxIn, self)._compute_values_for_statement_line( + box, record) + if self.env.context.get('active_model', '') == 'pos.session': + if box.product_id.id: + product = box.product_id + account_id = product.property_account_income.id or\ + product.categ_id.property_account_income_categ.id + if account_id: + values['account_id'] = account_id + else: + raise exceptions.Warning(_("""You have to define an + income account on the related product""")) + return values + + +class PosBoxOut(PosBoxCashMoveReason): + _inherit = 'cash.box.out' + + product_id = fields.Many2one( + comodel_name='product.template', string='Reason', + domain="[('expense_pdt', '=', True)]") + + @api.model + def _compute_values_for_statement_line(self, box, record): + values = super(PosBoxOut, self)._compute_values_for_statement_line( + box, record) + if self.env.context.get('active_model', '') == 'pos.session': + if box.product_id.id: + product = box.product_id + account_id = product.property_account_expense.id or\ + product.categ_id.property_account_expense_categ.id + if account_id: + values['account_id'] = account_id + else: + raise exceptions.Warning(_("""You have to define an + expense account on the related product""")) + return values diff --git a/pos_cash_move_reason/wizard/pos_box.xml b/pos_cash_move_reason/wizard/pos_box.xml new file mode 100644 index 00000000..ffcf7d7a --- /dev/null +++ b/pos_cash_move_reason/wizard/pos_box.xml @@ -0,0 +1,26 @@ + + + + + cash.box.in.form + cash.box.in + + + + + + + + + + cash.box.out.form + cash.box.out + + + + + + + + +