From 0a46e1d0242a32192e21286057cdc22216dbf6ed Mon Sep 17 00:00:00 2001 From: Darshan Patel Date: Mon, 18 Jan 2016 14:37:44 +0530 Subject: [PATCH 1/4] Added stock_account OU module Added Test Cases. Improved code for test cases Migrated Valuation method of quants. Migrated Valuation method _account_entry_move of quants. Completed test cases and modified valuation method --- stock_account_operating_unit/README.rst | 75 ++++ stock_account_operating_unit/__init__.py | 6 + stock_account_operating_unit/__openerp__.py | 24 ++ .../model/__init__.py | 6 + stock_account_operating_unit/model/stock.py | 100 ++++++ .../tests/__init__.py | 6 + .../test_stock_account_operating_unit.py | 324 ++++++++++++++++++ 7 files changed, 541 insertions(+) create mode 100644 stock_account_operating_unit/README.rst create mode 100644 stock_account_operating_unit/__init__.py create mode 100644 stock_account_operating_unit/__openerp__.py create mode 100644 stock_account_operating_unit/model/__init__.py create mode 100644 stock_account_operating_unit/model/stock.py create mode 100644 stock_account_operating_unit/tests/__init__.py create mode 100644 stock_account_operating_unit/tests/test_stock_account_operating_unit.py diff --git a/stock_account_operating_unit/README.rst b/stock_account_operating_unit/README.rst new file mode 100644 index 000000000..ec1701d79 --- /dev/null +++ b/stock_account_operating_unit/README.rst @@ -0,0 +1,75 @@ +.. image:: https://img.shields.io/badge/license-LGPLv3-blue.svg + :target: https://www.gnu.org/licenses/lgpl.html + :alt: License: LGPL-3 + +======================================= +Stock account moves with Operating Unit +======================================= + +This module introduces the following features: +- Creates account move lines when stock moves are posted between internal +locations within the same company, but different OU’s. + + +Configuration +============= + +If your company is required to generate a balanced balance sheet by +operating unit you can specify at company level that operating units should +be self-balanced, and then indicate a self-balancing clearing account. + +* Create an account for "Inter-OU Clearing" of type Regular. +* Go to *Settings / Companies / Configuration* and: + ** Set the "Operating Units are self-balanced" checkbox + ** Set the "Inter-OU Clearing" account in "Inter-operating unit clearing + account" field. +* Assign Operating Unit in Accounts. + + +Usage +===== + +Create stock moves between internal locations within the same company, but +different OU’s. The journal entries are created and they are self-balanced +within the OU when the journal entries are posted + +.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas + :alt: Try me on Runbot + :target: https://runbot.odoo-community.org/runbot/213/9.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. + +Credits +======= + +Images +------ + +* Odoo Community Association: `Icon `_. + +Contributors +------------ + +* Eficent Business and IT Consulting Services S.L. +* Serpent Consulting Services Pvt. Ltd. + +Maintainer +---------- + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: http://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 http://odoo-community.org. diff --git a/stock_account_operating_unit/__init__.py b/stock_account_operating_unit/__init__.py new file mode 100644 index 000000000..209c94a68 --- /dev/null +++ b/stock_account_operating_unit/__init__.py @@ -0,0 +1,6 @@ +# -*- coding: utf-8 -*- +# © 2015-17 Eficent Business and IT Consulting Services S.L. +# - Jordi Ballester Alomar +# © 2015-17 Serpent Consulting Services Pvt. Ltd. - Sudhir Arya +# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html). +from . import model diff --git a/stock_account_operating_unit/__openerp__.py b/stock_account_operating_unit/__openerp__.py new file mode 100644 index 000000000..88f3c1418 --- /dev/null +++ b/stock_account_operating_unit/__openerp__.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +# © 2015-17 Eficent Business and IT Consulting Services S.L. +# - Jordi Ballester Alomar +# © 2015-17 Serpent Consulting Services Pvt. Ltd. - Sudhir Arya +# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html). + +{ + "name": "Stock account moves with Operating Unit", + "summary": "Create journal entries in moves between internal locations " + "with different operating units.", + "version": "9.0.1.0.0", + "category": "Generic Modules/Sales & Purchases", + "author": "Eficent Business and IT Consulting Services S.L., " + "Serpent Consulting Services Pvt. Ltd.," + "Odoo Community Association (OCA)", + "license": "LGPL-3", + "website": "http://www.eficent.com", + "depends": [ + 'stock_operating_unit', + 'account_operating_unit', + "stock_account" + ], + "installable": True, +} diff --git a/stock_account_operating_unit/model/__init__.py b/stock_account_operating_unit/model/__init__.py new file mode 100644 index 000000000..5261ffb5a --- /dev/null +++ b/stock_account_operating_unit/model/__init__.py @@ -0,0 +1,6 @@ +# -*- coding: utf-8 -*- +# © 2015-17 Eficent Business and IT Consulting Services S.L. +# - Jordi Ballester Alomar +# © 2015-17 Serpent Consulting Services Pvt. Ltd. - Sudhir Arya +# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html). +from . import stock diff --git a/stock_account_operating_unit/model/stock.py b/stock_account_operating_unit/model/stock.py new file mode 100644 index 000000000..7a4088659 --- /dev/null +++ b/stock_account_operating_unit/model/stock.py @@ -0,0 +1,100 @@ +# -*- coding: utf-8 -*- +# © 2015-17 Eficent Business and IT Consulting Services S.L. +# - Jordi Ballester Alomar +# © 2015-17 Serpent Consulting Services Pvt. Ltd. - Sudhir Arya +# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html). +from openerp import _, api, exceptions, models + + +class StockQuant(models.Model): + _inherit = 'stock.quant' + + @api.model + def _prepare_account_move_line(self, move, qty, cost, credit_account_id, + debit_account_id): + res = super(StockQuant, self)._prepare_account_move_line( + move, qty, cost, credit_account_id, debit_account_id + ) + + debit_line_vals = res[0][2] + credit_line_vals = res[1][2] + + if ( + move.operating_unit_id and move.operating_unit_dest_id and + move.operating_unit_id != move.operating_unit_dest_id and + debit_line_vals['account_id'] != credit_line_vals['account_id'] + ): + raise exceptions.UserError( + _('You cannot create stock moves involving separate source ' + 'and destination accounts related to different operating ' + 'units.') + ) + + debit_line_vals['operating_unit_id'] = ( + move.operating_unit_dest_id.id or move.operating_unit_id.id + ) + credit_line_vals['operating_unit_id'] = ( + move.operating_unit_id.id or move.operating_unit_dest_id.id + ) + return [(0, 0, debit_line_vals), (0, 0, credit_line_vals)] + + @api.model + def _account_entry_move(self, quants, move): + """ + Generate an accounting moves if the product being moved is subject + to real_time valuation tracking, + and the source or destination location are + a transit location or is outside of the company or the source or + destination locations belong to different operating units. + """ + res = super(StockQuant, self)._account_entry_move(quants, move) + if move.product_id.valuation == 'real_time': + # Inter-operating unit moves do not accept to + # from/to non-internal location + if ( + move.location_id.company_id == + move.location_dest_id.company_id and + move.operating_unit_id != move.operating_unit_dest_id + ): + err = False + if (move.location_id.usage != 'internal' and + move.location_dest_id.usage == 'internal'): + err = True + if (move.location_id.usage == 'internal' and + move.location_dest_id.usage != 'internal'): + err = True + if (move.location_id.usage != 'internal' and + move.location_dest_id.usage != 'internal'): + err = True + if err: + raise exceptions.UserError( + _('Transfers between locations of different operating ' + 'unit locations is only allowed when both source ' + 'and destination locations are internal.') + ) + src_company_ctx = dict( + force_company=move.location_id.company_id.id + ) + company_ctx = dict(company_id=move.company_id.id) + self_c = self.with_context(src_company_ctx) + data = self_c._get_accounting_data_for_valuation(move) + (journal_id, acc_src, acc_dest, acc_valuation) = data + quant_cost_qty = {} + for quant in quants: + if quant_cost_qty.get(quant.cost): + quant_cost_qty[quant.cost] += quant.qty + else: + quant_cost_qty[quant.cost] = quant.qty + move_obj = self.env['account.move'] + for cost, qty in quant_cost_qty.items(): + move_lines = self._prepare_account_move_line(move, qty, + cost, + acc_valuation, + acc_valuation) + move_obj.with_context(company_ctx).create({ + 'journal_id': journal_id, + 'line_ids': move_lines, + 'company_id': move.company_id.id, + 'ref': move.picking_id and move.picking_id.name, + }) + return res diff --git a/stock_account_operating_unit/tests/__init__.py b/stock_account_operating_unit/tests/__init__.py new file mode 100644 index 000000000..9f4fa86db --- /dev/null +++ b/stock_account_operating_unit/tests/__init__.py @@ -0,0 +1,6 @@ +# -*- coding: utf-8 -*- +# © 2015-17 Eficent Business and IT Consulting Services S.L. - +# Jordi Ballester Alomar +# © 2015-17 Serpent Consulting Services Pvt. Ltd. - Sudhir Arya +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +from . import test_stock_account_operating_unit diff --git a/stock_account_operating_unit/tests/test_stock_account_operating_unit.py b/stock_account_operating_unit/tests/test_stock_account_operating_unit.py new file mode 100644 index 000000000..5f7ef6a39 --- /dev/null +++ b/stock_account_operating_unit/tests/test_stock_account_operating_unit.py @@ -0,0 +1,324 @@ +# -*- coding: utf-8 -*- +# © 2015-17 Eficent Business and IT Consulting Services S.L. - +# Jordi Ballester Alomar +# © 2015-17 Serpent Consulting Services Pvt. Ltd. - Sudhir Arya +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +from openerp.addons.stock.tests import common + + +class TestStockAccountOperatingUnit(common.TestStockCommon): + + def setUp(self): + super(TestStockAccountOperatingUnit, self).setUp() + self.res_groups = self.env['res.groups'] + self.res_users_model = self.env['res.users'] + self.aml_model = self.env['account.move.line'] + self.account_model = self.env['account.account'] + self.product_model = self.env['product.product'] + self.product_cteg_model = self.env['product.category'] + self.inv_line_model = self.env['account.invoice.line'] + self.acc_type_model = self.env['account.account.type'] + self.operating_unit_model = self.env['operating.unit'] + self.company_model = self.env['res.company'] + self.move_model = self.env['stock.move'] + self.picking_model = self.env['stock.picking'] + self.ModelDataObj = self.env['ir.model.data'] + # company + self.company = self.ModelDataObj.xmlid_to_res_id('base.main_company') + # groups + self.group_stock_manager =\ + self.ModelDataObj.xmlid_to_res_id('stock.group_stock_manager') + self.grp_acc_user =\ + self.ModelDataObj.xmlid_to_res_id('account.group_account_invoice') + self.grp_stock_user =\ + self.ModelDataObj.xmlid_to_res_id('stock.group_stock_user') + # Main Operating Unit + self.ou1 = self.ModelDataObj.get_object('operating_unit', + 'main_operating_unit') + # B2B Operating Unit + self.b2b = self.ModelDataObj.get_object('operating_unit', + 'b2b_operating_unit') + # B2C Operating Unit + self.b2c = self.ModelDataObj.get_object('operating_unit', + 'b2c_operating_unit') + # Partner + self.partner1 = self.ModelDataObj.xmlid_to_res_id('base.res_partner_1') + self.stock_location_stock =\ + self.ModelDataObj.xmlid_to_res_id('stock.stock_location_stock') + self.supplier_location =\ + self.ModelDataObj.xmlid_to_res_id('stock.stock_location_suppliers') + # Create user1 + self.user1 =\ + self._create_user('stock_account_user_1', + [self.grp_stock_user, self.grp_acc_user, + self.group_stock_manager], + self.company, [self.ou1.id, self.b2c.id]) + # Create user2 + self.user2 =\ + self._create_user('stock_account_user_2', + [self.grp_stock_user, self.grp_acc_user, + self.group_stock_manager], + self.company, [self.b2c.id]) + # Create account for Goods Received Not Invoiced + name = 'Goods Received Not Invoiced' + code = 'grni' + acc_type = self.env.ref('account.data_account_type_equity') + self.account_grni = self._create_account(acc_type, name, code, + self.company) +# # Create account for Cost of Goods Sold + name = 'Cost of Goods Sold' + code = 'cogs' + acc_type = self.env.ref('account.data_account_type_expenses') + self.account_cogs_id = self._create_account(acc_type, name, code, + self.company) +# # Create account for Inventory + name = 'Inventory' + code = 'inventory' + acc_type = self.env.ref('account.data_account_type_fixed_assets') + self.account_inventory = self._create_account(acc_type, name, code, + self.company) +# # Create account for Inter-OU Clearing + name = 'Inter-OU Clearing' + code = 'inter_ou' + acc_type = self.env.ref('account.data_account_type_equity') + self.account_inter_ou_clearing = self._create_account(acc_type, + name, code, + self.company) + # Update company data + company = self.env.ref('base.main_company') + company.write({'inter_ou_clearing_account_id': + self.account_inter_ou_clearing.id, + 'ou_is_self_balanced': True}) +# Create Product + self.product = self._create_product() +# Create incoming stock picking type + self.incoming_id = self.env.ref('stock.warehouse0').in_type_id.id +# Create incoming and internal stock picking types + b2c_wh = self.env.ref('stock_operating_unit.stock_warehouse_b2c') + b2c_wh.lot_stock_id.write({'operating_unit_id': self.b2c.id}) + self.location_b2c_id = b2c_wh.lot_stock_id.id + self.b2c_type_in_id = b2c_wh.in_type_id.id + self.b2c_type_int_id = b2c_wh.int_type_id.id + + def _create_user(self, login, groups, company, operating_units): + """Create a user.""" + group_ids = [group for group in groups] + user = self.res_users_model.create({ + 'name': 'Test Stock Account User', + 'login': login, + 'password': 'demo', + 'email': 'example@yourcompany.com', + 'company_id': company, + 'company_ids': [(4, company)], + 'operating_unit_ids': [(4, ou) for ou in operating_units], + 'groups_id': [(6, 0, group_ids)] + }) + return user + + def _create_account(self, acc_type, name, code, company): + """Create an account.""" + account = self.account_model.create({ + 'name': name, + 'code': code, + 'user_type_id': acc_type.ids and acc_type.ids[0], + 'company_id': company + }) + return account + + def _create_product(self): + """Create a Product with inventory valuation set to auto.""" + product_cteg = self.product_cteg_model.create({ + 'name': 'test_product_ctg', + 'property_valuation': 'real_time', + 'property_stock_valuation_account_id': self.account_inventory.id, + 'property_stock_account_input_categ_id': self.account_grni.id, + 'property_stock_account_output_categ_id': self.account_cogs_id, + }) + product = self.product_model.create({ + 'name': 'test_product', + 'categ_id': product_cteg.id, + 'type': 'product', + 'list_price': 1.0, + 'standard_price': 1.0 + }) + return product + + def _create_picking(self, user_id, ou_id, picking_type, + src_loc_id, dest_loc_id): + """Create a Picking.""" + picking = self.picking_model.sudo(user_id).create({ + 'picking_type_id': picking_type, + 'location_id': src_loc_id, + 'location_dest_id': dest_loc_id, + 'operating_unit_id': ou_id, + }) + self.move_model.sudo(user_id).create({ + 'name': 'a move', + 'product_id': self.product.id, + 'product_uom_qty': 1.0, + 'product_uom': 1, + 'picking_id': picking.id, + 'location_id': src_loc_id, + 'location_dest_id': dest_loc_id, + }) + return picking + + def _confirm_receive(self, user_id, picking, picking_type=None): + """ + Checks the stock availability, validates and process the stock picking. + """ + picking.action_confirm() + picking.force_assign() + res = picking.sudo(user_id).do_new_transfer() + validate_id = res['res_id'] + validate = self.env['stock.immediate.transfer'].browse(validate_id) + validate.process() + + def _check_account_balance(self, account_id, operating_unit=None, + expected_balance=0.0): + """ + Check the balance of the account based on different operating units. + """ + domain = [('account_id', '=', account_id)] + if operating_unit: + domain.extend([('operating_unit_id', '=', operating_unit.id)]) + + balance = self._get_balance(domain) + if operating_unit: + self.assertEqual( + balance, expected_balance, + 'Balance is not %s for Operating Unit %s.' + % (str(expected_balance), operating_unit.name)) + else: + self.assertEqual( + balance, expected_balance, + 'Balance is not %s for all Operating Units.' + % str(expected_balance)) \ + + + def _get_balance(self, domain): + """ + Call read_group method and return the balance of particular account. + """ + aml_rec = self.aml_model.read_group(domain, + ['debit', 'credit', 'account_id'], + ['account_id']) + if aml_rec: + return aml_rec[0].get('debit', 0) - aml_rec[0].get('credit', 0) + else: + return 0.0 + + def test_pickings(self): + """Test account balances during receiving stock into the main + operating unit, then into b2c operating unit, and then transfer stock + from main ou to b2c.""" + # Create Incoming Shipment 1 + self.picking = self._create_picking( + self.user1.id, self.ou1.id, self.incoming_id, + self.supplier_location, self.stock_location_stock) + # Receive it + self._confirm_receive(self.user1.id, self.picking) + # GL account ‘Inventory’ has balance 1 irrespective of the OU + expected_balance = 1.0 + self._check_account_balance(self.account_inventory.id, + operating_unit=None, + expected_balance=expected_balance) + # GL account ‘Inventory’ has balance 1 on OU main_operating_unit + expected_balance = 1.0 + self._check_account_balance(self.account_inventory.id, + operating_unit=self.ou1, + expected_balance=expected_balance) + # GL account ‘Inventory’ has balance 0 on OU main_operating_unit + expected_balance = 0.0 + self._check_account_balance(self.account_inventory.id, + operating_unit=self.b2c, + expected_balance=expected_balance) + # GL account ‘Goods Received Not Invoiced’ has balance - 1 + # irrespective of the OU + expected_balance = -1.0 + self._check_account_balance(self.account_grni.id, + operating_unit=None, + expected_balance=expected_balance) + # GL account ‘Goods Received Not Invoiced’ has balance -1 on Main OU + expected_balance = -1.0 + self._check_account_balance(self.account_grni.id, + operating_unit=self.ou1, + expected_balance=expected_balance) + # GL account ‘Goods Received Not Invoiced’ has balance 0 on OU b2c + expected_balance = 0.0 + self._check_account_balance(self.account_grni.id, + operating_unit=self.b2c, + expected_balance=expected_balance) + + # Create Incoming Shipment 2 + self.picking =\ + self._create_picking(self.user2.id, self.b2c.id, + self.b2c_type_in_id, + self.supplier_location, + self.location_b2c_id) + +# # Receive it + self._confirm_receive(self.user2.id, self.picking) + + # GL account ‘Inventory’ has balance 2 irrespective of the OU + expected_balance = 2.0 + self._check_account_balance(self.account_inventory.id, + operating_unit=None, + expected_balance=expected_balance) + # GL account ‘Inventory’ has balance 1 on OU main_operating_unit + expected_balance = 1.0 + self._check_account_balance(self.account_inventory.id, + operating_unit=self.ou1, + expected_balance=expected_balance) + # GL account ‘Inventory’ has balance 1 on OU b2c + expected_balance = 1.0 + self._check_account_balance(self.account_inventory.id, + operating_unit=self.b2c, + expected_balance=expected_balance) + # GL account ‘Goods Received Not Invoiced’ has balance - 2 + # irrespective of the OU + expected_balance = -2.0 + self._check_account_balance(self.account_grni.id, + operating_unit=None, + expected_balance=expected_balance) + # GL account ‘Goods Received Not Invoiced’ has balance -1 on Main OU + expected_balance = -1.0 + self._check_account_balance(self.account_grni.id, + operating_unit=self.ou1, + expected_balance=expected_balance) + # GL account ‘Goods Received Not Invoiced’ has balance 0 on OU b2c + expected_balance = -1.0 + self._check_account_balance(self.account_grni.id, + operating_unit=self.b2c, + expected_balance=expected_balance) + + # Create Internal Transfer + self.picking =\ + self._create_picking(self.user1.id, self.b2c.id, + self.b2c_type_int_id, + self.stock_location_stock, + self.location_b2c_id) + # Receive it + picking_type = 'internal' + self._confirm_receive(self.user1.id, self.picking, + picking_type=picking_type) + # GL account ‘Inventory’ has balance 2 irrespective of the OU + expected_balance = 2.0 + self._check_account_balance(self.account_inventory.id, + operating_unit=None, + expected_balance=expected_balance) + # GL account ‘Inventory’ has balance 0 on OU main_operating_unit + expected_balance = 0.0 + self._check_account_balance(self.account_inventory.id, + operating_unit=self.ou1, + expected_balance=expected_balance) + # GL account ‘Inventory’ has balance 2 on OU b2c + expected_balance = 2.0 + self._check_account_balance(self.account_inventory.id, + operating_unit=self.b2c, + expected_balance=expected_balance) + # GL account ‘Inter-OU clearing’ has balance 0 irrespective of the OU + expected_balance = 0.0 + self._check_account_balance(self.account_inter_ou_clearing.id, + operating_unit=None, + expected_balance=expected_balance) From 945b9807f379134729fd674a6efeddda517b9b65 Mon Sep 17 00:00:00 2001 From: aheficent Date: Fri, 26 May 2017 17:00:38 +0200 Subject: [PATCH 2/4] [MIG] stock_account_operating_unit: Migration to 10.0 --- stock_account_operating_unit/README.rst | 17 ++++--- .../{__openerp__.py => __manifest__.py} | 4 +- .../model/__init__.py | 3 +- .../model/stock_move.py | 38 ++++++++++++++++ .../model/{stock.py => stock_quant.py} | 44 +++---------------- 5 files changed, 59 insertions(+), 47 deletions(-) rename stock_account_operating_unit/{__openerp__.py => __manifest__.py} (90%) create mode 100644 stock_account_operating_unit/model/stock_move.py rename stock_account_operating_unit/model/{stock.py => stock_quant.py} (64%) diff --git a/stock_account_operating_unit/README.rst b/stock_account_operating_unit/README.rst index ec1701d79..aa7168248 100644 --- a/stock_account_operating_unit/README.rst +++ b/stock_account_operating_unit/README.rst @@ -18,12 +18,15 @@ If your company is required to generate a balanced balance sheet by operating unit you can specify at company level that operating units should be self-balanced, and then indicate a self-balancing clearing account. -* Create an account for "Inter-OU Clearing" of type Regular. -* Go to *Settings / Companies / Configuration* and: - ** Set the "Operating Units are self-balanced" checkbox - ** Set the "Inter-OU Clearing" account in "Inter-operating unit clearing - account" field. -* Assign Operating Unit in Accounts. +#. Create an account for "Inter-OU Clearing" of type Regular. +#. Go to *Settings / Companies / Configuration* and: + + * Set the "Operating Units are self-balanced" checkbox. + + * Set the "Inter-OU Clearing" account in "Inter-operating unit clearing + account" field. + +#. Assign Operating Unit in Accounts. Usage @@ -35,7 +38,7 @@ within the OU when the journal entries are posted .. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas :alt: Try me on Runbot - :target: https://runbot.odoo-community.org/runbot/213/9.0 + :target: https://runbot.odoo-community.org/runbot/213/10.0 Bug Tracker =========== diff --git a/stock_account_operating_unit/__openerp__.py b/stock_account_operating_unit/__manifest__.py similarity index 90% rename from stock_account_operating_unit/__openerp__.py rename to stock_account_operating_unit/__manifest__.py index 88f3c1418..abc1cc1d1 100644 --- a/stock_account_operating_unit/__openerp__.py +++ b/stock_account_operating_unit/__manifest__.py @@ -8,13 +8,13 @@ "name": "Stock account moves with Operating Unit", "summary": "Create journal entries in moves between internal locations " "with different operating units.", - "version": "9.0.1.0.0", + "version": "10.0.1.0.0", "category": "Generic Modules/Sales & Purchases", "author": "Eficent Business and IT Consulting Services S.L., " "Serpent Consulting Services Pvt. Ltd.," "Odoo Community Association (OCA)", "license": "LGPL-3", - "website": "http://www.eficent.com", + "website": "https://github.com/OCA/operating-unit", "depends": [ 'stock_operating_unit', 'account_operating_unit', diff --git a/stock_account_operating_unit/model/__init__.py b/stock_account_operating_unit/model/__init__.py index 5261ffb5a..6c3815ec4 100644 --- a/stock_account_operating_unit/model/__init__.py +++ b/stock_account_operating_unit/model/__init__.py @@ -3,4 +3,5 @@ # - Jordi Ballester Alomar # © 2015-17 Serpent Consulting Services Pvt. Ltd. - Sudhir Arya # License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html). -from . import stock +from . import stock_move +from . import stock_quant diff --git a/stock_account_operating_unit/model/stock_move.py b/stock_account_operating_unit/model/stock_move.py new file mode 100644 index 000000000..ae921a7ab --- /dev/null +++ b/stock_account_operating_unit/model/stock_move.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +# © 2015-17 Eficent Business and IT Consulting Services S.L. +# - Jordi Ballester Alomar +# © 2015-17 Serpent Consulting Services Pvt. Ltd. - Sudhir Arya +# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html). +from openerp import _, api, exceptions, models + + +class StockMove(models.Model): + _inherit = "stock.move" + + @api.model + def _prepare_account_move_line(self, qty, cost, credit_account_id, + debit_account_id): + res = super(StockMove, self)._prepare_account_move_line( + qty, cost, credit_account_id, debit_account_id) + + debit_line_vals = res[0][2] + credit_line_vals = res[1][2] + + if ( + self.operating_unit_id and self.operating_unit_dest_id and + self.operating_unit_id != self.operating_unit_dest_id and + debit_line_vals['account_id'] != credit_line_vals['account_id'] + ): + raise exceptions.UserError( + _('You cannot create stock moves involving separate source ' + 'and destination accounts related to different operating ' + 'units.') + ) + + debit_line_vals['operating_unit_id'] = ( + self.operating_unit_dest_id.id or self.operating_unit_id.id + ) + credit_line_vals['operating_unit_id'] = ( + self.operating_unit_id.id or self.operating_unit_dest_id.id + ) + return [(0, 0, debit_line_vals), (0, 0, credit_line_vals)] diff --git a/stock_account_operating_unit/model/stock.py b/stock_account_operating_unit/model/stock_quant.py similarity index 64% rename from stock_account_operating_unit/model/stock.py rename to stock_account_operating_unit/model/stock_quant.py index 7a4088659..6c3b026e8 100644 --- a/stock_account_operating_unit/model/stock.py +++ b/stock_account_operating_unit/model/stock_quant.py @@ -9,37 +9,8 @@ class StockQuant(models.Model): _inherit = 'stock.quant' - @api.model - def _prepare_account_move_line(self, move, qty, cost, credit_account_id, - debit_account_id): - res = super(StockQuant, self)._prepare_account_move_line( - move, qty, cost, credit_account_id, debit_account_id - ) - - debit_line_vals = res[0][2] - credit_line_vals = res[1][2] - - if ( - move.operating_unit_id and move.operating_unit_dest_id and - move.operating_unit_id != move.operating_unit_dest_id and - debit_line_vals['account_id'] != credit_line_vals['account_id'] - ): - raise exceptions.UserError( - _('You cannot create stock moves involving separate source ' - 'and destination accounts related to different operating ' - 'units.') - ) - - debit_line_vals['operating_unit_id'] = ( - move.operating_unit_dest_id.id or move.operating_unit_id.id - ) - credit_line_vals['operating_unit_id'] = ( - move.operating_unit_id.id or move.operating_unit_dest_id.id - ) - return [(0, 0, debit_line_vals), (0, 0, credit_line_vals)] - - @api.model - def _account_entry_move(self, quants, move): + @api.multi + def _account_entry_move(self, move): """ Generate an accounting moves if the product being moved is subject to real_time valuation tracking, @@ -47,7 +18,7 @@ def _account_entry_move(self, quants, move): a transit location or is outside of the company or the source or destination locations belong to different operating units. """ - res = super(StockQuant, self)._account_entry_move(quants, move) + res = super(StockQuant, self)._account_entry_move(move) if move.product_id.valuation == 'real_time': # Inter-operating unit moves do not accept to # from/to non-internal location @@ -76,19 +47,18 @@ def _account_entry_move(self, quants, move): force_company=move.location_id.company_id.id ) company_ctx = dict(company_id=move.company_id.id) - self_c = self.with_context(src_company_ctx) - data = self_c._get_accounting_data_for_valuation(move) + self = self.with_context(src_company_ctx) + data = move._get_accounting_data_for_valuation() (journal_id, acc_src, acc_dest, acc_valuation) = data quant_cost_qty = {} - for quant in quants: + for quant in self: if quant_cost_qty.get(quant.cost): quant_cost_qty[quant.cost] += quant.qty else: quant_cost_qty[quant.cost] = quant.qty move_obj = self.env['account.move'] for cost, qty in quant_cost_qty.items(): - move_lines = self._prepare_account_move_line(move, qty, - cost, + move_lines = move._prepare_account_move_line(qty, cost, acc_valuation, acc_valuation) move_obj.with_context(company_ctx).create({ From 839fde1d8e706d87affe826b63bb38016ca7d27b Mon Sep 17 00:00:00 2001 From: aheficent Date: Tue, 25 Jul 2017 16:33:25 +0200 Subject: [PATCH 3/4] [FIX]review round --- stock_account_operating_unit/README.rst | 3 ++- .../model/stock_quant.py | 24 ++++--------------- 2 files changed, 6 insertions(+), 21 deletions(-) diff --git a/stock_account_operating_unit/README.rst b/stock_account_operating_unit/README.rst index aa7168248..89dd0c295 100644 --- a/stock_account_operating_unit/README.rst +++ b/stock_account_operating_unit/README.rst @@ -7,6 +7,7 @@ Stock account moves with Operating Unit ======================================= This module introduces the following features: + - Creates account move lines when stock moves are posted between internal locations within the same company, but different OU’s. @@ -24,7 +25,7 @@ be self-balanced, and then indicate a self-balancing clearing account. * Set the "Operating Units are self-balanced" checkbox. * Set the "Inter-OU Clearing" account in "Inter-operating unit clearing - account" field. + account" field. #. Assign Operating Unit in Accounts. diff --git a/stock_account_operating_unit/model/stock_quant.py b/stock_account_operating_unit/model/stock_quant.py index 6c3b026e8..502611385 100644 --- a/stock_account_operating_unit/model/stock_quant.py +++ b/stock_account_operating_unit/model/stock_quant.py @@ -3,7 +3,7 @@ # - Jordi Ballester Alomar # © 2015-17 Serpent Consulting Services Pvt. Ltd. - Sudhir Arya # License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html). -from openerp import _, api, exceptions, models +from openerp import api, models class StockQuant(models.Model): @@ -12,7 +12,7 @@ class StockQuant(models.Model): @api.multi def _account_entry_move(self, move): """ - Generate an accounting moves if the product being moved is subject + Generate accounting moves if the product being moved is subject to real_time valuation tracking, and the source or destination location are a transit location or is outside of the company or the source or @@ -27,29 +27,13 @@ def _account_entry_move(self, move): move.location_dest_id.company_id and move.operating_unit_id != move.operating_unit_dest_id ): - err = False - if (move.location_id.usage != 'internal' and - move.location_dest_id.usage == 'internal'): - err = True - if (move.location_id.usage == 'internal' and - move.location_dest_id.usage != 'internal'): - err = True - if (move.location_id.usage != 'internal' and - move.location_dest_id.usage != 'internal'): - err = True - if err: - raise exceptions.UserError( - _('Transfers between locations of different operating ' - 'unit locations is only allowed when both source ' - 'and destination locations are internal.') - ) src_company_ctx = dict( force_company=move.location_id.company_id.id ) company_ctx = dict(company_id=move.company_id.id) self = self.with_context(src_company_ctx) - data = move._get_accounting_data_for_valuation() - (journal_id, acc_src, acc_dest, acc_valuation) = data + (journal_id, acc_src, acc_dest, acc_valuation) = \ + move._get_accounting_data_for_valuation() quant_cost_qty = {} for quant in self: if quant_cost_qty.get(quant.cost): From 83a4cf67ba940c801992753db5b25424e7e46a2f Mon Sep 17 00:00:00 2001 From: aheficent Date: Tue, 21 Nov 2017 15:23:46 +0100 Subject: [PATCH 4/4] [FIX]error when periodic valuation --- .../model/stock_move.py | 39 ++++++++++--------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/stock_account_operating_unit/model/stock_move.py b/stock_account_operating_unit/model/stock_move.py index ae921a7ab..a05e12da9 100644 --- a/stock_account_operating_unit/model/stock_move.py +++ b/stock_account_operating_unit/model/stock_move.py @@ -14,25 +14,26 @@ def _prepare_account_move_line(self, qty, cost, credit_account_id, debit_account_id): res = super(StockMove, self)._prepare_account_move_line( qty, cost, credit_account_id, debit_account_id) + if res: + debit_line_vals = res[0][2] + credit_line_vals = res[1][2] - debit_line_vals = res[0][2] - credit_line_vals = res[1][2] + if ( + self.operating_unit_id and self.operating_unit_dest_id and + self.operating_unit_id != self.operating_unit_dest_id and + debit_line_vals['account_id'] != credit_line_vals['account_id'] + ): + raise exceptions.UserError( + _('You cannot create stock moves involving separate source ' + 'and destination accounts related to different operating ' + 'units.') + ) - if ( - self.operating_unit_id and self.operating_unit_dest_id and - self.operating_unit_id != self.operating_unit_dest_id and - debit_line_vals['account_id'] != credit_line_vals['account_id'] - ): - raise exceptions.UserError( - _('You cannot create stock moves involving separate source ' - 'and destination accounts related to different operating ' - 'units.') + debit_line_vals['operating_unit_id'] = ( + self.operating_unit_dest_id.id or self.operating_unit_id.id ) - - debit_line_vals['operating_unit_id'] = ( - self.operating_unit_dest_id.id or self.operating_unit_id.id - ) - credit_line_vals['operating_unit_id'] = ( - self.operating_unit_id.id or self.operating_unit_dest_id.id - ) - return [(0, 0, debit_line_vals), (0, 0, credit_line_vals)] + credit_line_vals['operating_unit_id'] = ( + self.operating_unit_id.id or self.operating_unit_dest_id.id + ) + return [(0, 0, debit_line_vals), (0, 0, credit_line_vals)] + return res