[14.0][MIG] account_move_line_rma_order_line

This commit is contained in:
Christopher Ormaza
2022-01-26 10:43:55 -05:00
committed by AlexPForgeFlow
parent 30d2aa1639
commit 9cd5606df3
12 changed files with 258 additions and 272 deletions

View File

@@ -1 +1,10 @@
from . import models
import logging
from odoo import api, SUPERUSER_ID
_logger = logging.getLogger(__name__)
def post_init_hook(cr, registry):
api.Environment(cr, SUPERUSER_ID, {})

View File

@@ -1,19 +1,19 @@
# © 2017 Eficent Business and IT Consulting Services S.L. (www.eficent.com)
# © 2017-2022 ForgeFlow S.L. (www.forgeflow.com)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
{
"name": "Account Move Line Rma Order Line",
"summary": "Introduces the rma order line to the journal items",
"version": "12.0.1.0.0",
"author": "Eficent, "
"Odoo Community Association (OCA)",
"website": "http://www.github.com/OCA/account-financial-tools",
"version": "14.0.1.0.0",
"author": "ForgeFlow, " "Odoo Community Association (OCA)",
"website": "https://github.com/ForgeFlow/stock-rma",
"category": "Generic",
"depends": ["account", "rma_account"],
"depends": ["stock_account", "rma_account"],
"license": "AGPL-3",
"data": [
"security/account_security.xml",
"views/account_move_view.xml",
],
'installable': True,
"installable": True,
"maintainers": ["ChisOForgeFlow"],
"development_status": "Beta",
}

View File

@@ -1,3 +1,2 @@
from . import account_move
from . import account_invoice
from . import stock_move
from . import account_move

View File

@@ -1,28 +0,0 @@
# © 2017 Eficent Business and IT Consulting Services S.L. (www.eficent.com)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
from odoo import api, models
class AccountInvoice(models.Model):
_inherit = 'account.invoice'
@api.model
def invoice_line_move_line_get(self):
res = super(AccountInvoice, self).invoice_line_move_line_get()
invoice_line_model = self.env['account.invoice.line']
for move_line_dict in res:
if 'invl_id' in move_line_dict:
line = invoice_line_model.browse(move_line_dict['invl_id'])
move_line_dict['rma_line_id'] = line.rma_line_id.id
return res
@api.model
def line_get_convert(self, line, part):
res = super(AccountInvoice, self).line_get_convert(line, part)
if line.get('rma_line_id', False):
res['rma_line_id'] = line.get('rma_line_id')
return res

View File

@@ -1,13 +1,33 @@
# © 2017 Eficent Business and IT Consulting Services S.L. (www.eficent.com)
# © 2017-2022 ForgeFlow S.L. (www.forgeflow.com)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
from odoo import fields, models
from odoo import models
class AccountMoveLine(models.Model):
class AccountMove(models.Model):
_inherit = 'account.move.line'
_inherit = "account.move"
rma_line_id = fields.Many2one('rma.order.line',
'Rma Order Line',
ondelete='set null', index=True)
def _stock_account_prepare_anglo_saxon_out_lines_vals(self):
product_model = self.env["product.product"]
res = super()._stock_account_prepare_anglo_saxon_out_lines_vals()
for line in res:
if line.get("product_id", False):
product = product_model.browse(line.get("product_id", False))
if (
line.get("account_id")
!= product.categ_id.property_stock_valuation_account_id.id
):
current_move = self.browse(line.get("move_id", False))
current_rma = current_move.invoice_line_ids.filtered(
lambda x: x.rma_line_id and x.product_id.id == product.id
).mapped("rma_line_id")
if len(current_rma) == 1:
line.update({"rma_line_id": current_rma.id})
elif len(current_rma) > 1:
find_with_label_rma = current_rma.filtered(
lambda x: x.name == line.get("name")
)
if len(find_with_label_rma) == 1:
line.update({"rma_line_id": find_with_label_rma.id})
return res

View File

@@ -1,4 +1,4 @@
# © 2017 Eficent Business and IT Consulting Services S.L. (www.eficent.com)
# © 2017-2022 ForgeFlow S.L. (www.forgeflow.com)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
from odoo import api, models
@@ -8,12 +8,16 @@ class StockMove(models.Model):
_inherit = "stock.move"
@api.model
def _prepare_account_move_line(self, qty, cost,
credit_account_id, debit_account_id):
def _prepare_account_move_line(
self, qty, cost, credit_account_id, debit_account_id, description
):
res = super(StockMove, self)._prepare_account_move_line(
qty, cost, credit_account_id, debit_account_id)
qty, cost, credit_account_id, debit_account_id, description
)
for line in res:
if line[2]["account_id"] != self.product_id.categ_id.\
property_stock_valuation_account_id.id:
line[2]['rma_line_id'] = self.rma_line_id.id
if (
line[2]["account_id"]
!= self.product_id.categ_id.property_stock_valuation_account_id.id
):
line[2]["rma_line_id"] = self.rma_line_id.id
return res

View File

@@ -0,0 +1 @@
* Christopher Ormaza <chris.ormaza@forgeflow.com>

View File

@@ -0,0 +1,4 @@
This module will add the RMA order line to journal items.
The ultimate goal is to establish the RMA order line as one of the key
fields to reconcile the Goods Received Not Invoiced accrual account.

View File

@@ -0,0 +1,7 @@
The RMA order line will be automatically copied to the journal items.
* When a supplier invoice is created referencing RMA orders, the
RMA order line will be copied to the corresponding journal item.
* When a stock move is validated and generates a journal entry, the RMA
order line is copied to the account move line.

View File

@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record id="group_account_move_rma_order_line" model="res.groups">
<field name="name">Rma Order Line in Journal Items</field>
<field name="category_id" ref="base.module_category_hidden"/>
<field name="users" eval="[(4, ref('base.user_root'))]"/>
<field name="category_id" ref="base.module_category_hidden" />
<field name="users" eval="[(4, ref('base.user_root'))]" />
</record>
</odoo>

View File

@@ -1,153 +1,154 @@
# © 2017 Eficent Business and IT Consulting Services S.L. (www.eficent.com)
# © 2017-2022 ForgeFlow S.L. (www.forgeflow.com)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
from odoo.tests import common
class TestAccountMoveLineRmaOrderLine(common.SavepointCase):
@classmethod
def setUpClass(cls):
super(TestAccountMoveLineRmaOrderLine, cls).setUpClass()
cls.rma_model = cls.env['rma.order']
cls.rma_line_model = cls.env['rma.order.line']
cls.rma_refund_wiz = cls.env['rma.refund']
cls.rma_add_stock_move = cls.env['rma_add_stock_move']
cls.rma_make_picking = cls.env['rma_make_picking.wizard']
cls.invoice_model = cls.env['account.invoice']
cls.stock_picking_model = cls.env['stock.picking']
cls.invoice_line_model = cls.env['account.invoice.line']
cls.product_model = cls.env['product.product']
cls.product_ctg_model = cls.env['product.category']
cls.acc_type_model = cls.env['account.account.type']
cls.account_model = cls.env['account.account']
cls.aml_model = cls.env['account.move.line']
cls.res_users_model = cls.env['res.users']
cls.rma_model = cls.env["rma.order"]
cls.rma_line_model = cls.env["rma.order.line"]
cls.rma_refund_wiz = cls.env["rma.refund"]
cls.rma_add_stock_move = cls.env["rma_add_stock_move"]
cls.rma_make_picking = cls.env["rma_make_picking.wizard"]
cls.invoice_model = cls.env["account.move"]
cls.stock_picking_model = cls.env["stock.picking"]
cls.invoice_line_model = cls.env["account.move.line"]
cls.product_model = cls.env["product.product"]
cls.product_ctg_model = cls.env["product.category"]
cls.acc_type_model = cls.env["account.account.type"]
cls.account_model = cls.env["account.account"]
cls.aml_model = cls.env["account.move.line"]
cls.res_users_model = cls.env["res.users"]
cls.partner1 = cls.env.ref('base.res_partner_1')
cls.location_stock = cls.env.ref('stock.stock_location_stock')
cls.company = cls.env.ref('base.main_company')
cls.group_rma_user = cls.env.ref('rma.group_rma_customer_user')
cls.group_account_invoice = cls.env.ref(
'account.group_account_invoice')
cls.group_account_manager = cls.env.ref(
'account.group_account_manager')
cls.stock_location = cls.env.ref('stock.stock_location_stock')
wh = cls.env.ref('stock.warehouse0')
cls.partner1 = cls.env.ref("base.res_partner_1")
cls.location_stock = cls.env.ref("stock.stock_location_stock")
cls.company = cls.env.ref("base.main_company")
cls.group_rma_user = cls.env.ref("rma.group_rma_customer_user")
cls.group_account_invoice = cls.env.ref("account.group_account_invoice")
cls.group_account_manager = cls.env.ref("account.group_account_manager")
cls.stock_location = cls.env.ref("stock.stock_location_stock")
wh = cls.env.ref("stock.warehouse0")
cls.stock_rma_location = wh.lot_rma_id
cls.customer_location = cls.env.ref(
'stock.stock_location_customers')
cls.supplier_location = cls.env.ref(
'stock.stock_location_suppliers')
cls.customer_location = cls.env.ref("stock.stock_location_customers")
cls.supplier_location = cls.env.ref("stock.stock_location_suppliers")
# Create account for Goods Received Not Invoiced
acc_type = cls._create_account_type('equity', 'other')
name = 'Goods Received Not Invoiced'
code = 'grni'
cls.account_grni = cls._create_account(
acc_type, name, code, cls.company)
acc_type = cls._create_account_type("equity", "other", "equity")
name = "Goods Received Not Invoiced"
code = "grni"
cls.account_grni = cls._create_account(acc_type, name, code, cls.company)
# Create account for Cost of Goods Sold
acc_type = cls._create_account_type('expense', 'other')
name = 'Cost of Goods Sold'
code = 'cogs'
cls.account_cogs = cls._create_account(
acc_type, name, code, cls.company)
acc_type = cls._create_account_type("expense", "other", "expense")
name = "Cost of Goods Sold"
code = "cogs"
cls.account_cogs = cls._create_account(acc_type, name, code, cls.company)
# Create account for Inventory
acc_type = cls._create_account_type('asset', 'other')
name = 'Inventory'
code = 'inventory'
cls.account_inventory = cls._create_account(
acc_type, name, code, cls.company)
acc_type = cls._create_account_type("asset", "other", "asset")
name = "Inventory"
code = "inventory"
cls.account_inventory = cls._create_account(acc_type, name, code, cls.company)
# Create Product
cls.product = cls._create_product()
cls.product_uom_id = cls.env.ref('uom.product_uom_unit')
cls.product_uom_id = cls.env.ref("uom.product_uom_unit")
# Create users
cls.rma_user = cls._create_user(
'rma_user', [cls.group_rma_user,
cls.group_account_invoice], cls.company)
"rma_user", [cls.group_rma_user, cls.group_account_invoice], cls.company
)
cls.account_invoice = cls._create_user(
'account_invoice', [cls.group_account_invoice], cls.company)
"account_invoice", [cls.group_account_invoice], cls.company
)
cls.account_manager = cls._create_user(
'account_manager', [cls.group_account_manager], cls.company)
"account_manager", [cls.group_account_manager], cls.company
)
@classmethod
def _create_user(cls, login, groups, company):
""" Create a user."""
group_ids = [group.id for group in groups]
user = \
cls.res_users_model.with_context(
{'no_reset_password': True}).create({
'name': 'Test User',
'login': login,
'password': 'demo',
'email': 'test@yourcompany.com',
'company_id': company.id,
'company_ids': [(4, company.id)],
'groups_id': [(6, 0, group_ids)]
})
user = cls.res_users_model.with_context({"no_reset_password": True}).create(
{
"name": "Test User",
"login": login,
"password": "demo",
"email": "test@yourcompany.com",
"company_id": company.id,
"company_ids": [(4, company.id)],
"groups_id": [(6, 0, group_ids)],
}
)
return user.id
@classmethod
def _create_account_type(cls, name, type):
acc_type = cls.acc_type_model.create({
'name': name,
'type': type
})
def _create_account_type(cls, name, type, internal_group):
acc_type = cls.acc_type_model.create(
{"name": name, "type": type, "internal_group": internal_group}
)
return acc_type
@classmethod
def _create_account(cls, acc_type, name, code, company):
"""Create an account."""
account = cls.account_model.create({
'name': name,
'code': code,
'user_type_id': acc_type.id,
'company_id': company.id
})
account = cls.account_model.create(
{
"name": name,
"code": code,
"user_type_id": acc_type.id,
"company_id": company.id,
}
)
return account
@classmethod
def _create_product(cls):
"""Create a Product."""
# group_ids = [group.id for group in groups]
product_ctg = cls.product_ctg_model.create({
'name': 'test_product_ctg',
'property_stock_valuation_account_id': cls.account_inventory.id,
'property_valuation': 'real_time',
'property_stock_account_input_categ_id': cls.account_grni.id,
'property_stock_account_output_categ_id': cls.account_cogs.id,
})
product = cls.product_model.create({
'name': 'test_product',
'categ_id': product_ctg.id,
'type': 'product',
'standard_price': 1.0,
'list_price': 1.0,
})
product_ctg = cls.product_ctg_model.create(
{
"name": "test_product_ctg",
"property_stock_valuation_account_id": cls.account_inventory.id,
"property_valuation": "real_time",
"property_stock_account_input_categ_id": cls.account_grni.id,
"property_stock_account_output_categ_id": cls.account_cogs.id,
}
)
product = cls.product_model.create(
{
"name": "test_product",
"categ_id": product_ctg.id,
"type": "product",
"standard_price": 1.0,
"list_price": 1.0,
}
)
return product
@classmethod
def _create_picking(cls, partner):
return cls.stock_picking_model.create({
'partner_id': partner.id,
'picking_type_id': cls.env.ref('stock.picking_type_in').id,
'location_id': cls.stock_location.id,
'location_dest_id': cls.supplier_location.id
})
return cls.stock_picking_model.create(
{
"partner_id": partner.id,
"picking_type_id": cls.env.ref("stock.picking_type_in").id,
"location_id": cls.stock_location.id,
"location_dest_id": cls.supplier_location.id,
}
)
@classmethod
def _prepare_move(cls, product, qty, src, dest, picking_in):
res = {
'partner_id': cls.partner1.id,
'product_id': product.id,
'name': product.partner_ref,
'state': 'confirmed',
'product_uom': cls.product_uom_id.id or product.uom_id.id,
'product_uom_qty': qty,
'origin': 'Test RMA',
'location_id': src.id,
'location_dest_id': dest.id,
'picking_id': picking_in.id
"partner_id": cls.partner1.id,
"product_id": product.id,
"name": product.partner_ref,
"state": "confirmed",
"product_uom": cls.product_uom_id.id or product.uom_id.id,
"product_uom_qty": qty,
"origin": "Test RMA",
"location_id": src.id,
"location_dest_id": dest.id,
"picking_id": picking_in.id,
}
return res
@@ -157,35 +158,36 @@ class TestAccountMoveLineRmaOrderLine(common.SavepointCase):
moves = []
for item in products2move:
move_values = cls._prepare_move(
item[0], item[1], cls.stock_location,
cls.customer_location, picking_in)
moves.append(cls.env['stock.move'].create(move_values))
item[0], item[1], cls.stock_location, cls.customer_location, picking_in
)
moves.append(cls.env["stock.move"].create(move_values))
rma_id = cls.rma_model.create(
{
'reference': '0001',
'type': 'customer',
'partner_id': partner.id,
'company_id': cls.env.ref('base.main_company').id
})
"reference": "0001",
"type": "customer",
"partner_id": partner.id,
"company_id": cls.env.ref("base.main_company").id,
}
)
for move in moves:
wizard = cls.rma_add_stock_move.new(
{'stock_move_id': move.id, 'customer': True,
'active_ids': rma_id.id,
'rma_id': rma_id.id,
'partner_id': move.partner_id.id,
'active_model': 'rma.order',
}
{
"move_ids": [(6, 0, move.ids)],
"rma_id": rma_id.id,
"partner_id": move.partner_id.id,
}
)
data = wizard._prepare_rma_line_from_stock_move(move)
# data = wizard._prepare_rma_line_from_stock_move(move)
wizard.add_lines()
if move.product_id.rma_customer_operation_id:
move.product_id.rma_customer_operation_id.in_route_id = False
move.product_id.categ_id.rma_customer_operation_id = False
move.product_id.rma_customer_operation_id = False
wizard._prepare_rma_line_from_stock_move(move)
cls.line = cls.rma_line_model.create(data)
# CHECK ME: this code duplicate rma lines, what is the porpourse?
# if move.product_id.rma_customer_operation_id:
# move.product_id.rma_customer_operation_id.in_route_id = False
# move.product_id.categ_id.rma_customer_operation_id = False
# move.product_id.rma_customer_operation_id = False
# wizard._prepare_rma_line_from_stock_move(move)
# cls.line = cls.rma_line_model.create(data)
return rma_id
def _get_balance(self, domain):
@@ -193,69 +195,91 @@ class TestAccountMoveLineRmaOrderLine(common.SavepointCase):
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'])
domain, ["debit", "credit", "account_id"], ["account_id"]
)
if aml_rec:
return aml_rec[0].get('debit', 0) - aml_rec[0].get('credit', 0)
return aml_rec[0].get("debit", 0) - aml_rec[0].get("credit", 0)
else:
return 0.0
def _check_account_balance(self, account_id, rma_line=None,
expected_balance=0.0):
def _check_account_balance(self, account_id, rma_line=None, expected_balance=0.0):
"""
Check the balance of the account
"""
domain = [('account_id', '=', account_id)]
domain = [("account_id", "=", account_id)]
if rma_line:
domain.extend([('rma_line_id', '=', rma_line.id)])
domain.extend([("rma_line_id", "=", rma_line.id)])
balance = self._get_balance(domain)
if rma_line:
self.assertEqual(balance, expected_balance,
'Balance is not %s for rma Line %s.'
% (str(expected_balance), rma_line.name))
self.assertEqual(
balance,
expected_balance,
"Balance is not %s for rma Line %s."
% (str(expected_balance), rma_line.name),
)
def test_rma_invoice(self):
"""Test that the rma line moves from the rma order to the
account move line and to the invoice line.
"""
products2move = [(self.product, 1), ]
products2move = [
(self.product, 1),
]
rma = self._create_rma(products2move, self.partner1)
rma_line = rma.rma_line_ids[0]
rma_line = rma.rma_line_ids
rma_line.action_rma_approve()
wizard = self.rma_make_picking.with_context({
'active_id': 1,
'active_ids': rma.rma_line_ids.ids,
'active_model': 'rma.order.line',
'picking_type': 'incoming',
}).create({})
operation = self.env['rma.operation'].search(
[('type', '=', 'customer'),
('refund_policy', '=', 'received')], limit=1)
rma_line.operation_id = operation.id
rma_line.refund_policy = 'received'
wizard = self.rma_make_picking.with_context(
{
"active_id": 1,
"active_ids": rma_line.ids,
"active_model": "rma.order.line",
"picking_type": "incoming",
}
).create({})
operation = self.env["rma.operation"].search(
[("type", "=", "customer"), ("refund_policy", "=", "received")], limit=1
)
rma_line.write({"operation_id": operation.id})
rma_line.write({"refund_policy": "received"})
wizard._create_picking()
res = rma_line.action_view_in_shipments()
picking = self.env['stock.picking'].browse(res['res_id'])
picking.move_lines.write({'quantity_done': 1.0})
if "res_id" in res:
picking = self.env["stock.picking"].browse(res["res_id"])
else:
picking_ids = self.env["stock.picking"].search(res["domain"])
picking = self.env["stock.picking"].browse(picking_ids)
picking.move_lines.write({"quantity_done": 1.0})
picking.button_validate()
# decreasing cogs
expected_balance = -1.0
self._check_account_balance(self.account_cogs.id,
rma_line=rma_line,
expected_balance=expected_balance)
make_refund = self.rma_refund_wiz.with_context({
'customer': True,
'active_ids': rma_line.ids,
'active_model': 'rma.order.line',
}).create({
'description': 'Test refund',
})
for record in rma_line:
self._check_account_balance(
self.account_cogs.id, rma_line=record, expected_balance=expected_balance
)
make_refund = self.rma_refund_wiz.with_context(
{
"customer": True,
"active_ids": rma_line.ids,
"active_model": "rma.order.line",
}
).create(
{
"description": "Test refund",
}
)
make_refund.invoice_refund()
rma_line.refund_line_ids.invoice_id.action_invoice_open()
for aml in rma_line.refund_line_ids.invoice_id.move_id.line_ids:
if aml.product_id == rma_line.product_id and aml.invoice_id:
rma_line.refund_line_ids.move_id.filtered(
lambda x: x.state != "posted"
).action_post()
for aml in rma_line.refund_line_ids.move_id.filtered(
lambda x: x.move_type in ("out_refund", "in_refund")
).invoice_line_ids:
if aml.product_id == rma_line.product_id and aml.move_id:
self.assertEqual(
aml.rma_line_id, rma_line,
'Rma Order line has not been copied from the invoice to '
'the account move line.')
aml.rma_line_id,
rma_line,
"Rma Order line has not been copied from the invoice to "
"the account move line.",
)

View File

@@ -1,54 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="view_move_line_form" model="ir.ui.view">
<field name="name">account.move.line.form</field>
<field name="model">account.move.line</field>
<field name="inherit_id" ref="account.view_move_line_form"/>
<field name="arch" type="xml">
<field name="quantity" position="before">
<field name="rma_line_id"
groups="account_move_line_rma_order_line.group_account_move_rma_order_line"/>
</field>
</field>
</record>
<record id="view_move_line_tree" model="ir.ui.view">
<field name="name">account.move.line.tree</field>
<field name="model">account.move.line</field>
<field name="inherit_id" ref="account.view_move_line_tree"/>
<field name="arch" type="xml">
<field name="partner_id" position="after">
<field name="rma_line_id"
groups="account_move_line_rma_order_line.group_account_move_rma_order_line"/>
</field>
</field>
</record>
<record id="view_account_move_line_filter" model="ir.ui.view">
<field name="name">Journal Items</field>
<field name="model">account.move.line</field>
<field name="inherit_id" ref="account.view_account_move_line_filter"/>
<field name="arch" type="xml">
<field name="partner_id" position="after">
<field name="rma_line_id"
groups="account_move_line_rma_order_line.group_account_move_rma_order_line"/>
</field>
</field>
</record>
<record id="view_move_form" model="ir.ui.view">
<field name="name">account.move.form</field>
<field name="model">account.move</field>
<field name="inherit_id" ref="account.view_move_form"/>
<field name="arch" type="xml">
<xpath
expr="//field[@name='line_ids']/tree//field[@name='partner_id']" position="after">
<field name="rma_line_id"
groups="account_move_line_rma_order_line.group_account_move_rma_order_line"/>
</xpath>
</field>
</record>
</odoo>